mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-29 08:21:20 +00:00
Stop using bloom filters for deletes, false positives do happen
This commit is contained in:
parent
5c15c5462e
commit
eddab7010c
@ -45,7 +45,6 @@
|
||||
"@nostr-dev-kit/ndk": "^0.7.0",
|
||||
"@scure/base": "^1.1.1",
|
||||
"@tsconfig/svelte": "^3.0.0",
|
||||
"bloom-filters": "^3.0.0",
|
||||
"classnames": "^2.3.2",
|
||||
"compressorjs": "^1.1.1",
|
||||
"fuse.js": "^6.6.2",
|
||||
|
@ -18,7 +18,6 @@
|
||||
env,
|
||||
load,
|
||||
processZap,
|
||||
deriveMuted,
|
||||
derivePerson,
|
||||
getReplyHints,
|
||||
isEventMuted,
|
||||
@ -46,6 +45,7 @@
|
||||
let event = note
|
||||
let reply = null
|
||||
let replyIsActive = false
|
||||
let showMuted = false
|
||||
let actions = null
|
||||
let collapsed = false
|
||||
let replies = sortBy(
|
||||
@ -59,7 +59,6 @@
|
||||
const borderColor = invertColors ? "gray-6" : "gray-7"
|
||||
const showEntire = anchorId === event.id
|
||||
const interactive = !anchorId || !showEntire
|
||||
const muted = deriveMuted(event.id)
|
||||
|
||||
let interval, border, childrenContainer, noteContainer
|
||||
|
||||
@ -101,6 +100,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: muted = !showMuted && $isEventMuted(event)
|
||||
|
||||
// Show only notes that were passed in by the parent unless we want to show all
|
||||
$: visibleNotes = ((showContext ? replies : note.replies) || []).filter(
|
||||
e => !feedRelay || e.seen_on.includes(feedRelay.url)
|
||||
@ -219,20 +220,25 @@
|
||||
</small>
|
||||
{/if}
|
||||
</div>
|
||||
{#if $muted}
|
||||
{#if muted}
|
||||
<p class="border-l-2 border-solid border-gray-6 pl-4 text-gray-1">
|
||||
You have muted this note.
|
||||
<Anchor
|
||||
theme="anchor"
|
||||
on:click={() => {
|
||||
showMuted = true
|
||||
}}>Show</Anchor>
|
||||
</p>
|
||||
{:else}
|
||||
<NoteContent {anchorId} note={event} {showEntire} />
|
||||
{/if}
|
||||
<NoteActions
|
||||
note={event}
|
||||
muted={$muted}
|
||||
bind:this={actions}
|
||||
bind:replies
|
||||
bind:likes
|
||||
bind:zaps
|
||||
{muted}
|
||||
{reply}
|
||||
{setFeedRelay}
|
||||
{showEntire} />
|
||||
@ -241,7 +247,7 @@
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{#if !replyIsActive && visibleNotes.length > 0 && !showEntire && depth > 0 && !$muted}
|
||||
{#if !replyIsActive && visibleNotes.length > 0 && !showEntire && depth > 0 && !muted}
|
||||
<div class="relative">
|
||||
<div
|
||||
class="absolute right-0 top-0 z-10 -mr-2 -mt-4 flex h-6 w-6 cursor-pointer items-center
|
||||
@ -276,7 +282,7 @@
|
||||
}}
|
||||
{borderColor} />
|
||||
|
||||
{#if !collapsed && visibleNotes.length > 0 && depth > 0 && !$muted}
|
||||
{#if !collapsed && visibleNotes.length > 0 && depth > 0 && !muted}
|
||||
<div class="relative mt-4">
|
||||
<div class={`absolute w-px bg-${borderColor} z-10 -mt-4 ml-4 h-0`} bind:this={border} />
|
||||
<div class="note-children relative ml-8 flex flex-col gap-4" bind:this={childrenContainer}>
|
||||
|
@ -37,7 +37,9 @@
|
||||
|
||||
let limit = 20
|
||||
|
||||
$: groupedNotifications = groupNotifications($throttledNotifications).slice(0, limit)
|
||||
$: tabKinds = activeTab === tabs[0] ? noteKinds : reactionKinds.concat(9734)
|
||||
|
||||
$: groupedNotifications = groupNotifications($throttledNotifications, tabKinds).slice(0, limit)
|
||||
|
||||
$: tabNotifications =
|
||||
activeTab === tabs[0]
|
||||
|
@ -1,27 +1,7 @@
|
||||
import {prop} from "ramda"
|
||||
import {batch} from "hurdak"
|
||||
import {Worker} from "src/engine/core/utils"
|
||||
import type {Event} from "src/engine/events/model"
|
||||
import {sessions} from "src/engine/session/state"
|
||||
import {_events} from "src/engine/events/state"
|
||||
|
||||
export const projections = new Worker<Event>({
|
||||
getKey: prop("kind"),
|
||||
})
|
||||
|
||||
projections.addGlobalHandler(
|
||||
batch(500, (chunk: Event[]) => {
|
||||
const $sessions = sessions.get()
|
||||
const userEvents = chunk.filter(e => $sessions[e.pubkey])
|
||||
|
||||
if (userEvents.length > 0) {
|
||||
_events.mapStore.update($events => {
|
||||
for (const e of userEvents) {
|
||||
$events.set(e.id, e)
|
||||
}
|
||||
|
||||
return $events
|
||||
})
|
||||
}
|
||||
})
|
||||
)
|
||||
|
@ -270,15 +270,25 @@ export class Collection<T extends R> implements Readable<T[]> {
|
||||
map = (f: (v: T) => T) => this.update(map(f))
|
||||
}
|
||||
|
||||
export class DerivedCollection<T extends R> extends Derived<T[]> {
|
||||
export class DerivedCollection<T extends R> implements Readable<T[]> {
|
||||
readonly listStore: Derived<T[]>
|
||||
readonly mapStore: Readable<M<T>>
|
||||
|
||||
constructor(readonly pk: string, stores: Derivable, getValue: (values: any) => T[], t = 0) {
|
||||
super(stores, getValue, t)
|
||||
|
||||
this.mapStore = new Derived(this, xs => new Map(xs.map(x => [x[pk], x])), t)
|
||||
this.listStore = new Derived(stores, getValue, t)
|
||||
this.mapStore = new Derived(this.listStore, xs => new Map(xs.map(x => [x[pk], x])))
|
||||
}
|
||||
|
||||
get = () => this.listStore.get()
|
||||
|
||||
getMap = () => this.mapStore.get()
|
||||
|
||||
subscribe = (f: Subscriber<T[]>) => this.listStore.subscribe(f)
|
||||
|
||||
derived = <U>(f: (v: T[]) => U) => this.listStore.derived<U>(f)
|
||||
|
||||
throttle = (t: number) => this.listStore.throttle(t)
|
||||
|
||||
key = (k: string) => new DerivedKey(this.mapStore, this.pk, k)
|
||||
}
|
||||
|
||||
|
@ -11,9 +11,9 @@ export const events = new DerivedCollection<Event>("id", [_events, deletes], ([$
|
||||
$e.filter(e => !$d.has(e.id))
|
||||
)
|
||||
|
||||
export const userEvents = new DerivedCollection<Event>("id", [events, pubkey], ([$e, $pk]) =>
|
||||
$pk ? $e.filter(whereEq({pubkey: $pk})) : []
|
||||
)
|
||||
export const userEvents = new DerivedCollection<Event>("id", [events, pubkey], ([$e, $pk]) => {
|
||||
return $pk ? $e.filter(whereEq({pubkey: $pk})) : []
|
||||
})
|
||||
|
||||
export const isEventMuted = derived([mutes, settings], ([$mutes, $settings]) => {
|
||||
const words = $settings.muted_words
|
||||
@ -27,7 +27,7 @@ export const isEventMuted = derived([mutes, settings], ([$mutes, $settings]) =>
|
||||
return true
|
||||
}
|
||||
|
||||
if (regex && e.content.toLowerCase().match(regex)) {
|
||||
if (regex && e.content?.toLowerCase().match(regex)) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,22 @@
|
||||
import {batch} from "hurdak"
|
||||
import {Tags} from "src/util/nostr"
|
||||
import {projections} from "src/engine/core/projections"
|
||||
import type {Event} from "src/engine/events/model"
|
||||
import {sessions} from "src/engine/session/state"
|
||||
import {nip59} from "src/engine/session/derived"
|
||||
import {EventKind} from "./model"
|
||||
import {deletes, deletesLastUpdated} from "./state"
|
||||
import {_events, deletes, deletesLastUpdated} from "./state"
|
||||
|
||||
projections.addGlobalHandler(
|
||||
batch(500, (chunk: Event[]) => {
|
||||
const $sessions = sessions.get()
|
||||
const userEvents = chunk.filter(e => $sessions[e.pubkey])
|
||||
|
||||
if (userEvents.length > 0) {
|
||||
_events.update($events => $events.concat(userEvents))
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
projections.addHandler(EventKind.Delete, e => {
|
||||
const values = Tags.from(e).type(["a", "e"]).values().all()
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {ScalableBloomFilter} from "bloom-filters"
|
||||
import {Collection, Writable, writable} from "src/engine/core/utils"
|
||||
import type {Event} from "./model"
|
||||
|
||||
export const _events = new Collection<Event>("id", 1000)
|
||||
export const deletes = new Writable(new ScalableBloomFilter(), 1000)
|
||||
export const deletes = new Writable(new Set(), 1000)
|
||||
export const deletesLastUpdated = writable(0)
|
||||
|
@ -1,6 +1,4 @@
|
||||
import {ScalableBloomFilter} from "bloom-filters"
|
||||
import {prop, sortBy} from "ramda"
|
||||
import {tryFunc} from "hurdak"
|
||||
import {Storage, LocalStorageAdapter, IndexedDBAdapter, sortByPubkeyWhitelist} from "./core"
|
||||
import {_lists} from "./lists"
|
||||
import {people} from "./people"
|
||||
@ -36,11 +34,11 @@ export * from "./zaps"
|
||||
export const storage = new Storage([
|
||||
new LocalStorageAdapter("pubkey", pubkey),
|
||||
new LocalStorageAdapter("sessions", sessions),
|
||||
new LocalStorageAdapter("deletes", deletes, {
|
||||
dump: f => f.saveAsJSON(),
|
||||
load: d => tryFunc(() => ScalableBloomFilter.fromJSON(d)) || new ScalableBloomFilter(),
|
||||
new LocalStorageAdapter("deletes2", deletes, {
|
||||
dump: s => Array.from(s),
|
||||
load: a => new Set(a || []),
|
||||
}),
|
||||
new LocalStorageAdapter("deletesLastUpdated", deletesLastUpdated),
|
||||
new LocalStorageAdapter("deletesLastUpdated2", deletesLastUpdated),
|
||||
new LocalStorageAdapter("notificationsLastChecked", notificationsLastChecked),
|
||||
new LocalStorageAdapter("nip04ChannelsLastChecked", nip04ChannelsLastChecked),
|
||||
new LocalStorageAdapter("nip24ChannelsLastChecked", nip24ChannelsLastChecked),
|
||||
|
@ -44,7 +44,7 @@ export const hasNewNotifications = derived(
|
||||
}
|
||||
)
|
||||
|
||||
export const groupNotifications = $notifications => {
|
||||
export const groupNotifications = ($notifications, kinds) => {
|
||||
const $userEvents = userEvents.mapStore.get()
|
||||
|
||||
// Convert zaps to zap requests
|
||||
@ -62,6 +62,10 @@ export const groupNotifications = $notifications => {
|
||||
for (const ix of $notifications) {
|
||||
const event = $userEvents.get(findReplyId(ix))
|
||||
|
||||
if (!kinds.includes(ix.kind)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (reactionKinds.includes(ix.kind) && !event) {
|
||||
continue
|
||||
}
|
||||
@ -78,7 +82,7 @@ export const groupNotifications = $notifications => {
|
||||
Object.values(groups).map((group: any) => {
|
||||
const {event, interactions} = group
|
||||
const timestamp = interactions
|
||||
.map(ix => ix.created_at)
|
||||
.map(e => e.created_at)
|
||||
.concat(event?.created_at || 0)
|
||||
.reduce(max, 0)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user