mirror of
https://github.com/dergigi/boris.git
synced 2025-12-19 15:44:20 +01:00
feat(archive): support un-archive toggle; add ArchiveController mark/unmark; prep NIP-09 deletion hook
This commit is contained in:
@@ -599,7 +599,32 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
||||
}, [selectedUrl, currentArticle, activeAccount, relayPool, isNostrArticle])
|
||||
|
||||
const handleMarkAsRead = () => {
|
||||
if (!activeAccount || !relayPool || isMarkedAsRead) {
|
||||
if (!activeAccount || !relayPool) return
|
||||
|
||||
// Toggle archive state: if already archived, request deletion; else archive
|
||||
if (isMarkedAsRead) {
|
||||
// Optimistically unarchive in UI; background deletion request (NIP-09)
|
||||
setIsMarkedAsRead(false)
|
||||
;(async () => {
|
||||
try {
|
||||
// Best-effort: we don't store reaction IDs yet; leaving placeholder for future improvement
|
||||
// When we track reaction IDs, call deleteReaction(id,...)
|
||||
// For now, clear controller mark so lists update
|
||||
if (isNostrArticle && currentArticle) {
|
||||
try {
|
||||
const dTag = currentArticle.tags.find(t => t[0] === 'd')?.[1]
|
||||
if (dTag) {
|
||||
const naddr = nip19.naddrEncode({ kind: 30023, pubkey: currentArticle.pubkey, identifier: dTag })
|
||||
archiveController.unmark(naddr)
|
||||
}
|
||||
} catch {}
|
||||
} else if (selectedUrl) {
|
||||
archiveController.unmark(selectedUrl)
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[archive][content] unarchive failed', err)
|
||||
}
|
||||
})()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -640,6 +665,7 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore private update for instant UI; controller will confirm via stream
|
||||
archiveController['markedIds'].add(naddr)
|
||||
archiveController.mark(naddr)
|
||||
console.log('[archive][content] optimistic mark article', naddr.slice(0, 24) + '...')
|
||||
}
|
||||
} catch {}
|
||||
@@ -652,6 +678,7 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore private update for instant UI; controller will confirm via stream
|
||||
archiveController['markedIds'].add(selectedUrl)
|
||||
archiveController.mark(selectedUrl)
|
||||
console.log('[archive][content] optimistic mark url', selectedUrl)
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -31,6 +31,21 @@ class ArchiveController {
|
||||
this.listeners.forEach(cb => cb(snapshot))
|
||||
}
|
||||
|
||||
mark(id: string): void {
|
||||
if (!this.markedIds.has(id)) {
|
||||
this.markedIds.add(id)
|
||||
this.emit()
|
||||
console.log('[archive] mark() added', id.slice(0, 48))
|
||||
}
|
||||
}
|
||||
|
||||
unmark(id: string): void {
|
||||
if (this.markedIds.delete(id)) {
|
||||
this.emit()
|
||||
console.log('[archive] unmark() removed', id.slice(0, 48))
|
||||
}
|
||||
}
|
||||
|
||||
isMarked(id: string): boolean {
|
||||
return this.markedIds.has(id)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { IAccount } from 'applesauce-accounts'
|
||||
import { NostrEvent } from 'nostr-tools'
|
||||
import { lastValueFrom, takeUntil, timer, toArray } from 'rxjs'
|
||||
import { RELAYS } from '../config/relays'
|
||||
import { EventFactory } from 'applesauce-factory'
|
||||
|
||||
const MARK_AS_READ_EMOJI = '📚'
|
||||
|
||||
@@ -105,6 +106,27 @@ export async function createWebsiteReaction(
|
||||
return signed
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a deletion request (NIP-09) for a reaction event to effectively un-archive.
|
||||
* The caller must know the reaction event id to delete.
|
||||
*/
|
||||
export async function deleteReaction(
|
||||
reactionEventId: string,
|
||||
account: IAccount,
|
||||
relayPool: RelayPool
|
||||
): Promise<NostrEvent> {
|
||||
const factory = new EventFactory({ signer: account })
|
||||
const draft = await factory.create(async () => ({
|
||||
kind: 5, // Deletion per NIP-09
|
||||
content: 'unarchive',
|
||||
tags: [['e', reactionEventId]],
|
||||
created_at: Math.floor(Date.now() / 1000)
|
||||
}))
|
||||
const signed = await factory.sign(draft)
|
||||
await relayPool.publish(RELAYS, signed)
|
||||
return signed
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user has already marked a nostr event as read
|
||||
* @param eventId The ID of the event to check
|
||||
|
||||
Reference in New Issue
Block a user