make so it is possible to use DMs from the nos2x extension.

This commit is contained in:
fiatjaf 2022-01-25 20:13:56 -03:00
parent 294e534e5f
commit 20ada3f802
10 changed files with 131 additions and 56 deletions

View File

@ -15,11 +15,11 @@
"identicon.js": "^2.3.3",
"markdown-it": "^12.3.0",
"markdown-it-deflist": "^2.1.0",
"markdown-it-highlightjs": "^3.6.0",
"markdown-it-sub": "^1.0.0",
"markdown-it-sup": "^1.0.0",
"markdown-it-task-lists": "^2.1.1",
"markdown-it-highlightjs": "^3.6.0",
"nostr-tools": "^0.20.1",
"nostr-tools": "^0.21.4",
"pouchdb-adapter-idb": "6",
"pouchdb-core": "6",
"pouchdb-mapreduce": "6",

View File

@ -33,8 +33,6 @@
</template>
<script>
import {decrypt} from 'nostr-tools/nip04'
import helpersMixin from '../utils/mixin'
export default {
@ -55,41 +53,17 @@ export default {
},
text() {
return this.sequence.map(event => this.getPlaintext(event))
return this.sequence.map(evt => evt.text)
}
},
mounted() {
setTimeout(() => {
this.invisible = false
}, 150)
}, 20)
},
methods: {
getPlaintext(event) {
if (
event.tags.find(
([tag, value]) => tag === 'p' && value === this.$store.state.keys.pub
)
) {
// it is addressed to us
// decrypt it
let [ciphertext, iv] = event.content.split('?iv=')
return decrypt(
this.$store.state.keys.priv,
event.pubkey,
ciphertext,
iv
)
} else if (event.pubkey === this.$store.state.keys.pub) {
// it is coming from us
let [_, target] = event.tags.find(([tag]) => tag === 'p')
// decrypt it
let [ciphertext, iv] = event.content.split('?iv=')
return decrypt(this.$store.state.keys.priv, target, ciphertext, iv)
}
},
click(e) {
if (e.target.classList.contains('q-message-stamp')) {
this.metadataDialog = true

View File

@ -6,7 +6,7 @@ worker.onmessage = ev => {
let {id, success, error, data, stream} = JSON.parse(ev.data)
if (stream) {
console.log('🖴', id, '~>>', data)
console.debug('🖴', id, '~>>', data)
hub[id](data)
return
}
@ -17,14 +17,14 @@ worker.onmessage = ev => {
return
}
if (data) console.log('🖴', id, '->', data)
hub[id].resolve(data)
if (data) console.debug('🖴', id, '->', data)
hub[id]?.resolve?.(data)
delete hub[id]
}
function call(name, args) {
let id = name + ' ' + Math.random().toString().slice(-4)
console.log('🖴', id, '<-', args)
console.debug('🖴', id, '<-', args)
worker.postMessage(JSON.stringify({id, name, args}))
return new Promise((resolve, reject) => {
hub[id] = {resolve, reject}
@ -34,7 +34,7 @@ function call(name, args) {
function stream(name, args, callback) {
let id = name + ' ' + Math.random().toString().slice(-4)
hub[id] = callback
console.log('db <-', id, args)
console.debug('db <-', id, args)
worker.postMessage(JSON.stringify({id, name, args, stream: true}))
return {
cancel() {

View File

@ -59,6 +59,9 @@ export default {
async mounted() {
this.chats = await dbGetChats(this.$store.state.keys.pub)
this.chats.forEach(({peer}) =>
this.$store.dispatch('useProfile', {pubkey: peer})
)
}
}
</script>

View File

@ -48,6 +48,7 @@
<script>
import {debounce} from 'quasar'
import {decrypt} from 'nostr-tools/nip04'
import helpersMixin from '../utils/mixin'
import {getElementFullHeight, isElementFullyScrolled} from '../utils/helpers'
@ -64,7 +65,9 @@ export default {
canLoadMore: false,
text: '',
sending: null,
messagesSet: new Set()
messagesSet: new Set(),
unlock: () => {},
mutex: null
}
},
@ -110,6 +113,16 @@ export default {
},
methods: {
async lock() {
if (this.mutex) {
await this.mutex
}
this.mutex = new Promise(resolve => {
this.unlock = resolve
})
},
async restart() {
this.messagesSet = new Set()
if (this.listener) this.listener.cancel()
@ -118,6 +131,10 @@ export default {
this.$store.dispatch('useProfile', {pubkey: this.$route.params.pubkey})
this.messages = await dbGetMessages(this.$route.params.pubkey, 100)
for (let i = 0; i < this.messages.length; i++) {
this.messages[i].text = await this.getPlaintext(this.messages[i])
}
if (this.messages.length > 0) {
await this.scrollToBottom()
this.canLoadMore = true
@ -128,6 +145,10 @@ export default {
if (this.messagesSet.has(event.id)) return
this.messagesSet.add(event.id)
await this.lock()
event.text = await this.getPlaintext(event)
this.unlock()
if (
event.pubkey === this.$store.state.keys.pub &&
event.created_at === this.sending
@ -199,6 +220,10 @@ export default {
this.messages[0].created_at - 1
)
for (let i = 0; i < newMessages.length; i++) {
newMessages[i].text = await this.getPlaintext(newMessages[i])
}
if (newMessages.length === 0) {
this.canLoadMore = false
} else {
@ -220,6 +245,39 @@ export default {
}
this.messages = newMessages.concat(this.messages)
},
async getPlaintext(event) {
if (
event.tags.find(
([tag, value]) => tag === 'p' && value === this.$store.state.keys.pub
)
) {
// it is addressed to us
// decrypt it
return await this.decrypt(event.pubkey, event.content)
} else if (event.pubkey === this.$store.state.keys.pub) {
// it is coming from us
let [_, target] = event.tags.find(([tag]) => tag === 'p')
// decrypt it
return await this.decrypt(target, event.content)
}
},
async decrypt(peer, ciphertext) {
try {
if (this.$store.state.keys.priv) {
return decrypt(this.$store.state.keys.priv, peer, ciphertext)
} else if (
(await window?.nostr?.getPublicKey?.()) === this.$store.state.keys.pub
) {
return await window.nostr.nip04.decrypt(peer, ciphertext)
} else {
throw new Error('no private key available to decrypt!')
}
} catch (err) {
return '???'
}
}
}
}

View File

@ -77,7 +77,7 @@
<div class="flex justify-end">
<q-btn
:disable="!$store.state.keys.priv"
:disable="!$store.getters.canEncryptDecrypt"
round
flat
:to="'/messages/' + $route.params.pubkey"
@ -88,7 +88,7 @@
/>
<q-btn
v-if="isFollowing"
:disable="!$store.state.keys.priv"
:disable="!$store.getters.canSignEventsAutomatically"
round
unelevated
flat
@ -99,7 +99,7 @@
/>
<q-btn
v-if="!isFollowing"
:disable="!$store.state.keys.priv"
:disable="!$store.getters.canSignEventsAutomatically"
round
unelevated
color="primary"

View File

@ -52,6 +52,7 @@
color="negative"
icon="cancel"
size="xs"
:disable="$store.getters.canSignEventsAutomatically"
@click="removeRelay(url)"
/>
{{ url }}
@ -59,7 +60,10 @@
color="primary"
size="sm"
label="Share"
:disable="hasJustSharedRelay"
:disable="
hasJustSharedRelay ||
!$store.getters.canSignEventsAutomatically
"
@click="shareRelay(url)"
/>
</div>
@ -69,14 +73,22 @@
<span
class="cursor-pointer tracking-wide"
:class="{'font-bold': opts.read, 'text-secondary': opts.read}"
@click="setRelayOpt(url, 'read', !opts.read)"
@click="
$store.getters.canSignEventsAutomatically
? setRelayOpt(url, 'read', !opts.read)
: null
"
>
read
</span>
<span
class="cursor-pointer tracking-wide"
:class="{'font-bold': opts.write, 'text-secondary': opts.write}"
@click="setRelayOpt(url, 'write', !opts.write)"
@click="
$store.getters.canSignEventsAutomatically
? setRelayOpt(url, 'write', !opts.write)
: null
"
>
write
</span>
@ -85,7 +97,13 @@
</q-item>
</q-list>
<q-form @submit="addRelay">
<q-input v-model="addingRelay" class="mx-3" filled label="Add a relay">
<q-input
v-model="addingRelay"
class="mx-3"
filled
label="Add a relay"
:disable="!$store.getters.canSignEventsAutomatically"
>
<template #append>
<q-btn
label="Add"

View File

@ -196,7 +196,20 @@ export async function recommendServer(store, url) {
export async function sendChatMessage(store, {now, pubkey, text, replyTo}) {
if (text.length === 0) return
let [ciphertext, iv] = encrypt(store.state.keys.priv, pubkey, text)
let ciphertext = '???'
try {
if (store.state.keys.priv) {
ciphertext = encrypt(store.state.keys.priv, pubkey, text)
} else if (
(await window?.nostr?.getPublicKey?.()) === store.state.keys.pub
) {
ciphertext = await window.nostr.nip04.encrypt(pubkey, text)
} else {
throw new Error('no private key available to encrypt!')
}
} catch (err) {
/***/
}
// make event
let event = {
@ -204,7 +217,7 @@ export async function sendChatMessage(store, {now, pubkey, text, replyTo}) {
created_at: now,
kind: 4,
tags: [['p', pubkey]],
content: ciphertext + '?iv=' + iv
content: ciphertext
}
if (replyTo) {
event.tags.push(['e', replyTo])

View File

@ -52,3 +52,11 @@ export function unreadChats(state) {
delete state.unreadMessages[state.keys.pub]
return Object.values(state.unreadMessages).filter(v => v).length
}
export function canSignEventsAutomatically(state) {
return Boolean(state.keys.priv || window.nostr)
}
export function canEncryptDecrypt(state) {
return Boolean(state.keys.priv || (window.nostr && window.nostr.nip04))
}

View File

@ -4470,6 +4470,14 @@ markdown-it-deflist@^2.1.0:
resolved "https://registry.yarnpkg.com/markdown-it-deflist/-/markdown-it-deflist-2.1.0.tgz#50d7a56b9544cd81252f7623bd785e28a8dcef5c"
integrity sha512-3OuqoRUlSxJiuQYu0cWTLHNhhq2xtoSFqsZK8plANg91+RJQU1ziQ6lA2LzmFAEes18uPBsHZpcX6We5l76Nzg==
markdown-it-highlightjs@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/markdown-it-highlightjs/-/markdown-it-highlightjs-3.6.0.tgz#b567408c633d71e5e4cc33e1d0121a44447d2f29"
integrity sha512-ex+Lq3cVkprh0GpGwFyc53A/rqY6GGzopPCG1xMsf8Ya3XtGC8Uw9tChN1rWbpyDae7tBBhVHVcMM29h4Btamw==
dependencies:
highlight.js "^11.3.1"
lodash.flow "^3.5.0"
markdown-it-sub@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/markdown-it-sub/-/markdown-it-sub-1.0.0.tgz#375fd6026eae7ddcb012497f6411195ea1e3afe8"
@ -4484,13 +4492,6 @@ markdown-it-task-lists@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz#f68f4d2ac2bad5a2c373ba93081a1a6848417088"
integrity sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA==
markdown-it-highlightjs@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/markdown-it-highlightjs/-/markdown-it-highlightjs-3.6.0.tgz#b567408c633d71e5e4cc33e1d0121a44447d2f29"
integrity sha512-ex+Lq3cVkprh0GpGwFyc53A/rqY6GGzopPCG1xMsf8Ya3XtGC8Uw9tChN1rWbpyDae7tBBhVHVcMM29h4Btamw==
dependencies:
highlight.js "^11.3.1"
lodash.flow "^3.5.0"
markdown-it@^12.3.0:
version "12.3.0"
@ -4806,10 +4807,10 @@ normalize-url@^6.0.1:
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
nostr-tools@^0.20.1:
version "0.20.1"
resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-0.20.1.tgz#440b9b4328a6e6e09c283d917c064f241c28a0e6"
integrity sha512-S3JpQmX87h4MzP801OV8l5djp/2bNJC8pG4yBBmeTY/eWM8IrV15lFvTwxZLoW+O2s3mahli1VetqKEI1Ex3Fg==
nostr-tools@^0.21.4:
version "0.21.4"
resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-0.21.4.tgz#c9cb88955ee55ab751287aa4e492708221748d86"
integrity sha512-ZCYRMfryb2loOFm3WGG5dXPGCJP3OqanNur6q1moZUMKhzDOzb3d95yVBa752LF9cReTjjxyIoZm0TklEAaeAA==
dependencies:
"@noble/hashes" "^0.5.7"
"@noble/secp256k1" "^1.3.0"