feat: favorite relays (#250)
This commit is contained in:
parent
fab9ff88b5
commit
c739d9d28c
63 changed files with 1081 additions and 982 deletions
|
|
@ -90,10 +90,6 @@ class ClientService extends EventTarget {
|
|||
await indexedDb.iterateProfileEvents((profileEvent) => this.addUsernameToIndex(profileEvent))
|
||||
}
|
||||
|
||||
listConnectionStatus() {
|
||||
return this.pool.listConnectionStatus()
|
||||
}
|
||||
|
||||
setCurrentRelayUrls(urls: string[]) {
|
||||
this.currentRelayUrls = urls
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { ExtendedKind } from '@/constants'
|
||||
import { tagNameEquals } from '@/lib/tag'
|
||||
import { Event, kinds } from 'nostr-tools'
|
||||
|
||||
|
|
@ -13,7 +14,9 @@ const StoreNames = {
|
|||
FOLLOW_LIST_EVENTS: 'followListEvents',
|
||||
MUTE_LIST_EVENTS: 'muteListEvents',
|
||||
MUTE_DECRYPTED_TAGS: 'muteDecryptedTags',
|
||||
RELAY_INFO_EVENTS: 'relayInfoEvents'
|
||||
RELAY_INFO_EVENTS: 'relayInfoEvents',
|
||||
FAVORITE_RELAYS: 'favoriteRelays',
|
||||
RELAY_SETS: 'relaySets'
|
||||
}
|
||||
|
||||
class IndexedDbService {
|
||||
|
|
@ -32,7 +35,7 @@ class IndexedDbService {
|
|||
init(): Promise<void> {
|
||||
if (!this.initPromise) {
|
||||
this.initPromise = new Promise((resolve, reject) => {
|
||||
const request = window.indexedDB.open('jumble', 2)
|
||||
const request = window.indexedDB.open('jumble', 3)
|
||||
|
||||
request.onerror = (event) => {
|
||||
reject(event)
|
||||
|
|
@ -63,6 +66,12 @@ class IndexedDbService {
|
|||
if (!db.objectStoreNames.contains(StoreNames.RELAY_INFO_EVENTS)) {
|
||||
db.createObjectStore(StoreNames.RELAY_INFO_EVENTS, { keyPath: 'key' })
|
||||
}
|
||||
if (!db.objectStoreNames.contains(StoreNames.FAVORITE_RELAYS)) {
|
||||
db.createObjectStore(StoreNames.FAVORITE_RELAYS, { keyPath: 'key' })
|
||||
}
|
||||
if (!db.objectStoreNames.contains(StoreNames.RELAY_SETS)) {
|
||||
db.createObjectStore(StoreNames.RELAY_SETS, { keyPath: 'key' })
|
||||
}
|
||||
this.db = db
|
||||
}
|
||||
})
|
||||
|
|
@ -84,14 +93,15 @@ class IndexedDbService {
|
|||
const transaction = this.db.transaction(storeName, 'readwrite')
|
||||
const store = transaction.objectStore(storeName)
|
||||
|
||||
const getRequest = store.get(event.pubkey)
|
||||
const key = this.getReplaceableEventKey(event)
|
||||
const getRequest = store.get(key)
|
||||
getRequest.onsuccess = () => {
|
||||
const oldValue = getRequest.result as TValue<Event> | undefined
|
||||
if (oldValue && oldValue.value.created_at >= event.created_at) {
|
||||
transaction.commit()
|
||||
return resolve(oldValue.value)
|
||||
}
|
||||
const putRequest = store.put(this.formatValue(event.pubkey, event))
|
||||
const putRequest = store.put(this.formatValue(key, event))
|
||||
putRequest.onsuccess = () => {
|
||||
transaction.commit()
|
||||
resolve(event)
|
||||
|
|
@ -110,7 +120,7 @@ class IndexedDbService {
|
|||
})
|
||||
}
|
||||
|
||||
async getReplaceableEvent(pubkey: string, kind: number): Promise<Event | undefined> {
|
||||
async getReplaceableEvent(pubkey: string, kind: number, d?: string): Promise<Event | undefined> {
|
||||
const storeName = this.getStoreNameByKind(kind)
|
||||
if (!storeName) {
|
||||
return Promise.reject('store name not found')
|
||||
|
|
@ -122,7 +132,8 @@ class IndexedDbService {
|
|||
}
|
||||
const transaction = this.db.transaction(storeName, 'readonly')
|
||||
const store = transaction.objectStore(storeName)
|
||||
const request = store.get(pubkey)
|
||||
const key = d === undefined ? pubkey : `${pubkey}:${d}`
|
||||
const request = store.get(key)
|
||||
|
||||
request.onsuccess = () => {
|
||||
transaction.commit()
|
||||
|
|
@ -298,6 +309,18 @@ class IndexedDbService {
|
|||
})
|
||||
}
|
||||
|
||||
private getReplaceableEventKey(event: Event): string {
|
||||
if (
|
||||
[kinds.Metadata, kinds.Contacts].includes(event.kind) ||
|
||||
(event.kind >= 10000 && event.kind < 20000)
|
||||
) {
|
||||
return event.pubkey
|
||||
}
|
||||
|
||||
const [, d] = event.tags.find(tagNameEquals('d')) ?? []
|
||||
return `${event.pubkey}:${d ?? ''}`
|
||||
}
|
||||
|
||||
private getStoreNameByKind(kind: number): string | undefined {
|
||||
switch (kind) {
|
||||
case kinds.Metadata:
|
||||
|
|
@ -308,6 +331,10 @@ class IndexedDbService {
|
|||
return StoreNames.FOLLOW_LIST_EVENTS
|
||||
case kinds.Mutelist:
|
||||
return StoreNames.MUTE_LIST_EVENTS
|
||||
case kinds.Relaysets:
|
||||
return StoreNames.RELAY_SETS
|
||||
case ExtendedKind.FAVORITE_RELAYS:
|
||||
return StoreNames.FAVORITE_RELAYS
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,41 +4,16 @@ import { randomString } from '@/lib/random'
|
|||
import {
|
||||
TAccount,
|
||||
TAccountPointer,
|
||||
TFeedType,
|
||||
TFeedInfo,
|
||||
TNoteListMode,
|
||||
TRelaySet,
|
||||
TThemeSetting
|
||||
} from '@/types'
|
||||
|
||||
const DEFAULT_RELAY_SETS: TRelaySet[] = [
|
||||
{
|
||||
id: randomString(),
|
||||
name: 'Safer Global',
|
||||
relayUrls: ['wss://nostr.wine/', 'wss://pyramid.fiatjaf.com/']
|
||||
},
|
||||
{
|
||||
id: randomString(),
|
||||
name: 'Short Notes',
|
||||
relayUrls: ['wss://140.f7z.io/']
|
||||
},
|
||||
{
|
||||
id: randomString(),
|
||||
name: 'News',
|
||||
relayUrls: ['wss://news.utxo.one/']
|
||||
},
|
||||
{
|
||||
id: randomString(),
|
||||
name: 'Algo',
|
||||
relayUrls: ['wss://algo.utxo.one']
|
||||
}
|
||||
]
|
||||
|
||||
class LocalStorageService {
|
||||
static instance: LocalStorageService
|
||||
|
||||
private relaySets: TRelaySet[] = []
|
||||
private activeRelaySetId: string | null = null
|
||||
private feedType: TFeedType = 'relays'
|
||||
private themeSetting: TThemeSetting = 'system'
|
||||
private accounts: TAccount[] = []
|
||||
private currentAccount: TAccount | null = null
|
||||
|
|
@ -47,6 +22,7 @@ class LocalStorageService {
|
|||
private defaultZapSats: number = 21
|
||||
private defaultZapComment: string = 'Zap!'
|
||||
private quickZap: boolean = false
|
||||
private accountFeedInfoMap: Record<string, TFeedInfo | undefined> = {}
|
||||
|
||||
constructor() {
|
||||
if (!LocalStorageService.instance) {
|
||||
|
|
@ -63,12 +39,6 @@ class LocalStorageService {
|
|||
this.accounts = accountsStr ? JSON.parse(accountsStr) : []
|
||||
const currentAccountStr = window.localStorage.getItem(StorageKey.CURRENT_ACCOUNT)
|
||||
this.currentAccount = currentAccountStr ? JSON.parse(currentAccountStr) : null
|
||||
const feedType = window.localStorage.getItem(StorageKey.FEED_TYPE)
|
||||
if (feedType && ['following', 'relays'].includes(feedType)) {
|
||||
this.feedType = feedType as 'following' | 'relays'
|
||||
} else {
|
||||
this.feedType = 'relays'
|
||||
}
|
||||
const noteListModeStr = window.localStorage.getItem(StorageKey.NOTE_LIST_MODE)
|
||||
this.noteListMode =
|
||||
noteListModeStr && ['posts', 'postsAndReplies', 'pictures'].includes(noteListModeStr)
|
||||
|
|
@ -93,16 +63,12 @@ class LocalStorageService {
|
|||
})
|
||||
}
|
||||
if (!relaySets.length) {
|
||||
relaySets = DEFAULT_RELAY_SETS
|
||||
relaySets = []
|
||||
}
|
||||
const activeRelaySetId = relaySets[0].id
|
||||
window.localStorage.setItem(StorageKey.RELAY_SETS, JSON.stringify(relaySets))
|
||||
window.localStorage.setItem(StorageKey.ACTIVE_RELAY_SET_ID, activeRelaySetId)
|
||||
this.relaySets = relaySets
|
||||
this.activeRelaySetId = activeRelaySetId
|
||||
} else {
|
||||
this.relaySets = JSON.parse(relaySetsStr)
|
||||
this.activeRelaySetId = window.localStorage.getItem(StorageKey.ACTIVE_RELAY_SET_ID) ?? null
|
||||
}
|
||||
|
||||
const defaultZapSatsStr = window.localStorage.getItem(StorageKey.DEFAULT_ZAP_SATS)
|
||||
|
|
@ -115,12 +81,18 @@ class LocalStorageService {
|
|||
this.defaultZapComment = window.localStorage.getItem(StorageKey.DEFAULT_ZAP_COMMENT) ?? 'Zap!'
|
||||
this.quickZap = window.localStorage.getItem(StorageKey.QUICK_ZAP) === 'true'
|
||||
|
||||
const accountFeedInfoMapStr =
|
||||
window.localStorage.getItem(StorageKey.ACCOUNT_FEED_INFO_MAP) ?? '{}'
|
||||
this.accountFeedInfoMap = JSON.parse(accountFeedInfoMapStr)
|
||||
|
||||
// Clean up deprecated data
|
||||
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)
|
||||
window.localStorage.removeItem(StorageKey.ACCOUNT_MUTE_LIST_EVENT_MAP)
|
||||
window.localStorage.removeItem(StorageKey.ACCOUNT_MUTE_DECRYPTED_TAGS_MAP)
|
||||
window.localStorage.removeItem(StorageKey.ACTIVE_RELAY_SET_ID)
|
||||
window.localStorage.removeItem(StorageKey.FEED_TYPE)
|
||||
}
|
||||
|
||||
getRelaySets() {
|
||||
|
|
@ -132,28 +104,6 @@ class LocalStorageService {
|
|||
window.localStorage.setItem(StorageKey.RELAY_SETS, JSON.stringify(this.relaySets))
|
||||
}
|
||||
|
||||
getActiveRelaySetId() {
|
||||
return this.activeRelaySetId
|
||||
}
|
||||
|
||||
setActiveRelaySetId(id: string | null) {
|
||||
this.activeRelaySetId = id
|
||||
if (id) {
|
||||
window.localStorage.setItem(StorageKey.ACTIVE_RELAY_SET_ID, id)
|
||||
} else {
|
||||
window.localStorage.removeItem(StorageKey.ACTIVE_RELAY_SET_ID)
|
||||
}
|
||||
}
|
||||
|
||||
getFeedType() {
|
||||
return this.feedType
|
||||
}
|
||||
|
||||
setFeedType(feedType: TFeedType) {
|
||||
this.feedType = feedType
|
||||
window.localStorage.setItem(StorageKey.FEED_TYPE, this.feedType)
|
||||
}
|
||||
|
||||
getThemeSetting() {
|
||||
return this.themeSetting
|
||||
}
|
||||
|
|
@ -260,6 +210,18 @@ class LocalStorageService {
|
|||
JSON.stringify(this.lastReadNotificationTimeMap)
|
||||
)
|
||||
}
|
||||
|
||||
getFeedInfo(pubkey: string) {
|
||||
return this.accountFeedInfoMap[pubkey]
|
||||
}
|
||||
|
||||
setFeedInfo(info: TFeedInfo, pubkey?: string | null) {
|
||||
this.accountFeedInfoMap[pubkey ?? 'default'] = info
|
||||
window.localStorage.setItem(
|
||||
StorageKey.ACCOUNT_FEED_INFO_MAP,
|
||||
JSON.stringify(this.accountFeedInfoMap)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const instance = new LocalStorageService()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue