From a8bea51636255e1e2f3a4f690fc8e39e7ae40c4e Mon Sep 17 00:00:00 2001 From: Silberengel Date: Fri, 10 Oct 2025 22:56:48 +0200 Subject: [PATCH] dynamic subtopics cont. --- src/App.tsx | 15 +- .../DiscussionNotification.tsx | 33 ++++ .../NotificationItem/index.tsx | 4 + src/components/TopicSubscribeButton/index.tsx | 90 ++++++++++ src/constants.ts | 2 +- src/lib/discussion-topics.ts | 2 + src/lib/draft-event.ts | 9 + src/pages/primary/DiscussionsPage/index.tsx | 29 ++-- src/providers/InterestListProvider.tsx | 155 ++++++++++++++++++ src/providers/NostrProvider/index.tsx | 20 +++ src/providers/NotificationProvider.tsx | 57 ++++++- src/services/client.service.ts | 4 + src/services/indexed-db.service.ts | 8 + 13 files changed, 408 insertions(+), 20 deletions(-) create mode 100644 src/components/NotificationList/NotificationItem/DiscussionNotification.tsx create mode 100644 src/components/TopicSubscribeButton/index.tsx create mode 100644 src/providers/InterestListProvider.tsx diff --git a/src/App.tsx b/src/App.tsx index fee7dd5..a265c9a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,6 +8,7 @@ import { DeletedEventProvider } from '@/providers/DeletedEventProvider' import { FavoriteRelaysProvider } from '@/providers/FavoriteRelaysProvider' import { FeedProvider } from '@/providers/FeedProvider' import { FollowListProvider } from '@/providers/FollowListProvider' +import { InterestListProvider } from '@/providers/InterestListProvider' import { KindFilterProvider } from '@/providers/KindFilterProvider' import { MediaUploadServiceProvider } from '@/providers/MediaUploadServiceProvider' import { MuteListProvider } from '@/providers/MuteListProvider' @@ -33,9 +34,10 @@ export default function App(): JSX.Element { - - - + + + + @@ -46,9 +48,10 @@ export default function App(): JSX.Element { - - - + + + + diff --git a/src/components/NotificationList/NotificationItem/DiscussionNotification.tsx b/src/components/NotificationList/NotificationItem/DiscussionNotification.tsx new file mode 100644 index 0000000..c7466e7 --- /dev/null +++ b/src/components/NotificationList/NotificationItem/DiscussionNotification.tsx @@ -0,0 +1,33 @@ +import { MessageCircle } from 'lucide-react' +import { Event } from 'nostr-tools' +import { useTranslation } from 'react-i18next' +import Notification from './Notification' + +export function DiscussionNotification({ + notification, + isNew = false +}: { + notification: Event + isNew?: boolean +}) { + const { t } = useTranslation() + + // Get the topic from t-tags + const topicTags = notification.tags.filter(tag => tag[0] === 't' && tag[1]) + const topics = topicTags.map(tag => tag[1]) + const topicString = topics.length > 0 ? topics.join(', ') : t('general') + + return ( + } + targetEvent={notification} + isNew={isNew} + showStats={false} + /> + ) +} + diff --git a/src/components/NotificationList/NotificationItem/index.tsx b/src/components/NotificationList/NotificationItem/index.tsx index 766ecc2..0bd8051 100644 --- a/src/components/NotificationList/NotificationItem/index.tsx +++ b/src/components/NotificationList/NotificationItem/index.tsx @@ -6,6 +6,7 @@ import { useNostr } from '@/providers/NostrProvider' import { useUserTrust } from '@/providers/UserTrustProvider' import { Event, kinds } from 'nostr-tools' import { useMemo } from 'react' +import { DiscussionNotification } from './DiscussionNotification' import { MentionNotification } from './MentionNotification' import { PollResponseNotification } from './PollResponseNotification' import { PublicMessageNotification } from './PublicMessageNotification' @@ -41,6 +42,9 @@ export function NotificationItem({ ]) if (!canShow) return null + if (notification.kind === 11) { + return + } if (notification.kind === kinds.Reaction) { return } diff --git a/src/components/TopicSubscribeButton/index.tsx b/src/components/TopicSubscribeButton/index.tsx new file mode 100644 index 0000000..c051cd4 --- /dev/null +++ b/src/components/TopicSubscribeButton/index.tsx @@ -0,0 +1,90 @@ +import { Button } from '@/components/ui/button' +import { useInterestList } from '@/providers/InterestListProvider' +import { useNostr } from '@/providers/NostrProvider' +import { Bell, BellOff, Loader2 } from 'lucide-react' +import { useTranslation } from 'react-i18next' + +interface TopicSubscribeButtonProps { + topic: string + variant?: 'default' | 'outline' | 'ghost' | 'icon' + size?: 'default' | 'sm' | 'lg' | 'icon' + showLabel?: boolean +} + +export default function TopicSubscribeButton({ + topic, + variant = 'outline', + size = 'sm', + showLabel = true +}: TopicSubscribeButtonProps) { + const { t } = useTranslation() + const { pubkey } = useNostr() + const { isSubscribed, subscribe, unsubscribe, changing } = useInterestList() + + if (!pubkey) { + return null + } + + const subscribed = isSubscribed(topic) + + const handleClick = async (e: React.MouseEvent) => { + e.stopPropagation() + e.preventDefault() + + if (changing) return + + if (subscribed) { + await unsubscribe(topic) + } else { + await subscribe(topic) + } + } + + if (variant === 'icon' || !showLabel) { + return ( + + ) + } + + return ( + + ) +} + diff --git a/src/constants.ts b/src/constants.ts index ae14853..445853e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -157,7 +157,7 @@ export const EMAIL_REGEX = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ export const EMOJI_SHORT_CODE_REGEX = /:[a-zA-Z0-9_-]+:/g export const EMBEDDED_EVENT_REGEX = /nostr:(note1[a-z0-9]{58}|nevent1[a-z0-9]+|naddr1[a-z0-9]+)/g export const EMBEDDED_MENTION_REGEX = /nostr:(npub1[a-z0-9]{58}|nprofile1[a-z0-9]+)/g -export const HASHTAG_REGEX = /#[\p{L}\p{N}\p{M}_]+/gu +export const HASHTAG_REGEX = /#[a-zA-Z0-9_\u00C0-\u017F\u0100-\u017F\u0180-\u024F\u1E00-\u1EFF]+/g export const LN_INVOICE_REGEX = /(ln(?:bc|tb|bcrt))([0-9]+[munp]?)?1([02-9ac-hj-np-z]+)/g export const EMOJI_REGEX = /[\u{1F600}-\u{1F64F}]|[\u{1F300}-\u{1F5FF}]|[\u{1F680}-\u{1F6FF}]|[\u{1F1E0}-\u{1F1FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]|[\u{1F900}-\u{1F9FF}]|[\u{1FA70}-\u{1FAFF}]|[\u{1F004}]|[\u{1F0CF}]|[\u{1F18E}]|[\u{3030}]|[\u{2B50}]|[\u{2B55}]|[\u{2934}-\u{2935}]|[\u{2B05}-\u{2B07}]|[\u{2B1B}-\u{2B1C}]|[\u{3297}]|[\u{3299}]|[\u{303D}]|[\u{00A9}]|[\u{00AE}]|[\u{2122}]|[\u{23E9}-\u{23EF}]|[\u{23F0}]|[\u{23F3}]|[\u{FE00}-\u{FE0F}]|[\u{200D}]/gu diff --git a/src/lib/discussion-topics.ts b/src/lib/discussion-topics.ts index 2d9cc06..6c7d400 100644 --- a/src/lib/discussion-topics.ts +++ b/src/lib/discussion-topics.ts @@ -69,6 +69,7 @@ export function analyzeThreadTopics( for (const thread of threads) { const allTopics = extractAllTopics(thread) + // Find the primary topic (first match from available topics) let primaryTopic = 'general' for (const topic of allTopics) { @@ -118,6 +119,7 @@ export function getDynamicSubtopics( const subtopics: string[] = [] + for (const [subtopic, npubs] of analysis.subtopics.entries()) { if (npubs.size >= minNpubs) { subtopics.push(subtopic) diff --git a/src/lib/draft-event.ts b/src/lib/draft-event.ts index 9675070..330e4a1 100644 --- a/src/lib/draft-event.ts +++ b/src/lib/draft-event.ts @@ -452,6 +452,15 @@ export function createBookmarkDraftEvent(tags: string[][], content = ''): TDraft } } +export function createInterestListDraftEvent(topics: string[], content = ''): TDraftEvent { + return { + kind: 10015, + content, + tags: topics.map(topic => ['t', topic]), + created_at: dayjs().unix() + } +} + export function createBlossomServerListDraftEvent(servers: string[]): TDraftEvent { return { kind: ExtendedKind.BLOSSOM_SERVER_LIST, diff --git a/src/pages/primary/DiscussionsPage/index.tsx b/src/pages/primary/DiscussionsPage/index.tsx index 56775d3..bb5a943 100644 --- a/src/pages/primary/DiscussionsPage/index.tsx +++ b/src/pages/primary/DiscussionsPage/index.tsx @@ -14,6 +14,7 @@ import ThreadSort, { SortOption } from '@/pages/primary/DiscussionsPage/ThreadSo import CreateThreadDialog, { DISCUSSION_TOPICS } from '@/pages/primary/DiscussionsPage/CreateThreadDialog' import ViewToggle from '@/pages/primary/DiscussionsPage/ViewToggle' import SubtopicFilter from '@/pages/primary/DiscussionsPage/SubtopicFilter' +import TopicSubscribeButton from '@/components/TopicSubscribeButton' import { NostrEvent } from 'nostr-tools' import client from '@/services/client.service' import noteStatsService from '@/services/note-stats.service' @@ -122,7 +123,6 @@ const DiscussionsPage = forwardRef((_, ref) => { setCustomVoteStats({}) // Clear custom stats when fetching try { // Fetch all kind 11 events (limit 100, newest first) - console.log('Fetching kind 11 events from relays:', relayUrls) // Fetch recent kind 11 events (last 30 days) const thirtyDaysAgo = Math.floor((Date.now() - (30 * 24 * 60 * 60 * 1000)) / 1000) @@ -168,14 +168,14 @@ const DiscussionsPage = forwardRef((_, ref) => { }, [fetchAllThreads]) // Analyze topics whenever threads change - useEffect(() => { - if (allThreads.length > 0) { - const analysis = analyzeThreadTopics(allThreads, availableTopicIds) - setTopicAnalysis(analysis) - } else { - setTopicAnalysis(new Map()) - } - }, [allThreads, availableTopicIds]) + useEffect(() => { + if (allThreads.length > 0) { + const analysis = analyzeThreadTopics(allThreads, availableTopicIds) + setTopicAnalysis(analysis) + } else { + setTopicAnalysis(new Map()) + } + }, [allThreads, availableTopicIds]) // Update available subtopics when topic analysis or selected topic changes useEffect(() => { @@ -526,9 +526,14 @@ const DiscussionsPage = forwardRef((_, ref) => { >
-

- {t('Discussions')} - {selectedTopic === 'all' ? t('All Topics') : DISCUSSION_TOPICS.find(t => t.id === selectedTopic)?.label} -

+
+

+ {t('Discussions')} - {selectedTopic === 'all' ? t('All Topics') : DISCUSSION_TOPICS.find(t => t.id === selectedTopic)?.label} +

+ {selectedTopic !== 'all' && selectedTopic !== 'general' && ( + + )} +
{selectedTopic === 'all' && ( + changing: boolean + isSubscribed: (topic: string) => boolean + subscribe: (topic: string) => Promise + unsubscribe: (topic: string) => Promise + getSubscribedTopics: () => string[] +} + +const InterestListContext = createContext(undefined) + +export const useInterestList = () => { + const context = useContext(InterestListContext) + if (!context) { + throw new Error('useInterestList must be used within an InterestListProvider') + } + return context +} + +export function InterestListProvider({ children }: { children: React.ReactNode }) { + const { t } = useTranslation() + const { pubkey: accountPubkey, interestListEvent, publish, updateInterestListEvent } = useNostr() + const [topics, setTopics] = useState([]) + const subscribedTopics = useMemo(() => new Set(topics), [topics]) + const [changing, setChanging] = useState(false) + + useEffect(() => { + const updateTopics = () => { + if (!interestListEvent) { + setTopics([]) + return + } + + // Extract t-tags from the interest list + const topicTags = interestListEvent.tags + .filter(tag => tag[0] === 't' && tag[1]) + .map(tag => normalizeTopic(tag[1])) + + setTopics(topicTags) + } + updateTopics() + }, [interestListEvent]) + + const getSubscribedTopics = useCallback(() => { + return Array.from(subscribedTopics) + }, [subscribedTopics]) + + const isSubscribed = useCallback( + (topic: string): boolean => { + return subscribedTopics.has(normalizeTopic(topic)) + }, + [subscribedTopics] + ) + + const publishNewInterestListEvent = async (newTopics: string[]) => { + const newInterestListEvent = createInterestListDraftEvent(newTopics) + const publishedEvent = await publish(newInterestListEvent) + return publishedEvent + } + + const subscribe = async (topic: string) => { + if (!accountPubkey || changing) return + + const normalizedTopic = normalizeTopic(topic) + if (subscribedTopics.has(normalizedTopic)) { + return + } + + setChanging(true) + try { + const interestListEvent = await client.fetchInterestListEvent(accountPubkey) + const currentTopics = interestListEvent + ? interestListEvent.tags + .filter(tag => tag[0] === 't' && tag[1]) + .map(tag => normalizeTopic(tag[1])) + : [] + + if (currentTopics.includes(normalizedTopic)) { + // Already subscribed + return + } + + const newTopics = [...currentTopics, normalizedTopic] + const newInterestListEvent = await publishNewInterestListEvent(newTopics) + await updateInterestListEvent(newInterestListEvent) + + toast.success(t('Subscribed to topic')) + } catch (error) { + console.error('Failed to subscribe to topic:', error) + toast.error(t('Failed to subscribe to topic') + ': ' + (error as Error).message) + } finally { + setChanging(false) + } + } + + const unsubscribe = async (topic: string) => { + if (!accountPubkey || changing) return + + const normalizedTopic = normalizeTopic(topic) + if (!subscribedTopics.has(normalizedTopic)) { + return + } + + setChanging(true) + try { + const interestListEvent = await client.fetchInterestListEvent(accountPubkey) + if (!interestListEvent) return + + const currentTopics = interestListEvent.tags + .filter(tag => tag[0] === 't' && tag[1]) + .map(tag => normalizeTopic(tag[1])) + + const newTopics = currentTopics.filter(t => t !== normalizedTopic) + + if (newTopics.length === currentTopics.length) { + // Topic wasn't in the list + return + } + + const newInterestListEvent = await publishNewInterestListEvent(newTopics) + await updateInterestListEvent(newInterestListEvent) + + toast.success(t('Unsubscribed from topic')) + } catch (error) { + console.error('Failed to unsubscribe from topic:', error) + toast.error(t('Failed to unsubscribe from topic') + ': ' + (error as Error).message) + } finally { + setChanging(false) + } + } + + return ( + + {children} + + ) +} + diff --git a/src/providers/NostrProvider/index.tsx b/src/providers/NostrProvider/index.tsx index 97efcfc..0fdad10 100644 --- a/src/providers/NostrProvider/index.tsx +++ b/src/providers/NostrProvider/index.tsx @@ -53,6 +53,7 @@ type TNostrContext = { followListEvent: Event | null muteListEvent: Event | null bookmarkListEvent: Event | null + interestListEvent: Event | null favoriteRelaysEvent: Event | null userEmojiListEvent: Event | null notificationsSeenAt: number @@ -84,6 +85,7 @@ type TNostrContext = { updateFollowListEvent: (followListEvent: Event) => Promise updateMuteListEvent: (muteListEvent: Event, privateTags: string[][]) => Promise updateBookmarkListEvent: (bookmarkListEvent: Event) => Promise + updateInterestListEvent: (interestListEvent: Event) => Promise updateFavoriteRelaysEvent: (favoriteRelaysEvent: Event) => Promise updateNotificationsSeenAt: (skipPublish?: boolean) => Promise } @@ -117,6 +119,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { const [followListEvent, setFollowListEvent] = useState(null) const [muteListEvent, setMuteListEvent] = useState(null) const [bookmarkListEvent, setBookmarkListEvent] = useState(null) + const [interestListEvent, setInterestListEvent] = useState(null) const [favoriteRelaysEvent, setFavoriteRelaysEvent] = useState(null) const [userEmojiListEvent, setUserEmojiListEvent] = useState(null) const [notificationsSeenAt, setNotificationsSeenAt] = useState(-1) @@ -241,6 +244,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { kinds.Contacts, kinds.Mutelist, kinds.BookmarkList, + 10015, // Interest list ExtendedKind.FAVORITE_RELAYS, ExtendedKind.BLOSSOM_SERVER_LIST, kinds.UserEmojiList @@ -258,6 +262,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { const followListEvent = sortedEvents.find((e) => e.kind === kinds.Contacts) const muteListEvent = sortedEvents.find((e) => e.kind === kinds.Mutelist) const bookmarkListEvent = sortedEvents.find((e) => e.kind === kinds.BookmarkList) + const interestListEvent = sortedEvents.find((e) => e.kind === 10015) const favoriteRelaysEvent = sortedEvents.find((e) => e.kind === ExtendedKind.FAVORITE_RELAYS) const blossomServerListEvent = sortedEvents.find( (e) => e.kind === ExtendedKind.BLOSSOM_SERVER_LIST @@ -299,6 +304,12 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { setBookmarkListEvent(bookmarkListEvent) } } + if (interestListEvent) { + const updatedInterestListEvent = await indexedDb.putReplaceableEvent(interestListEvent) + if (updatedInterestListEvent.id === interestListEvent.id) { + setInterestListEvent(interestListEvent) + } + } if (favoriteRelaysEvent) { const updatedFavoriteRelaysEvent = await indexedDb.putReplaceableEvent(favoriteRelaysEvent) if (updatedFavoriteRelaysEvent.id === favoriteRelaysEvent.id) { @@ -746,6 +757,13 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { setBookmarkListEvent(newBookmarkListEvent) } + const updateInterestListEvent = async (interestListEvent: Event) => { + const newInterestListEvent = await indexedDb.putReplaceableEvent(interestListEvent) + if (newInterestListEvent.id !== interestListEvent.id) return + + setInterestListEvent(newInterestListEvent) + } + const updateFavoriteRelaysEvent = async (favoriteRelaysEvent: Event) => { const newFavoriteRelaysEvent = await indexedDb.putReplaceableEvent(favoriteRelaysEvent) if (newFavoriteRelaysEvent.id !== favoriteRelaysEvent.id) return @@ -786,6 +804,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { followListEvent, muteListEvent, bookmarkListEvent, + interestListEvent, favoriteRelaysEvent, userEmojiListEvent, notificationsSeenAt, @@ -814,6 +833,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { updateFollowListEvent, updateMuteListEvent, updateBookmarkListEvent, + updateInterestListEvent, updateFavoriteRelaysEvent, updateNotificationsSeenAt }} diff --git a/src/providers/NotificationProvider.tsx b/src/providers/NotificationProvider.tsx index f452970..a4b8039 100644 --- a/src/providers/NotificationProvider.tsx +++ b/src/providers/NotificationProvider.tsx @@ -11,6 +11,7 @@ import { useContentPolicy } from './ContentPolicyProvider' import { useMuteList } from './MuteListProvider' import { useNostr } from './NostrProvider' import { useUserTrust } from './UserTrustProvider' +import { useInterestList } from './InterestListProvider' type TNotificationContext = { hasNewNotification: boolean @@ -36,6 +37,7 @@ export function NotificationProvider({ children }: { children: React.ReactNode } const { hideUntrustedNotifications, isUserTrusted } = useUserTrust() const { mutePubkeySet } = useMuteList() const { hideContentMentioningMutedUsers } = useContentPolicy() + const { getSubscribedTopics } = useInterestList() const [newNotifications, setNewNotifications] = useState([]) const [readNotificationIdSet, setReadNotificationIdSet] = useState>(new Set()) const filteredNewNotifications = useMemo(() => { @@ -87,18 +89,67 @@ export function NotificationProvider({ children }: { children: React.ReactNode } const subCloserRef: { current: SubCloser | null } = { current: null } + const topicSubCloserRef: { + current: SubCloser | null + } = { current: null } const subscribe = async () => { if (subCloserRef.current) { subCloserRef.current.close() subCloserRef.current = null } + if (topicSubCloserRef.current) { + topicSubCloserRef.current.close() + topicSubCloserRef.current = null + } if (!isMountedRef.current) return null try { let eosed = false const relayList = await client.fetchRelayList(pubkey) const notificationRelays = relayList.read.length > 0 ? relayList.read.slice(0, 5) : BIG_RELAY_URLS + + // Subscribe to subscribed topics (kind 11 discussions) + const subscribedTopics = getSubscribedTopics() + if (subscribedTopics.length > 0) { + let topicEosed = false + const topicSubCloser = client.subscribe( + notificationRelays, + [ + { + kinds: [11], // Discussion threads + '#t': subscribedTopics, + limit: 10 + } + ], + { + oneose: (e) => { + if (e) { + topicEosed = e + } + }, + onevent: (evt) => { + // Don't notify about our own threads + if (evt.pubkey !== pubkey) { + setNewNotifications((prev) => { + if (!topicEosed) { + return [evt, ...prev] + } + if (prev.length && compareEvents(prev[0], evt) >= 0) { + return prev + } + + client.emitNewEvent(evt) + return [evt, ...prev] + }) + } + } + } + ) + topicSubCloserRef.current = topicSubCloser + } + + // Regular notifications subscription const subCloser = client.subscribe( notificationRelays, [ @@ -187,8 +238,12 @@ export function NotificationProvider({ children }: { children: React.ReactNode } subCloserRef.current.close() subCloserRef.current = null } + if (topicSubCloserRef.current) { + topicSubCloserRef.current.close() + topicSubCloserRef.current = null + } } - }, [pubkey]) + }, [pubkey, getSubscribedTopics]) useEffect(() => { const newNotificationCount = filteredNewNotifications.length diff --git a/src/services/client.service.ts b/src/services/client.service.ts index a562c65..cb720b3 100644 --- a/src/services/client.service.ts +++ b/src/services/client.service.ts @@ -1685,6 +1685,10 @@ class ClientService extends EventTarget { return this.fetchReplaceableEvent(pubkey, kinds.BookmarkList) } + async fetchInterestListEvent(pubkey: string) { + return this.fetchReplaceableEvent(pubkey, 10015) + } + async fetchBlossomServerListEvent(pubkey: string) { return await this.fetchReplaceableEvent(pubkey, ExtendedKind.BLOSSOM_SERVER_LIST) } diff --git a/src/services/indexed-db.service.ts b/src/services/indexed-db.service.ts index 3bf4481..adb961f 100644 --- a/src/services/indexed-db.service.ts +++ b/src/services/indexed-db.service.ts @@ -16,6 +16,7 @@ const StoreNames = { MUTE_LIST_EVENTS: 'muteListEvents', BOOKMARK_LIST_EVENTS: 'bookmarkListEvents', BLOSSOM_SERVER_LIST_EVENTS: 'blossomServerListEvents', + INTEREST_LIST_EVENTS: 'interestListEvents', MUTE_DECRYPTED_TAGS: 'muteDecryptedTags', USER_EMOJI_LIST_EVENTS: 'userEmojiListEvents', EMOJI_SET_EVENTS: 'emojiSetEvents', @@ -70,6 +71,9 @@ class IndexedDbService { if (!db.objectStoreNames.contains(StoreNames.BOOKMARK_LIST_EVENTS)) { db.createObjectStore(StoreNames.BOOKMARK_LIST_EVENTS, { keyPath: 'key' }) } + if (!db.objectStoreNames.contains(StoreNames.INTEREST_LIST_EVENTS)) { + db.createObjectStore(StoreNames.INTEREST_LIST_EVENTS, { keyPath: 'key' }) + } if (!db.objectStoreNames.contains(StoreNames.MUTE_DECRYPTED_TAGS)) { db.createObjectStore(StoreNames.MUTE_DECRYPTED_TAGS, { keyPath: 'key' }) } @@ -447,6 +451,10 @@ class IndexedDbService { return StoreNames.FOLLOW_LIST_EVENTS case kinds.Mutelist: return StoreNames.MUTE_LIST_EVENTS + case kinds.BookmarkList: + return StoreNames.BOOKMARK_LIST_EVENTS + case 10015: // Interest list + return StoreNames.INTEREST_LIST_EVENTS case ExtendedKind.BLOSSOM_SERVER_LIST: return StoreNames.BLOSSOM_SERVER_LIST_EVENTS case kinds.Relaysets: