mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-30 00:41:12 +00:00
Fix alerts
This commit is contained in:
parent
efac30809a
commit
9bdb707d27
@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npm run lint
|
||||
npm run check
|
||||
npm run qa:lint
|
||||
npm run qa:check
|
||||
|
||||
|
13
README.md
13
README.md
@ -55,7 +55,6 @@ If you like Coracle and want to support its development, you can donate sats via
|
||||
- Put user detail in a modal?
|
||||
- ReplaceState for settings modals?
|
||||
- [ ] Mentions are sorta weird, usually mention self
|
||||
- [ ] Alerts are not showing likes, just generally screwy. Maybe because I threadify before adding to the db?
|
||||
- [ ] Change network tab to list relays the user is connected to
|
||||
- [ ] Sync mentions box and in-reply mentions
|
||||
- [ ] Add petnames for channels
|
||||
@ -63,11 +62,17 @@ If you like Coracle and want to support its development, you can donate sats via
|
||||
|
||||
# Changelog
|
||||
|
||||
## 0.2.10
|
||||
|
||||
- [x] Fixed likes not showing up in alerts
|
||||
- [x] Raised threshold for pool to 2 so we don't have such a small amount of results
|
||||
- [x] Wait for profile info on login, navigate to network by default
|
||||
|
||||
## 0.2.9
|
||||
|
||||
- [x] Fix a bug in pool.subscribe which was causing requests to wait for all connections
|
||||
- [x] Add typescript with pre-commit hook
|
||||
- [x] Fix layout for chat, person pages
|
||||
- [x] Fixed a bug in pool.subscribe which was causing requests to wait for all connections
|
||||
- [x] Added typescript with pre-commit hook
|
||||
- [x] Fixed layout for chat, person pages
|
||||
- [x] Parse relays for kind 3
|
||||
|
||||
## 0.2.8
|
||||
|
BIN
package-lock.json
generated
BIN
package-lock.json
generated
Binary file not shown.
@ -7,8 +7,10 @@
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint src/*/** --quiet",
|
||||
"check": "svelte-check --tsconfig ./tsconfig.json --threshold error"
|
||||
"qa:lint": "eslint src/*/** --quiet",
|
||||
"qa:check": "svelte-check --tsconfig ./tsconfig.json --threshold error",
|
||||
"qa:all": "run-p qa:lint qa:check",
|
||||
"watch": "find src -type f | entr -r npm run qa:all"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^1.1.0",
|
||||
@ -36,6 +38,7 @@
|
||||
"hurdak": "github:ConsignCloud/hurdak",
|
||||
"husky": "^8.0.3",
|
||||
"nostr-tools": "^1.2.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"ramda": "^0.28.0",
|
||||
"svelte-check": "^3.0.3",
|
||||
"svelte-link-preview": "^0.3.3",
|
||||
|
@ -12,9 +12,10 @@
|
||||
import {displayPerson, isLike} from 'src/util/nostr'
|
||||
import {timedelta, now} from 'src/util/misc'
|
||||
import {keys, user, pool, getRelays} from 'src/agent'
|
||||
import {modal, toast, settings, logUsage, alerts, messages} from "src/app"
|
||||
import {modal, toast, settings, logUsage, alerts, messages, loadAppData} from "src/app"
|
||||
import {routes} from "src/app/ui"
|
||||
import Anchor from 'src/partials/Anchor.svelte'
|
||||
import Content from 'src/partials/Content.svelte'
|
||||
import Spinner from 'src/partials/Spinner.svelte'
|
||||
import Modal from 'src/partials/Modal.svelte'
|
||||
import SignUp from "src/views/SignUp.svelte"
|
||||
@ -74,9 +75,7 @@
|
||||
|
||||
onMount(() => {
|
||||
if ($user) {
|
||||
alerts.load(getRelays(), $user.pubkey)
|
||||
alerts.listen(getRelays(), $user.pubkey)
|
||||
messages.listen(getRelays(), $user.pubkey)
|
||||
loadAppData($user.pubkey)
|
||||
}
|
||||
|
||||
const interval = setInterval(() => {
|
||||
@ -301,6 +300,13 @@
|
||||
<PubKeyLogin />
|
||||
{:else if $modal.type === 'person/settings'}
|
||||
<PersonSettings />
|
||||
{:else if $modal.type === 'message'}
|
||||
<Content size="lg">
|
||||
<div class="text-center">{$modal.message}</div>
|
||||
{#if $modal.spinner}
|
||||
<Spinner delay={0} />
|
||||
{/if}
|
||||
</Content>
|
||||
{/if}
|
||||
</Modal>
|
||||
{/if}
|
||||
|
@ -192,17 +192,15 @@ const request = (relays, filters, {threshold = 2} = {}): Promise<Record<string,
|
||||
const eose = []
|
||||
|
||||
const attemptToComplete = () => {
|
||||
const done = (
|
||||
eose.length === relays.length
|
||||
|| eose.filter(url => relaysWithEvents.has(url)).length >= threshold
|
||||
|| (
|
||||
Date.now() - now >= 1000
|
||||
&& eose.length > relays.length - Math.round(relays.length / 10)
|
||||
)
|
||||
|| Date.now() - now >= 5000
|
||||
const allEose = eose.length === relays.length
|
||||
const atThreshold = eose.filter(url => relaysWithEvents.has(url)).length >= threshold
|
||||
const hardTimeout = Date.now() - now >= 5000
|
||||
const softTimeout = (
|
||||
Date.now() - now >= 1000
|
||||
&& eose.length > relays.length - Math.round(relays.length / 10)
|
||||
)
|
||||
|
||||
if (done) {
|
||||
if (allEose || atThreshold || hardTimeout || softTimeout) {
|
||||
agg.unsub()
|
||||
resolve(events)
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
import {get} from 'svelte/store'
|
||||
import {synced, batch, now} from 'src/util/misc'
|
||||
import {isAlert} from 'src/util/nostr'
|
||||
import {load as _load, listen as _listen, getMuffle, db} from 'src/agent'
|
||||
import {groupBy, pluck, partition, propEq} from 'ramda'
|
||||
import {synced, timedelta, batch, now} from 'src/util/misc'
|
||||
import {isAlert, findReplyId} from 'src/util/nostr'
|
||||
import {load as _load, listen as _listen, db} from 'src/agent'
|
||||
import loaders from 'src/app/loaders'
|
||||
import {threadify} from 'src/app'
|
||||
import {annotate} from 'src/app'
|
||||
|
||||
let listener
|
||||
|
||||
@ -14,21 +15,29 @@ const onChunk = async (relays, pubkey, events) => {
|
||||
events = events.filter(e => isAlert(e, pubkey))
|
||||
|
||||
if (events.length > 0) {
|
||||
const context = await loaders.loadContext(relays, events, {threshold: 2})
|
||||
const notes = threadify(events, context, {muffle: getMuffle()})
|
||||
const context = await loaders.loadContext(relays, events)
|
||||
const [likes, notes] = partition(propEq('kind', 7), events)
|
||||
const annotatedNotes = notes.map(n => annotate(n, context))
|
||||
const likesByParent = groupBy(findReplyId, likes)
|
||||
const likedNotes = context
|
||||
.filter(e => likesByParent[e.id])
|
||||
.map(e => annotate({...e, likedBy: pluck('pubkey', likesByParent[e.id])}, context))
|
||||
|
||||
await db.table('alerts').bulkPut(notes)
|
||||
await db.table('alerts').bulkPut(annotatedNotes.concat(likedNotes))
|
||||
|
||||
mostRecentAlert.update($t => events.reduce((t, e) => Math.max(t, e.created_at), $t))
|
||||
}
|
||||
}
|
||||
|
||||
const load = async (relays, pubkey) => {
|
||||
const since = get(mostRecentAlert)
|
||||
// Include an offset so we don't miss alerts on one relay but not another
|
||||
const since = get(mostRecentAlert) - timedelta(30, 'days')
|
||||
|
||||
// Crank the threshold up since we can afford for this to be slow
|
||||
const events = await _load(
|
||||
relays,
|
||||
{kinds: [1, 7], '#p': [pubkey], since, limit: 100},
|
||||
{threshold: 2}
|
||||
{kinds: [1, 7], '#p': [pubkey], since, limit: 1000},
|
||||
{threshold: 10}
|
||||
)
|
||||
|
||||
onChunk(relays, pubkey, events)
|
||||
|
@ -14,6 +14,15 @@ import loaders from 'src/app/loaders'
|
||||
|
||||
export {toast, modal, settings, alerts, messages, logUsage}
|
||||
|
||||
export const loadAppData = pubkey => {
|
||||
return Promise.all([
|
||||
loaders.loadNetwork(getRelays(), pubkey),
|
||||
alerts.load(getRelays(), pubkey),
|
||||
alerts.listen(getRelays(), pubkey),
|
||||
messages.listen(getRelays(), pubkey),
|
||||
])
|
||||
}
|
||||
|
||||
export const login = async ({privkey, pubkey}: {privkey?: string, pubkey?: string}, usingExtension = false) => {
|
||||
if (privkey) {
|
||||
keys.setPrivateKey(privkey)
|
||||
@ -21,16 +30,18 @@ export const login = async ({privkey, pubkey}: {privkey?: string, pubkey?: strin
|
||||
keys.setPublicKey(pubkey)
|
||||
}
|
||||
|
||||
modal.set({type: 'message', message: "Loading your profile data...", spinner: true})
|
||||
|
||||
// Load network and start listening, but don't wait for it
|
||||
loaders.loadNetwork(getRelays(), pubkey),
|
||||
alerts.load(getRelays(), pubkey),
|
||||
alerts.listen(getRelays(), pubkey),
|
||||
messages.listen(getRelays(), pubkey)
|
||||
loadAppData(pubkey)
|
||||
|
||||
// Load our user so we can populate network and show profile info
|
||||
await loaders.loadPeople(getRelays(), [pubkey])
|
||||
|
||||
// Not ideal, but the network tab depends on the user's social network being
|
||||
// loaded, so put them on global when they first log in so we're not slowing
|
||||
// down users' first run experience too much
|
||||
navigate('/notes/global')
|
||||
navigate('/notes/network')
|
||||
}
|
||||
|
||||
export const addRelay = async relay => {
|
||||
@ -46,12 +57,8 @@ export const addRelay = async relay => {
|
||||
// Publish to the new set of relays
|
||||
await cmd.setRelays(relays, relays)
|
||||
|
||||
await Promise.all([
|
||||
loaders.loadNetwork(relays, person.pubkey),
|
||||
alerts.load(relays, person.pubkey),
|
||||
alerts.listen(relays, person.pubkey),
|
||||
messages.listen(getRelays(), person.pubkey)
|
||||
])
|
||||
// Reload alerts, messages, etc
|
||||
await loadAppData(person.pubkey)
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,6 +133,7 @@ export const annotate = (note, context) => {
|
||||
|
||||
export const threadify = (events, context, {muffle = [], showReplies = true} = {}) => {
|
||||
const contextById = createMap('id', events.concat(context))
|
||||
|
||||
// Show parents when possible. For reactions, if there's no parent,
|
||||
// throw it away. Sort by created date descending
|
||||
const notes = sortBy(
|
||||
|
@ -2,8 +2,7 @@ import {uniqBy, prop, uniq, flatten, pluck, identity} from 'ramda'
|
||||
import {ensurePlural, createMap, chunk} from 'hurdak/lib/hurdak'
|
||||
import {findReply, personKinds, Tags} from 'src/util/nostr'
|
||||
import {now, timedelta} from 'src/util/misc'
|
||||
import {load, getPerson} from 'src/agent'
|
||||
import defaults from 'src/agent/defaults'
|
||||
import {load, getPerson, getFollows} from 'src/agent'
|
||||
|
||||
const getStalePubkeys = pubkeys => {
|
||||
// If we're not reloading, only get pubkeys we don't already know about
|
||||
@ -28,17 +27,7 @@ const loadPeople = (relays, pubkeys, {kinds = personKinds, force = false, ...opt
|
||||
}
|
||||
|
||||
const loadNetwork = async (relays, pubkey) => {
|
||||
// Get this user's profile to start with. This may update what relays
|
||||
// are available, so don't assign relays to a variable here.
|
||||
const events = pubkey ? await loadPeople(relays, [pubkey], {force: true}) : []
|
||||
let petnames = Tags.from(events.filter(e => e.kind === 3)).type("p").all()
|
||||
|
||||
// Default to some cool guys we know
|
||||
if (petnames.length === 0) {
|
||||
petnames = defaults.petnames
|
||||
}
|
||||
|
||||
const tags = Tags.wrap(petnames)
|
||||
const tags = Tags.wrap(getFollows(pubkey))
|
||||
|
||||
// Use nip-2 recommended relays to load our user's second-order follows
|
||||
await loadPeople(tags.relays(), tags.values().all())
|
||||
|
@ -178,7 +178,7 @@
|
||||
</div>
|
||||
|
||||
{#if suggestions.length > 0}
|
||||
<div class="rounded border border-solid border-medium mt-2" in:fly={{y: 20}}>
|
||||
<div class="rounded border border-solid border-medium mt-2 flex flex-col" in:fly={{y: 20}}>
|
||||
{#each suggestions as person, i (person.pubkey)}
|
||||
<button
|
||||
class="py-2 px-4 cursor-pointer"
|
||||
|
@ -1,10 +1,11 @@
|
||||
<script>
|
||||
import {fly} from 'svelte/transition'
|
||||
import {uniqBy, prop} from 'ramda'
|
||||
import {uniq} from 'ramda'
|
||||
import {ellipsize, quantify} from 'hurdak/lib/hurdak'
|
||||
import Badge from "src/partials/Badge.svelte"
|
||||
import {formatTimestamp} from 'src/util/misc'
|
||||
import {killEvent} from 'src/util/html'
|
||||
import {getPerson} from 'src/agent'
|
||||
import {modal} from 'src/app'
|
||||
|
||||
export let note
|
||||
@ -26,11 +27,11 @@
|
||||
|
||||
<button
|
||||
class="py-2 px-3 flex flex-col gap-2 text-white cursor-pointer transition-all
|
||||
border border-solid border-black hover:border-medium hover:bg-dark"
|
||||
border border-solid border-black hover:border-medium hover:bg-dark text-left"
|
||||
on:click={() => modal.set({type: 'note/detail', note})}>
|
||||
<div class="flex gap-2 items-center justify-between relative">
|
||||
<button class="cursor-pointer" on:click={openPopover}>
|
||||
{quantify(note.people.length, 'person', 'people')} liked your note.
|
||||
{quantify(note.likedBy.length, 'person', 'people')} liked your note.
|
||||
</button>
|
||||
{#if isOpen}
|
||||
<button in:fly={{y: 20}} class="fixed inset-0 z-10" on:click={closePopover} />
|
||||
@ -39,8 +40,8 @@
|
||||
in:fly={{y: 20}}
|
||||
class="absolute top-0 mt-8 py-2 px-4 rounded border border-solid border-medium
|
||||
bg-dark grid grid-cols-3 gap-y-2 gap-x-4 z-20">
|
||||
{#each uniqBy(prop('pubkey'), note.people) as person (person.pubkey)}
|
||||
<Badge {person} />
|
||||
{#each uniq(note.likedBy) as pubkey}
|
||||
<Badge person={getPerson(pubkey)} />
|
||||
{/each}
|
||||
</button>
|
||||
{/if}
|
||||
|
@ -1,9 +1,11 @@
|
||||
<script>
|
||||
import {fade} from 'svelte/transition'
|
||||
import {Circle2} from 'svelte-loading-spinners'
|
||||
|
||||
export let delay = 1000
|
||||
</script>
|
||||
|
||||
<div class="py-20 flex flex-col gap-4 items-center justify-center" in:fade={{delay: 1000}}>
|
||||
<div class="py-20 flex flex-col gap-4 items-center justify-center" in:fade={{delay}}>
|
||||
<slot />
|
||||
<Circle2 colorOuter="#CCC5B9" colorInner="#403D39" colorCenter="#EB5E28" />
|
||||
</div>
|
||||
|
@ -3,14 +3,14 @@
|
||||
import {onMount} from 'svelte'
|
||||
import {fly} from 'svelte/transition'
|
||||
import {now, createScroller} from 'src/util/misc'
|
||||
import {user, db} from 'src/agent'
|
||||
import {db} from 'src/agent'
|
||||
import {alerts} from 'src/app'
|
||||
import Note from 'src/partials/Note.svelte'
|
||||
import Content from 'src/partials/Content.svelte'
|
||||
import Like from 'src/partials/Like.svelte'
|
||||
|
||||
let limit = 0
|
||||
let annotatedNotes = []
|
||||
let notes = []
|
||||
|
||||
onMount(async () => {
|
||||
alerts.lastCheckedAlerts.set(now())
|
||||
@ -19,37 +19,24 @@
|
||||
limit += 10
|
||||
|
||||
const events = await db.table('alerts').toArray()
|
||||
const notes = events.filter(e => e.kind === 1)
|
||||
const likes = events.filter(e => e.kind === 7)
|
||||
|
||||
// Combine likes of a single note. Remove grandchild likes
|
||||
const likesById = {}
|
||||
for (const like of likes.filter(e => e.parent?.pubkey === $user.pubkey)) {
|
||||
if (!likesById[like.parent.id]) {
|
||||
likesById[like.parent.id] = {...like.parent, people: []}
|
||||
}
|
||||
|
||||
likesById[like.parent.id].people.push(like.person)
|
||||
}
|
||||
|
||||
annotatedNotes = sortBy(
|
||||
e => -e.created_at,
|
||||
notes
|
||||
.filter(e => e && e.pubkey !== $user.pubkey)
|
||||
.concat(Object.values(likesById))
|
||||
).slice(0, limit)
|
||||
notes = sortBy(e => -e.created_at, events).slice(0, limit)
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<Content>
|
||||
{#each annotatedNotes as e (e.id)}
|
||||
{#each notes as e (e.id)}
|
||||
<div in:fly={{y: 20}}>
|
||||
{#if e.people}
|
||||
{#if e.likedBy}
|
||||
<Like note={e} />
|
||||
{:else}
|
||||
<Note note={e} />
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<Content size="lg" class="text-center">
|
||||
No alerts found - check back later!
|
||||
</Content>
|
||||
{/each}
|
||||
</Content>
|
||||
|
Loading…
Reference in New Issue
Block a user