async function chatDialog(path) { const template = await loadTemplateAsync(path) Vue.component('chat-dialog', { name: 'chat-dialog', template, props: ['account', 'merchant', 'relays', 'pool'], data: function () { return { dialog: false, isChat: true, loading: false, messagesMap: new Map(), nostrMessages: [], newMessage: '', ordersTable: { columns: [ { name: 'id', align: 'left', label: 'ID', field: 'id' }, { name: 'created_at', align: 'left', label: 'Created/Updated', field: 'created_at', sortable: true }, { name: 'paid', align: 'left', label: 'Paid', field: 'paid', sortable: true }, { name: 'shipped', align: 'left', label: 'Shipped', field: 'shipped', sortable: true }, { name: 'invoice', align: 'left', label: 'Invoice', field: row => row.payment_options && row.payment_options.find(p => p.type == 'ln')?.link } ], pagination: { rowsPerPage: 10 } } } }, computed: { sortedMessages() { return this.nostrMessages.sort((a, b) => b.created_at - a.created_at) }, ordersList() { let orders = this.nostrMessages .filter(o => o.json) .sort((a, b) => b.created_at - a.created_at) .reduce((acc, cur) => { const obj = cur.json const key = obj.id const curGroup = acc[key] ?? {created_at: cur.timestamp} return {...acc, [key]: {...curGroup, ...obj}} }, {}) return Object.values(orders) } }, methods: { async startDialog() { this.dialog = true await this.startPool() }, async closeDialog() { this.dialog = false await this.sub.unsub() }, async startPool() { this.loading = true let relays = Array.from(this.relays) let filters = [ { kinds: [4], authors: [this.account.pubkey] }, { kinds: [4], '#p': [this.account.pubkey] } ] let events = await this.pool.list(relays, filters) for (const event of events) { await this.processMessage(event) } this.nostrMessages = Array.from(this.messagesMap.values()) this.loading = false let sub = this.pool.sub( relays, filters.map(f => ({...f, since: Date.now() / 1000})) ) sub.on('event', async event => { await this.processMessage(event) this.nostrMessages = Array.from(this.messagesMap.values()) }) this.sub = sub }, async processMessage(event) { let mine = event.pubkey == this.account.pubkey let sender = mine ? this.merchant : event.pubkey try { let plaintext if (this.account.privkey) { plaintext = await NostrTools.nip04.decrypt( this.account.privkey, sender, event.content ) } else if (this.account.useExtension && this.hasNip07) { plaintext = await window.nostr.nip04.decrypt(sender, event.content) } if (plaintext) { let {text, json} = this.filterJsonMsg(plaintext) this.messagesMap.set(event.id, { created_at: event.created_at, msg: text, timestamp: timeFromNow(event.created_at * 1000), sender: `${mine ? 'Me' : 'Merchant'}`, json }) } return null } catch { console.debug('Unable to decrypt message! Not for us...') return null } }, filterJsonMsg(text) { let json = null if (!isJson(text)) return {text, json} json = JSON.parse(text) if (json.message) { text = json.message } else if (json.items) { text = `Order placed!
OrderID: ${json.id}` } else if (json.payment_options) { text = `Invoice for order: ${json.id}
LN ⚡︎` } return {text, json} }, async sendMessage() { if (this.newMessage && this.newMessage.length < 1) return let event = { ...(await NostrTools.getBlankEvent()), kind: 4, created_at: Math.floor(Date.now() / 1000), tags: [['p', this.merchant]], pubkey: this.account.pubkey, content: await this.encryptMsg() } event.id = NostrTools.getEventHash(event) event.sig = this.signEvent(event) let pub = this.pool.publish(Array.from(this.relays), event) pub.on('ok', () => console.debug(`Event was sent`)) pub.on('failed', error => console.error(error)) this.newMessage = '' }, async encryptMsg() { try { let cypher if (this.account.privkey) { cypher = await NostrTools.nip04.encrypt( this.account.privkey, this.merchant, this.newMessage ) } else if (this.account.useExtension && this.hasNip07) { cypher = await window.nostr.nip04.encrypt( this.merchant, this.newMessage ) } return cypher } catch (e) { console.error(e) } }, async signEvent(event) { if (this.account.privkey) { event.sig = await NostrTools.signEvent(event, this.account.privkey) } else if (this.account.useExtension && this.hasNip07) { event = await window.nostr.signEvent(event) } return event } }, created() { setTimeout(() => { if (window.nostr) { this.hasNip07 = true } }, 1000) } }) }