mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-29 00:10:52 +00:00
Massage DM notifications
This commit is contained in:
parent
c46a4df261
commit
23160dff4b
@ -19,6 +19,7 @@ If you like Coracle and want to support its development, you can donate sats via
|
||||
- [x] NIP 05
|
||||
- [x] Direct messages using NIP 04
|
||||
- [ ] Add petnames for channels
|
||||
- [ ] Add notifications for chat messages
|
||||
- [ ] Deploy coracle relay, set better defaults
|
||||
- [ ] Image uploads
|
||||
- Use dufflepud. Default will charge via lightning and have a tos, others can self-host and skip that.
|
||||
@ -42,6 +43,7 @@ If you like Coracle and want to support its development, you can donate sats via
|
||||
- [ ] Attachments (a tag w/content type and url)
|
||||
- [ ] Add Labs tab with cards for non-standard features
|
||||
- Time travel - see events as of a date/time
|
||||
- [ ] Linkify bech32 entities
|
||||
|
||||
|
||||
# Bugs
|
||||
@ -53,10 +55,6 @@ If you like Coracle and want to support its development, you can donate sats via
|
||||
## Current
|
||||
|
||||
- [ ] Figure out migrations from previous version
|
||||
- [ ] Fix notes search
|
||||
- [ ] Linkify bech32 entities
|
||||
- [ ] Add notification for dms
|
||||
- [ ] Default to network/following
|
||||
|
||||
## 0.2.7
|
||||
|
||||
|
@ -65,7 +65,9 @@
|
||||
|
||||
$: {
|
||||
hasNewMessages = Boolean(find(
|
||||
([k, t]) => ($lastCheckedByPubkey[k] || 0) < t,
|
||||
([k, t]) => {
|
||||
return t > now() - timedelta(7, 'days') && ($lastCheckedByPubkey[k] || 0) < t
|
||||
},
|
||||
Object.entries($mostRecentByPubkey)
|
||||
))
|
||||
}
|
||||
@ -211,11 +213,11 @@
|
||||
</a>
|
||||
</li>
|
||||
{#if $user}
|
||||
<li class="cursor-pointer">
|
||||
<li class="cursor-pointer relative">
|
||||
<a class="block px-4 py-2 hover:bg-accent transition-all" href="/chat">
|
||||
<i class="fa-solid fa-message mr-2" /> Chat
|
||||
{#if hasNewMessages}
|
||||
<div class="w-2 h-2 rounded bg-accent absolute top-3 left-6" />
|
||||
<div class="w-2 h-2 rounded bg-accent absolute top-2 left-7" />
|
||||
{/if}
|
||||
</a>
|
||||
</li>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import Dexie from 'dexie'
|
||||
import Dexie, {liveQuery} from 'dexie'
|
||||
import {pick} from 'ramda'
|
||||
import {nip05} from 'nostr-tools'
|
||||
import {writable} from 'svelte/store'
|
||||
@ -6,6 +6,14 @@ import {noop, ensurePlural, createMap, switcherFn} from 'hurdak/lib/hurdak'
|
||||
import {now} from 'src/util/misc'
|
||||
import {personKinds, Tags, roomAttrs} from 'src/util/nostr'
|
||||
|
||||
export const lq = cb => liveQuery(async () => {
|
||||
try {
|
||||
return await cb()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
})
|
||||
|
||||
export const db = new Dexie('agent/data/db')
|
||||
|
||||
db.version(13).stores({
|
||||
|
@ -4,11 +4,11 @@ import {Tags} from 'src/util/nostr'
|
||||
import pool from 'src/agent/pool'
|
||||
import keys from 'src/agent/keys'
|
||||
import defaults from 'src/agent/defaults'
|
||||
import {db, people, ready, getPerson, processEvents} from 'src/agent/data'
|
||||
import {lq, db, people, ready, getPerson, processEvents} from 'src/agent/data'
|
||||
|
||||
Object.assign(window, {pool, db})
|
||||
|
||||
export {pool, keys, db, ready, people, getPerson}
|
||||
export {pool, keys, lq, db, ready, people, getPerson}
|
||||
|
||||
export const user = derived(
|
||||
[keys.pubkey, people],
|
||||
|
@ -1,10 +1,11 @@
|
||||
import {pluck} from 'ramda'
|
||||
import {synced, batch} from 'src/util/misc'
|
||||
import {synced, now, timedelta, batch} from 'src/util/misc'
|
||||
import {listen as _listen} from 'src/agent'
|
||||
import loaders from 'src/app/loaders'
|
||||
|
||||
let listener
|
||||
|
||||
const since = now() - timedelta(30, 'days')
|
||||
const mostRecentByPubkey = synced('app/messages/mostRecentByPubkey', {})
|
||||
const lastCheckedByPubkey = synced('app/messages/lastCheckedByPubkey', {})
|
||||
|
||||
@ -15,7 +16,8 @@ const listen = async (relays, pubkey) => {
|
||||
|
||||
listener = await _listen(
|
||||
relays,
|
||||
{kinds: [4], '#p': [pubkey]},
|
||||
[{kinds: [4], authors: [pubkey], since},
|
||||
{kinds: [4], '#p': [pubkey], since}],
|
||||
batch(300, async events => {
|
||||
if (events.length > 0) {
|
||||
await loaders.loadPeople(relays, pluck('pubkey', events))
|
||||
|
@ -132,13 +132,18 @@
|
||||
<p class="text-sm text-light">{formatTimestamp(m.created_at)}</p>
|
||||
</div>
|
||||
{/if}
|
||||
<div class={cx("overflow-hidden text-ellipsis", {
|
||||
'ml-6': type === 'chat',
|
||||
'rounded-2xl py-2 px-4': type === 'dm',
|
||||
'ml-12 bg-light text-black rounded-br-none': type === 'dm' && m.person.pubkey === $user.pubkey,
|
||||
'mr-12 bg-dark rounded-bl-none': type === 'dm' && m.person.pubkey !== $user.pubkey,
|
||||
<div class={cx("flex overflow-hidden text-ellipsis", {
|
||||
'ml-12 justify-end': type === 'dm' && m.person.pubkey === $user.pubkey,
|
||||
'mr-12': type === 'dm' && m.person.pubkey !== $user.pubkey,
|
||||
})}>
|
||||
{@html render(m, {showEntire: true})}
|
||||
<div class={cx({
|
||||
'ml-6': type === 'chat',
|
||||
'rounded-2xl py-2 px-4 flex max-w-xl': type === 'dm',
|
||||
'bg-light text-black rounded-br-none': type === 'dm' && m.person.pubkey === $user.pubkey,
|
||||
'bg-dark rounded-bl-none': type === 'dm' && m.person.pubkey !== $user.pubkey,
|
||||
})}>
|
||||
{@html render(m, {showEntire: true})}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
@ -185,7 +190,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fixed z-10 bottom-0 w-full flex bg-medium border-medium border-t border-solid border-dark">
|
||||
<div class="fixed z-10 bottom-0 w-full flex bg-medium border-medium border-t border-solid border-dark lg:-ml-56 lg:pl-56">
|
||||
<textarea
|
||||
rows="4"
|
||||
autofocus
|
||||
|
@ -1,14 +1,36 @@
|
||||
<script>
|
||||
import {onMount} from 'svelte'
|
||||
import {fly} from 'svelte/transition'
|
||||
import {ellipsize} from 'hurdak/lib/hurdak'
|
||||
import Anchor from 'src/partials/Anchor.svelte'
|
||||
import {displayPerson} from 'src/util/nostr'
|
||||
import {now, timedelta} from 'src/util/misc'
|
||||
import {messages} from 'src/app'
|
||||
|
||||
export let joined = false
|
||||
export let room
|
||||
export let setRoom
|
||||
export let joinRoom
|
||||
export let leaveRoom
|
||||
|
||||
let hasNewMessages = false
|
||||
|
||||
const {mostRecentByPubkey, lastCheckedByPubkey} = messages
|
||||
|
||||
onMount(() => {
|
||||
const interval = setInterval(() => {
|
||||
// TODO notifications for channel messages
|
||||
if (room.type === 'npub') {
|
||||
const mostRecent = $mostRecentByPubkey[room.pubkey] || Infinity
|
||||
const lastChecked = $lastCheckedByPubkey[room.pubkey] || 0
|
||||
|
||||
// Include a cut-off since we lose read receipts every log out
|
||||
hasNewMessages = mostRecent > now() - timedelta(7, 'days') && lastChecked < mostRecent
|
||||
}
|
||||
}, 1000)
|
||||
|
||||
return () => clearInterval(interval)
|
||||
})
|
||||
</script>
|
||||
|
||||
<li
|
||||
@ -19,7 +41,7 @@
|
||||
class="overflow-hidden w-14 h-14 rounded-full bg-cover bg-center shrink-0 border border-solid border-white"
|
||||
style="background-image: url({room.picture})" />
|
||||
<div class="flex flex-grow flex-col justify-start gap-2">
|
||||
<div class="flex flex-grow items-center justify-between gap-2">
|
||||
<div class="flex flex-grow items-start justify-between gap-2">
|
||||
<div class="flex gap-2 items-center">
|
||||
{#if room.type === 'npub'}
|
||||
<i class="fa fa-lock text-light" />
|
||||
@ -29,6 +51,16 @@
|
||||
<h2 class="text-lg">{room.name}</h2>
|
||||
{/if}
|
||||
</div>
|
||||
{#if joined}
|
||||
{#if hasNewMessages}
|
||||
<div class="relative">
|
||||
<i class="fa fa-bell" />
|
||||
<div class="absolute top-0 right-0 mt-1 w-1 h-1 bg-accent rounded-full" />
|
||||
</div>
|
||||
{:else}
|
||||
<i class="fa fa-bell text-light" />
|
||||
{/if}
|
||||
{/if}
|
||||
{#if room.type === 'note'}
|
||||
{#if joined}
|
||||
<Anchor type="button" class="flex items-center gap-2" on:click={e => { e.stopPropagation(); leaveRoom(room.id) }}>
|
||||
|
@ -1,12 +1,12 @@
|
||||
<script>
|
||||
import {uniq, pluck} from 'ramda'
|
||||
import {without, uniq, sortBy, pluck} from 'ramda'
|
||||
import {onMount} from "svelte"
|
||||
import {nip19} from 'nostr-tools'
|
||||
import {navigate} from "svelte-routing"
|
||||
import {liveQuery} from 'dexie'
|
||||
import {fuzzy} from "src/util/misc"
|
||||
import {getRelays, getPerson, listen, db} from 'src/agent'
|
||||
import {modal} from 'src/app'
|
||||
import {getRelays, user, lq, getPerson, listen, db} from 'src/agent'
|
||||
import {modal, messages} from 'src/app'
|
||||
import loaders from 'src/app/loaders'
|
||||
import Room from "src/partials/Room.svelte"
|
||||
import Input from "src/partials/Input.svelte"
|
||||
@ -17,16 +17,17 @@
|
||||
let q = ""
|
||||
let roomsCount = 0
|
||||
|
||||
const rooms = liveQuery(async () => {
|
||||
const [rooms, messages] = await Promise.all([
|
||||
db.rooms.where('joined').equals(1).toArray(),
|
||||
db.messages.toArray(),
|
||||
])
|
||||
const {mostRecentByPubkey} = messages
|
||||
|
||||
const rooms = lq(async () => {
|
||||
const rooms = await db.rooms.where('joined').equals(1).toArray()
|
||||
const messages = await db.messages.toArray()
|
||||
|
||||
const pubkeys = without([$user.pubkey], uniq(pluck('pubkey', messages)))
|
||||
|
||||
const pubkeys = uniq(pluck('pubkey', messages))
|
||||
await loaders.loadPeople(getRelays(), pubkeys)
|
||||
|
||||
return pubkeys
|
||||
return sortBy(k => -(mostRecentByPubkey[k] || 0), pubkeys)
|
||||
.map(k => ({type: 'npub', id: k, ...getPerson(k, true)}))
|
||||
.concat(rooms.map(room => ({type: 'note', ...room})))
|
||||
})
|
||||
@ -58,7 +59,7 @@
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
const sub = listen(getRelays(), {kinds: [40, 41]})
|
||||
const sub = listen(getRelays(), [{kinds: [40, 41]}])
|
||||
|
||||
return () => {
|
||||
sub.then(s => {
|
||||
|
@ -3,9 +3,10 @@
|
||||
import {nip19} from 'nostr-tools'
|
||||
import {sortBy, pluck} from 'ramda'
|
||||
import {personKinds} from 'src/util/nostr'
|
||||
import {batch} from 'src/util/misc'
|
||||
import {batch, now} from 'src/util/misc'
|
||||
import Channel from 'src/partials/Channel.svelte'
|
||||
import {getRelays, user, db, listen, keys} from 'src/agent'
|
||||
import {messages} from 'src/app'
|
||||
import {routes} from 'src/app/ui'
|
||||
import cmd from 'src/app/cmd'
|
||||
|
||||
@ -15,6 +16,8 @@
|
||||
let {data: pubkey} = nip19.decode(entity)
|
||||
let person = liveQuery(() => db.people.get(pubkey))
|
||||
|
||||
messages.lastCheckedByPubkey.update($obj => ({...$obj, [pubkey]: now()}))
|
||||
|
||||
const decryptMessages = async events => {
|
||||
// Gotta do it in serial because of extension limitations
|
||||
for (const event of events) {
|
||||
|
Loading…
Reference in New Issue
Block a user