Guess delta from filter, don't duplicate session across multiple stores

This commit is contained in:
Jonathan Staab 2023-09-14 17:14:07 -07:00
parent 66fcd2cc34
commit c313069bc0
23 changed files with 83 additions and 59 deletions

View File

@ -6,21 +6,21 @@
import Card from "src/partials/Card.svelte"
import PersonCircle from "src/app/shared/PersonCircle.svelte"
import {slowConnections} from "src/app/state"
import {env, session, sessions, logoutPubkey, displayPubkey} from "src/engine2"
import {env, pubkey, sessions, logoutPubkey, displayPubkey} from "src/engine2"
const showLogin = () => modal.push({type: "login/advanced"})
</script>
<Popover theme="transparent" placement="top-end" opts={{hideOnClick: true}}>
<div slot="trigger" class="relative flex cursor-pointer items-center">
<PersonCircle size={10} pubkey={$session.pubkey} />
<PersonCircle size={10} pubkey={$pubkey} />
</div>
<div slot="tooltip" class="flex justify-end">
<Card class="mt-1 w-48 overflow-hidden shadow-lg">
<div class="-mx-3 -mt-1">
<Anchor
class="block p-3 px-4 transition-all hover:bg-accent hover:text-white"
href={`/${nip19.npubEncode($session.pubkey)}`}>
href={`/${nip19.npubEncode($pubkey)}`}>
<i class="fa fa-user mr-2" /> Profile
</Anchor>
<Anchor class="block p-3 px-4 transition-all hover:bg-accent hover:text-white" href="/keys">
@ -49,11 +49,11 @@
</Anchor>
<div class="my-2 h-px w-full bg-gray-5" />
{#each Object.values($sessions) as s (s.pubkey)}
{#if s.pubkey !== $session.pubkey}
{#if s.pubkey !== $pubkey}
<div
class="block flex cursor-pointer items-center justify-between gap-2 p-3 px-4
transition-all hover:bg-accent hover:text-white"
on:click={() => session.set(s)}>
on:click={() => pubkey.set(s.pubkey)}>
<div class="flex items-center gap-2">
<PersonCircle pubkey={s.pubkey} />
{displayPubkey(s.pubkey)}

View File

@ -21,7 +21,6 @@
export let hideControls = false
export let queryCache = false
export let onEvent = null
export let delta = null
let scroller, feed, scrollerElement
let feedRelay = null
@ -90,7 +89,6 @@
filters: [compileFilter(filter)],
shouldLoadParents: true,
onEvent,
delta,
})
oldNotes = feed.feed

View File

@ -1,5 +1,4 @@
<script lang="ts">
import {seconds} from "hurdak"
import Feed from "src/app/shared/Feed.svelte"
export let pubkey
@ -8,4 +7,4 @@
const filter = {kinds: [7], authors: [pubkey]}
</script>
<Feed hideControls delta={seconds(12, "hour")} {relays} {filter} />
<Feed hideControls {relays} {filter} />

View File

@ -1,5 +1,4 @@
<script lang="ts">
import {seconds} from "hurdak"
import {noteKinds} from "src/util/nostr"
import Feed from "src/app/shared/Feed.svelte"
import {sessions} from "src/engine2/state"
@ -10,9 +9,4 @@
const filter = {kinds: noteKinds, authors: [pubkey]}
</script>
<Feed
invertColors
delta={seconds(12, "hour")}
queryCache={Boolean($sessions[pubkey])}
{relays}
{filter} />
<Feed invertColors queryCache={Boolean($sessions[pubkey])} {relays} {filter} />

View File

@ -12,12 +12,12 @@
import NoteById from "src/app/shared/NoteById.svelte"
import PersonBadgeSmall from "src/app/shared/PersonBadgeSmall.svelte"
import {
session,
getPubkeysWithDefaults,
labels,
getPubkeyHints,
session,
follows,
subscribe,
getPubkeyHints,
getPubkeysWithDefaults,
} from "src/engine2"
type LabelGroup = {

View File

@ -2,16 +2,14 @@
import Feed from "src/app/shared/Feed.svelte"
import Content from "src/partials/Content.svelte"
import Heading from "src/partials/Heading.svelte"
import {selectHints, getSetting} from "src/engine2"
import {selectHints} from "src/engine2"
export let label
export let hints
export let ids
const relays = selectHints(getSetting("relay_limit"), hints)
</script>
<Content>
<Heading>{label}</Heading>
<Feed invertColors {relays} filter={{ids}} />
<Feed invertColors relays={selectHints(hints)} filter={{ids}} />
</Content>

View File

@ -3,7 +3,7 @@
import {onMount} from "svelte"
import {fly} from "src/util/transition"
import {navigate} from "svelte-routing"
import {now, createScroller, formatTimestampAsLocalISODate} from "src/util/misc"
import {now, createScroller, formatTimestampAsDate} from "src/util/misc"
import {noteKinds, reactionKinds} from "src/util/nostr"
import Tabs from "src/partials/Tabs.svelte"
import Content from "src/partials/Content.svelte"
@ -28,11 +28,8 @@
const cur = tabNotifications[i]
const prev = tabNotifications[i - 1]
if (
!prev ||
formatTimestampAsLocalISODate(prev.timestamp) !== formatTimestampAsLocalISODate(cur.timestamp)
) {
return formatTimestampAsLocalISODate(cur.timestamp)
if (!prev || formatTimestampAsDate(prev.timestamp) !== formatTimestampAsDate(cur.timestamp)) {
return formatTimestampAsDate(cur.timestamp)
}
}

View File

@ -1,6 +1,5 @@
import {appDataKeys} from "src/util/nostr"
import {session} from "src/engine2/state"
import {canSign, nip04} from "src/engine2/queries"
import {session, canSign, nip04} from "src/engine2/queries"
import {publishEvent} from "./util"
export const setAppData = async (d: string, data: any) => {

View File

@ -1,9 +1,9 @@
import EventEmitter from "events"
import {omit} from "ramda"
import {defer, union, difference} from "hurdak"
import {info} from "src/util/logger"
import type {Event, NostrEvent} from "src/engine2/model"
import {getUrls, getExecutor} from "src/engine2/queries"
import {projections} from "src/engine2/projections"
export type PublisherOpts = {
timeout?: number
@ -17,13 +17,16 @@ export type StaticPublisherOpts = PublisherOpts & {
export class Publisher extends EventEmitter {
result = defer()
event: NostrEvent
constructor(readonly event: NostrEvent) {
constructor(event: NostrEvent) {
super()
if ((event as Event).wrap) {
throw new Error("Can't publish unwrapped events")
}
this.event = event
}
static publish({event, relays, ...opts}: StaticPublisherOpts) {
@ -63,6 +66,8 @@ export class Publisher extends EventEmitter {
this.emit("progress", progress)
}
projections.push({...this.event, seen_on: []})
setTimeout(() => {
for (const url of urls) {
if (!succeeded.has(url) && !failed.has(url)) {
@ -73,7 +78,7 @@ export class Publisher extends EventEmitter {
attemptToResolve()
}, timeout)
const sub = executor.publish(omit(["seen_on"], this.event), {
const sub = executor.publish(this.event, {
verb,
onOk: (url: string) => {
succeeded.add(url)

View File

@ -1,13 +1,13 @@
import {omit, assoc} from "ramda"
import {generatePrivateKey, getPublicKey} from "nostr-tools"
import type {Session} from "src/engine2/model"
import {pool, sessions, session} from "src/engine2/state"
import {canSign, signer} from "src/engine2/queries"
import {pool, sessions, pubkey} from "src/engine2/state"
import {canSign, signer, session} from "src/engine2/queries"
import {buildEvent} from "./util"
const addSession = (s: Session) => {
sessions.update(assoc(s.pubkey, s))
session.set(s)
pubkey.set(s.pubkey)
}
export const loginWithPrivateKey = privkey =>
@ -29,7 +29,7 @@ export const logoutPubkey = pubkey => {
}
export const logout = () => {
session.set(null)
pubkey.set(null)
sessions.set({})
}

View File

@ -69,7 +69,6 @@ projections.addHandler(30078, async (e: Event) => {
}
if (Tags.from(e).getMeta("d") === appDataKeys.NIP28_LAST_CHECKED) {
console.log(e)
await tryJson(async () => {
const payload = JSON.parse(await nip04.get().decryptAsUser(e.content, e.pubkey))

View File

@ -2,8 +2,8 @@ import {find, whereEq, nth} from "ramda"
import {derived} from "src/engine2/util"
import type {Event} from "src/engine2/model"
import {findReplyAndRootIds} from "src/util/nostr"
import {session, lists} from "src/engine2/state"
import {user} from "src/engine2/queries/session"
import {lists} from "src/engine2/state"
import {user, session} from "src/engine2/queries/session"
export const mutes = user.derived($user => ($user?.mutes || []).map(nth(1)))

View File

@ -1,5 +1,6 @@
import {prop} from "ramda"
import {env, session} from "src/engine2/state"
import {env} from "src/engine2/state"
import {session} from "src/engine2/queries"
export const getDefaultSettings = () => ({
relay_limit: 10,

View File

@ -2,12 +2,16 @@ import {max, sortBy} from "ramda"
import {Tags, findReplyId, findReplyAndRootIds} from "src/util/nostr"
import {formatTimestampAsLocalISODate, tryJson} from "src/util/misc"
import {derived} from "src/engine2/util/store"
import {session, events, notificationsLastChecked} from "src/engine2/state"
import {userEventsById} from "src/engine2/queries/session"
import {events, notificationsLastChecked} from "src/engine2/state"
import {session, userEventsById} from "src/engine2/queries/session"
export const notifications = derived(
[session, userEventsById.throttle(500), events.throttle(500)],
([$session, $userEventsById, $events]) => {
if (!$session) {
return []
}
return $events.filter(e => {
const {root, reply} = findReplyAndRootIds(e)

View File

@ -2,7 +2,7 @@ import {nip19} from "nostr-tools"
import {propEq} from "ramda"
import {createMap} from "hurdak"
import {derived} from "src/engine2/util/store"
import {session, events, people} from "src/engine2/state"
import {sessions, pubkey, events, people} from "src/engine2/state"
import {prepareNdk, ndkInstances} from "./ndk"
import {Signer} from "./signer"
import {Nip04} from "./nip04"
@ -21,7 +21,9 @@ export const isKeyValid = (key: string) => {
return true
}
export const stateKey = session.derived($s => $s?.pubkey || "anonymous")
export const stateKey = pubkey.derived($pk => $pk || "anonymous")
export const session = derived([pubkey, sessions], ([$pk, $sessions]) => $sessions[$pk])
export const user = derived([session, people.mapStore], ([$s, $p]) => $p.get($s?.pubkey))

View File

@ -1,16 +1,16 @@
import {mergeRight, identity, sortBy} from "ramda"
import {seconds, first} from "hurdak"
import {first} from "hurdak"
import {now} from "src/util/misc"
import {EPOCH} from "src/util/nostr"
import type {Filter, Event} from "src/engine2/model"
import type {Subscription} from "./subscription"
import {subscribe} from "./subscription"
import {guessFilterDelta} from "./filter"
export type CursorOpts = {
relay: string
filters: Filter[]
onEvent?: (e: Event) => void
delta?: number
}
export class Cursor {
@ -23,7 +23,7 @@ export class Cursor {
done = false
constructor(readonly opts: CursorOpts) {
this.delta = opts.delta || seconds(10, "minute")
this.delta = guessFilterDelta(opts.filters)
this.since = now() - this.delta
}

View File

@ -13,7 +13,6 @@ export type FeedOpts = {
depth: number
relays: string[]
filters: Filter[]
delta?: number
onEvent?: (e: Event) => void
shouldLoadParents?: boolean
}
@ -61,7 +60,6 @@ export class FeedLoader {
relay =>
new Cursor({
relay,
delta: opts.delta,
filters: opts.filters,
onEvent: batch(100, (context: Event[]) => {
this.context.addContext(context, {shouldLoadParents: true, depth: opts.depth})

View File

@ -1,5 +1,5 @@
import {omit, prop, groupBy, uniq} from "ramda"
import {shuffle} from "hurdak"
import {omit, find, prop, groupBy, uniq} from "ramda"
import {shuffle, seconds, avg} from "hurdak"
import type {DynamicFilter, Filter} from "src/engine2/model"
import {env} from "src/engine2/state"
import {follows, network} from "src/engine2/queries"
@ -57,6 +57,30 @@ export const getIdFilters = values => {
return filters
}
export const getFilterGenerality = filter => {
if (filter.ids) {
return 1
}
const hasTags = find(k => k.startsWith("#"), Object.keys(filter))
if (filter.pubkeys && hasTags) {
return 0.8
}
if (filter.pubkeys) {
return 1 - Math.max(1, filter.pubkeys.length / 100)
}
return 0
}
export const guessFilterDelta = filters => {
const avgGenerality = avg(filters.map(getFilterGenerality))
return Math.round(seconds(7, "day") * Math.max(0.001, avgGenerality))
}
export const getPubkeysWithDefaults = (pubkeys: string[]) =>
shuffle(pubkeys.length > 0 ? pubkeys : (env.get().DEFAULT_FOLLOWS as string[])).slice(0, 1024)

View File

@ -2,8 +2,8 @@ import {pluck} from "ramda"
import {batch, seconds} from "hurdak"
import {now} from "src/util/misc"
import {EventKind} from "src/engine2/model"
import {session, nip04ChannelsLastChecked} from "src/engine2/state"
import {getInboxHints, getUserRelayUrls} from "src/engine2/queries"
import {nip04ChannelsLastChecked} from "src/engine2/state"
import {session, getInboxHints, getUserRelayUrls} from "src/engine2/queries"
import {load} from "./load"
import {loadPubkeys} from "./pubkeys"
import {subscribe} from "./subscription"

View File

@ -1,8 +1,8 @@
import {seconds} from "hurdak"
import {now} from "src/util/misc"
import {EventKind} from "src/engine2/model"
import {session, nip24ChannelsLastChecked} from "src/engine2/state"
import {getUserRelayUrls, nip24Channels, getNip24ChannelPubkeys} from "src/engine2/queries"
import {nip24ChannelsLastChecked} from "src/engine2/state"
import {session, getUserRelayUrls, nip24Channels, getNip24ChannelPubkeys} from "src/engine2/queries"
import {load} from "./load"
import {loadPubkeys} from "./pubkeys"

View File

@ -4,8 +4,8 @@ import type {Event, Delete, Session, Channel, Topic, List, Person, Relay} from "
// Synchronous stores
export const pubkey = writable<string | null>(null)
export const sessions = writable<Record<string, Session>>({})
export const session = writable<Session | null>(null)
export const env = writable<Record<string, any>>({})
export const notificationsLastChecked = writable(0)
export const nip04ChannelsLastChecked = writable(0)

View File

@ -24,8 +24,8 @@ const sortByPubkeyWhitelist = (fallback: (x: any) => number) => (rows: Record<st
}
export const storage = new Storage([
new LocalStorageAdapter("pubkey", state.pubkey),
new LocalStorageAdapter("sessions", state.sessions),
new LocalStorageAdapter("session", state.session),
new LocalStorageAdapter("notificationsLastChecked", state.notificationsLastChecked),
new LocalStorageAdapter("nip04ChannelsLastChecked", state.nip04ChannelsLastChecked),
new LocalStorageAdapter("nip24ChannelsLastChecked", state.nip24ChannelsLastChecked),

View File

@ -16,6 +16,12 @@
"bg-input border border-solid border-gray-3 text-black",
{"pl-10": showBefore, "pr-10": showAfter}
)
$: {
if ($$props.type === "range" && typeof value === "string") {
value = parseInt(value)
}
}
</script>
<div class={cx(wrapperClass, "relative")}>