Files
nostrdvm/tests/gui/vuejs/noogle/src/components/Search.vue
2024-01-20 22:38:53 +01:00

255 lines
7.6 KiB
Vue

<script setup>
import {Client, Filter, Timestamp, Event, Metadata, PublicKey, EventBuilder, Tag, EventId} from "@rust-nostr/nostr-sdk";
import store from '../store';
import miniToastr from "mini-toastr";
import VueNotifications from "vue-notifications";
let items = []
let listener = false
async function send_search_request(message) {
try {
if (message === undefined){
message = "Nostr"
}
if(store.state.pubkey === undefined){
miniToastr.showMessage("Please login first", "No pubkey set", VueNotifications.types.warn)
return
}
items = []
store.state.results = []
let client = store.state.client
let tags = []
let users = [];
const taggedUsersFrom = message.split(' ')
.filter(word => word.startsWith('from:'))
.map(word => word.replace('from:', ''));
// search
let search = message;
// tags
for (let word of taggedUsersFrom) {
search = search.replace(word, "");
if(word === "me"){
word = store.state.pubkey.toBech32()
}
const userPubkey = PublicKey.fromBech32(word.replace("@", "")).toHex()
const pTag = Tag.parse(["p", userPubkey]);
users.push(pTag.asVec());
}
message = search.replace(/from:|to:|@/g, '').trim();
console.log(search);
tags.push(Tag.parse(["i", message, "text"]))
tags.push(Tag.parse(["param", "max_results", "100"]))
tags.push(Tag.parse(['param', 'users', JSON.stringify(users)]))
let evt = new EventBuilder(5302, "NIP 90 Search request", tags)
let res = await client.sendEventBuilder(evt)
miniToastr.showMessage("Sent Request to DVMs", "Awaiting results", VueNotifications.types.warn)
if (!store.state.hasEventListener){
listen()
store.commit('set_hasEventListener', true)
}
else{
console.log("Already has event listener")
}
console.log(res)
} catch (error) {
console.log(error);
}
}
async function getEvents(eventids) {
const event_filter = new Filter().ids(eventids)
let client = store.state.client
return await client.getEventsOf([event_filter], 5)
}
async function get_user_infos(pubkeys){
let profiles = []
let client = store.state.client
const profile_filter = new Filter().kind(0).authors(pubkeys)
let evts = await client.getEventsOf([profile_filter], 10)
console.log("PROFILES:" + evts.length)
for (const entry of evts){
try{
let contentjson = JSON.parse(entry.content)
profiles.push({profile: contentjson, author: entry.author.toHex(), createdAt: entry.createdAt});
}
catch(error){
console.log("error")
}
}
return profiles
}
async function listen() {
listener = true
let client = store.state.client
let pubkey = store.state.pubkey
const filter = new Filter().kinds([7000, 6302]).pubkey(pubkey).since(Timestamp.now());
await client.subscribe([filter]);
const handle = {
// Handle event
handleEvent: async (relayUrl, event) => {
if (store.state.hasEventListener === false){
return true
}
console.log("Received new event from", relayUrl);
if (event.kind === 7000) {
try {
console.log("7000:", event.content);
miniToastr.showMessage("DVM replied", event.content, VueNotifications.types.info)
//if (content === "stop") {
// return true
//}
} catch (error) {
console.log("Error: ", error);
}
}
else if(event.kind === 6302) {
let entries = []
console.log("6302:", event.content);
miniToastr.showMessage("DVM replied", "Received Results", VueNotifications.types.success)
let event_etags = JSON.parse(event.content)
for (let etag of event_etags){
const eventid = EventId.fromHex(etag[1])
entries.push(eventid)
}
const events = await getEvents(entries)
let authors = []
for (const evt of events){
authors.push(evt.author)
}
let profiles = await get_user_infos(authors)
for (const evt of events){
let p = profiles.find( record => record.author === evt.author.toHex())
let bech32id = evt.id.toBech32()
let picture = p === undefined ? "../assets/nostr-purple.svg" : p["profile"]["picture"]
let name = p === undefined ? bech32id : p["profile"]["name"]
let highlighterurl = "https://highlighter.com/a/" + bech32id
let njumpurl = "https://njump.me/" + bech32id
let nostrudelurl = "https://nostrudel.ninja/#/n/" + evt.id.toBech32()
if (!items.find(e => e.id === evt.id)) {
items.push({id:evt.id, content: evt.content, author: name, authorurl: "https://njump.me/" + evt.author.toBech32(), links: {"highlighter": highlighterurl, "njump": njumpurl, "nostrudel": nostrudelurl} , avatar: picture, indicator: {"time": evt.createdAt.toHumanDatetime()}})
}
}
store.commit('set_search_results', items)
}
},
// Handle relay message
handleMsg: async (relayUrl, message) => {
//console.log("Received message from", relayUrl, message.asJson());
}
};
client.handleNotifications(handle);
}
defineProps({
msg: {
type: String,
required: false
},
})
</script>
<template>
<div class="greetings">
<img alt="Nostr logo" class="logo" src="../assets/nostr-purple.svg" />
<br>
<h1 class="text-7xl font-black tracking-wide">Noogle</h1>
<h2 class="text-base-200-content text-center tracking-wide text-2xl font-thin">
Search the Nostr with Data Vending Machines
</h2>
<h3>
<br>
<input class="c-Input" v-model="message">
<button class="v-Button" @click="send_search_request(message)">Search the Nostr
</button>
<!--
<a href="https://vitejs.dev/" target="_blank" rel="noopener">Vite</a> +
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>. -->
</h3>
</div>
</template>
<style scoped>
.v-Button {
@apply bg-nostr hover:bg-nostr2 focus:ring-white mb-2 inline-flex flex-none items-center rounded-lg border border-black px-3 py-1.5 text-sm leading-4 text-white transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-white dark:focus:ring-offset-gray-900;
height: 48px;
margin: 5px;
}
.c-Input {
@apply bg-black hover:bg-gray-900 focus:ring-white mb-2 inline-flex flex-none items-center rounded-lg border border-transparent px-3 py-1.5 text-sm leading-4 text-white transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-white dark:focus:ring-offset-gray-900;
width: 400px;
height: 48px;
color: white;
background: black;
}
.logo {
display: flex;
width:100%;
height:125px;
justify-content: center;
align-items: center;
}
h3 {
font-size: 1.2rem;
}
.greetings h1,
.greetings h3 {
text-align: center;
}
@media (min-width: 1024px) {
.greetings h1,
.greetings h3 {
text-align: center;
}
}
</style>