feat: pinned users event
This commit is contained in:
parent
ad016aba35
commit
7ec4835c61
10 changed files with 303 additions and 136 deletions
|
|
@ -1397,6 +1397,10 @@ class ClientService extends EventTarget {
|
|||
return this.fetchReplaceableEvent(pubkey, kinds.UserEmojiList)
|
||||
}
|
||||
|
||||
async fetchPinnedUsersList(pubkey: string) {
|
||||
return this.fetchReplaceableEvent(pubkey, ExtendedKind.PINNED_USERS)
|
||||
}
|
||||
|
||||
async updateBlossomServerListEventCache(evt: NEvent) {
|
||||
await this.updateReplaceableEventCache(evt)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ const StoreNames = {
|
|||
MUTE_LIST_EVENTS: 'muteListEvents',
|
||||
BOOKMARK_LIST_EVENTS: 'bookmarkListEvents',
|
||||
BLOSSOM_SERVER_LIST_EVENTS: 'blossomServerListEvents',
|
||||
MUTE_DECRYPTED_TAGS: 'muteDecryptedTags',
|
||||
USER_EMOJI_LIST_EVENTS: 'userEmojiListEvents',
|
||||
EMOJI_SET_EVENTS: 'emojiSetEvents',
|
||||
PIN_LIST_EVENTS: 'pinListEvents',
|
||||
|
|
@ -24,6 +23,9 @@ const StoreNames = {
|
|||
RELAY_SETS: 'relaySets',
|
||||
FOLLOWING_FAVORITE_RELAYS: 'followingFavoriteRelays',
|
||||
RELAY_INFOS: 'relayInfos',
|
||||
DECRYPTED_CONTENTS: 'decryptedContents',
|
||||
PINNED_USERS_EVENTS: 'pinnedUsersEvents',
|
||||
MUTE_DECRYPTED_TAGS: 'muteDecryptedTags', // deprecated
|
||||
RELAY_INFO_EVENTS: 'relayInfoEvents' // deprecated
|
||||
}
|
||||
|
||||
|
|
@ -43,7 +45,7 @@ class IndexedDbService {
|
|||
init(): Promise<void> {
|
||||
if (!this.initPromise) {
|
||||
this.initPromise = new Promise((resolve, reject) => {
|
||||
const request = window.indexedDB.open('jumble', 9)
|
||||
const request = window.indexedDB.open('jumble', 10)
|
||||
|
||||
request.onerror = (event) => {
|
||||
reject(event)
|
||||
|
|
@ -71,8 +73,8 @@ class IndexedDbService {
|
|||
if (!db.objectStoreNames.contains(StoreNames.BOOKMARK_LIST_EVENTS)) {
|
||||
db.createObjectStore(StoreNames.BOOKMARK_LIST_EVENTS, { keyPath: 'key' })
|
||||
}
|
||||
if (!db.objectStoreNames.contains(StoreNames.MUTE_DECRYPTED_TAGS)) {
|
||||
db.createObjectStore(StoreNames.MUTE_DECRYPTED_TAGS, { keyPath: 'key' })
|
||||
if (!db.objectStoreNames.contains(StoreNames.DECRYPTED_CONTENTS)) {
|
||||
db.createObjectStore(StoreNames.DECRYPTED_CONTENTS, { keyPath: 'key' })
|
||||
}
|
||||
if (!db.objectStoreNames.contains(StoreNames.FAVORITE_RELAYS)) {
|
||||
db.createObjectStore(StoreNames.FAVORITE_RELAYS, { keyPath: 'key' })
|
||||
|
|
@ -98,9 +100,16 @@ class IndexedDbService {
|
|||
if (!db.objectStoreNames.contains(StoreNames.PIN_LIST_EVENTS)) {
|
||||
db.createObjectStore(StoreNames.PIN_LIST_EVENTS, { keyPath: 'key' })
|
||||
}
|
||||
if (!db.objectStoreNames.contains(StoreNames.PINNED_USERS_EVENTS)) {
|
||||
db.createObjectStore(StoreNames.PINNED_USERS_EVENTS, { keyPath: 'key' })
|
||||
}
|
||||
|
||||
if (db.objectStoreNames.contains(StoreNames.RELAY_INFO_EVENTS)) {
|
||||
db.deleteObjectStore(StoreNames.RELAY_INFO_EVENTS)
|
||||
}
|
||||
if (db.objectStoreNames.contains(StoreNames.MUTE_DECRYPTED_TAGS)) {
|
||||
db.deleteObjectStore(StoreNames.MUTE_DECRYPTED_TAGS)
|
||||
}
|
||||
this.db = db
|
||||
}
|
||||
})
|
||||
|
|
@ -268,19 +277,19 @@ class IndexedDbService {
|
|||
})
|
||||
}
|
||||
|
||||
async getMuteDecryptedTags(id: string): Promise<string[][] | null> {
|
||||
async getDecryptedContent(key: string): Promise<string | null> {
|
||||
await this.initPromise
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.db) {
|
||||
return reject('database not initialized')
|
||||
}
|
||||
const transaction = this.db.transaction(StoreNames.MUTE_DECRYPTED_TAGS, 'readonly')
|
||||
const store = transaction.objectStore(StoreNames.MUTE_DECRYPTED_TAGS)
|
||||
const request = store.get(id)
|
||||
const transaction = this.db.transaction(StoreNames.DECRYPTED_CONTENTS, 'readonly')
|
||||
const store = transaction.objectStore(StoreNames.DECRYPTED_CONTENTS)
|
||||
const request = store.get(key)
|
||||
|
||||
request.onsuccess = () => {
|
||||
transaction.commit()
|
||||
resolve((request.result as TValue<string[][]>)?.value)
|
||||
resolve((request.result as TValue<string>)?.value)
|
||||
}
|
||||
|
||||
request.onerror = (event) => {
|
||||
|
|
@ -290,16 +299,16 @@ class IndexedDbService {
|
|||
})
|
||||
}
|
||||
|
||||
async putMuteDecryptedTags(id: string, tags: string[][]): Promise<void> {
|
||||
async putDecryptedContent(key: string, content: string): Promise<void> {
|
||||
await this.initPromise
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.db) {
|
||||
return reject('database not initialized')
|
||||
}
|
||||
const transaction = this.db.transaction(StoreNames.MUTE_DECRYPTED_TAGS, 'readwrite')
|
||||
const store = transaction.objectStore(StoreNames.MUTE_DECRYPTED_TAGS)
|
||||
const transaction = this.db.transaction(StoreNames.DECRYPTED_CONTENTS, 'readwrite')
|
||||
const store = transaction.objectStore(StoreNames.DECRYPTED_CONTENTS)
|
||||
|
||||
const putRequest = store.put(this.formatValue(id, tags))
|
||||
const putRequest = store.put(this.formatValue(key, content))
|
||||
putRequest.onsuccess = () => {
|
||||
transaction.commit()
|
||||
resolve()
|
||||
|
|
@ -471,6 +480,8 @@ class IndexedDbService {
|
|||
return StoreNames.EMOJI_SET_EVENTS
|
||||
case kinds.Pinlist:
|
||||
return StoreNames.PIN_LIST_EVENTS
|
||||
case ExtendedKind.PINNED_USERS:
|
||||
return StoreNames.PINNED_USERS_EVENTS
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ class LocalStorageService {
|
|||
private enableSingleColumnLayout: boolean = true
|
||||
private faviconUrlTemplate: string = DEFAULT_FAVICON_URL_TEMPLATE
|
||||
private filterOutOnionRelays: boolean = !isTorBrowser()
|
||||
private pinnedPubkeys: Set<string> = new Set()
|
||||
|
||||
constructor() {
|
||||
if (!LocalStorageService.instance) {
|
||||
|
|
@ -231,12 +230,8 @@ class LocalStorageService {
|
|||
this.filterOutOnionRelays = filterOutOnionRelaysStr !== 'false'
|
||||
}
|
||||
|
||||
const pinnedPubkeysStr = window.localStorage.getItem(StorageKey.PINNED_PUBKEYS)
|
||||
if (pinnedPubkeysStr) {
|
||||
this.pinnedPubkeys = new Set(JSON.parse(pinnedPubkeysStr))
|
||||
}
|
||||
|
||||
// Clean up deprecated data
|
||||
window.localStorage.removeItem(StorageKey.PINNED_PUBKEYS)
|
||||
window.localStorage.removeItem(StorageKey.ACCOUNT_PROFILE_EVENT_MAP)
|
||||
window.localStorage.removeItem(StorageKey.ACCOUNT_FOLLOW_LIST_EVENT_MAP)
|
||||
window.localStorage.removeItem(StorageKey.ACCOUNT_RELAY_LIST_EVENT_MAP)
|
||||
|
|
@ -564,18 +559,6 @@ class LocalStorageService {
|
|||
this.filterOutOnionRelays = filterOut
|
||||
window.localStorage.setItem(StorageKey.FILTER_OUT_ONION_RELAYS, filterOut.toString())
|
||||
}
|
||||
|
||||
getPinnedPubkeys(): Set<string> {
|
||||
return this.pinnedPubkeys
|
||||
}
|
||||
|
||||
setPinnedPubkeys(pinnedPubkeys: Set<string>) {
|
||||
this.pinnedPubkeys = pinnedPubkeys
|
||||
window.localStorage.setItem(
|
||||
StorageKey.PINNED_PUBKEYS,
|
||||
JSON.stringify(Array.from(this.pinnedPubkeys))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const instance = new LocalStorageService()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { getEventKey } from '@/lib/event'
|
||||
import storage from '@/services/local-storage.service'
|
||||
import { TFeedSubRequest } from '@/types'
|
||||
import dayjs from 'dayjs'
|
||||
import { Event } from 'nostr-tools'
|
||||
|
|
@ -14,7 +13,6 @@ export type TUserAggregation = {
|
|||
class UserAggregationService {
|
||||
static instance: UserAggregationService
|
||||
|
||||
private pinnedPubkeys: Set<string> = new Set()
|
||||
private aggregationStore: Map<string, Map<string, Event[]>> = new Map()
|
||||
private listenersMap: Map<string, Set<() => void>> = new Map()
|
||||
private lastViewedMap: Map<string, number> = new Map()
|
||||
|
|
@ -24,7 +22,6 @@ class UserAggregationService {
|
|||
return UserAggregationService.instance
|
||||
}
|
||||
UserAggregationService.instance = this
|
||||
this.pinnedPubkeys = storage.getPinnedPubkeys()
|
||||
}
|
||||
|
||||
subscribeAggregationChange(feedId: string, pubkey: string, listener: () => void) {
|
||||
|
|
@ -64,33 +61,6 @@ class UserAggregationService {
|
|||
}
|
||||
}
|
||||
|
||||
// Pinned users management
|
||||
getPinnedPubkeys(): string[] {
|
||||
return [...this.pinnedPubkeys]
|
||||
}
|
||||
|
||||
isPinned(pubkey: string): boolean {
|
||||
return this.pinnedPubkeys.has(pubkey)
|
||||
}
|
||||
|
||||
pinUser(pubkey: string) {
|
||||
this.pinnedPubkeys.add(pubkey)
|
||||
storage.setPinnedPubkeys(this.pinnedPubkeys)
|
||||
}
|
||||
|
||||
unpinUser(pubkey: string) {
|
||||
this.pinnedPubkeys.delete(pubkey)
|
||||
storage.setPinnedPubkeys(this.pinnedPubkeys)
|
||||
}
|
||||
|
||||
togglePin(pubkey: string) {
|
||||
if (this.isPinned(pubkey)) {
|
||||
this.unpinUser(pubkey)
|
||||
} else {
|
||||
this.pinUser(pubkey)
|
||||
}
|
||||
}
|
||||
|
||||
// Aggregate events by user
|
||||
aggregateByUser(events: Event[]): TUserAggregation[] {
|
||||
const userEventsMap = new Map<string, Event[]>()
|
||||
|
|
@ -125,21 +95,6 @@ class UserAggregationService {
|
|||
})
|
||||
}
|
||||
|
||||
sortWithPinned(aggregations: TUserAggregation[]): TUserAggregation[] {
|
||||
const pinned: TUserAggregation[] = []
|
||||
const unpinned: TUserAggregation[] = []
|
||||
|
||||
aggregations.forEach((agg) => {
|
||||
if (this.isPinned(agg.pubkey)) {
|
||||
pinned.push(agg)
|
||||
} else {
|
||||
unpinned.push(agg)
|
||||
}
|
||||
})
|
||||
|
||||
return [...pinned, ...unpinned]
|
||||
}
|
||||
|
||||
saveAggregations(feedId: string, aggregations: TUserAggregation[]) {
|
||||
const map = new Map<string, Event[]>()
|
||||
aggregations.forEach((agg) => map.set(agg.pubkey, agg.events))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue