feat: 💨

This commit is contained in:
codytseng
2025-01-19 22:17:49 +08:00
parent 9c0fa6257a
commit df4d5e52ae
4 changed files with 64 additions and 26 deletions

View File

@@ -8,6 +8,7 @@ export const StorageKey = {
ACCOUNT_RELAY_LIST_EVENT_MAP: 'accountRelayListEventMap', ACCOUNT_RELAY_LIST_EVENT_MAP: 'accountRelayListEventMap',
ACCOUNT_FOLLOW_LIST_EVENT_MAP: 'accountFollowListEventMap', ACCOUNT_FOLLOW_LIST_EVENT_MAP: 'accountFollowListEventMap',
ACCOUNT_MUTE_LIST_EVENT_MAP: 'accountMuteListEventMap', ACCOUNT_MUTE_LIST_EVENT_MAP: 'accountMuteListEventMap',
ACCOUNT_MUTE_DECRYPTED_TAGS_MAP: 'accountMuteDecryptedTagsMap',
ACCOUNT_PROFILE_EVENT_MAP: 'accountProfileEventMap', ACCOUNT_PROFILE_EVENT_MAP: 'accountProfileEventMap',
ADD_CLIENT_TAG: 'addClientTag' ADD_CLIENT_TAG: 'addClientTag'
} }

View File

@@ -115,7 +115,7 @@ export default function SettingsPage({ index }: { index?: number }) {
} }
const SettingItem = forwardRef<HTMLDivElement, HTMLProps<HTMLDivElement>>( const SettingItem = forwardRef<HTMLDivElement, HTMLProps<HTMLDivElement>>(
({ children, className, ...props }) => { ({ children, className, ...props }, ref) => {
return ( return (
<div <div
className={cn( className={cn(
@@ -123,6 +123,7 @@ const SettingItem = forwardRef<HTMLDivElement, HTMLProps<HTMLDivElement>>(
className className
)} )}
{...props} {...props}
ref={ref}
> >
{children} {children}
</div> </div>

View File

@@ -30,28 +30,6 @@ export function MuteListProvider({ children }: { children: React.ReactNode }) {
const [tags, setTags] = useState<string[][]>([]) const [tags, setTags] = useState<string[][]>([])
const mutePubkeys = useMemo(() => extractPubkeysFromEventTags(tags), [tags]) const mutePubkeys = useMemo(() => extractPubkeysFromEventTags(tags), [tags])
useEffect(() => {
if (!muteListEvent) {
setTags([])
return
}
const updateTags = async () => {
const tags = muteListEvent.tags
if (muteListEvent.content && accountPubkey) {
try {
const plainText = await nip04Decrypt(accountPubkey, muteListEvent.content)
const contentTags = z.array(z.array(z.string())).parse(JSON.parse(plainText))
tags.push(...contentTags.filter((tag) => tags.every((t) => !isSameTag(t, tag))))
} catch {
// ignore
}
}
setTags(tags)
}
updateTags()
}, [muteListEvent])
useEffect(() => { useEffect(() => {
if (!accountPubkey) return if (!accountPubkey) return
@@ -60,6 +38,8 @@ export function MuteListProvider({ children }: { children: React.ReactNode }) {
const storedMuteListEvent = storage.getAccountMuteListEvent(accountPubkey) const storedMuteListEvent = storage.getAccountMuteListEvent(accountPubkey)
if (storedMuteListEvent) { if (storedMuteListEvent) {
setMuteListEvent(storedMuteListEvent) setMuteListEvent(storedMuteListEvent)
const tags = await extractMuteTags(storedMuteListEvent)
setTags(tags)
} }
const events = await client.fetchEvents(relayList?.write ?? client.getDefaultRelayUrls(), { const events = await client.fetchEvents(relayList?.write ?? client.getDefaultRelayUrls(), {
kinds: [kinds.Mutelist], kinds: [kinds.Mutelist],
@@ -68,16 +48,41 @@ export function MuteListProvider({ children }: { children: React.ReactNode }) {
const muteEvent = getLatestEvent(events) as Event | undefined const muteEvent = getLatestEvent(events) as Event | undefined
if (muteEvent) { if (muteEvent) {
setMuteListEvent(muteEvent) setMuteListEvent(muteEvent)
const tags = await extractMuteTags(muteEvent)
setTags(tags)
} }
} }
init() init()
}, [accountPubkey]) }, [accountPubkey])
const updateMuteListEvent = (event: Event) => { const extractMuteTags = async (muteListEvent: Event) => {
const tags = [...muteListEvent.tags]
if (muteListEvent.content) {
const storedDecryptedTags = storage.getAccountMuteDecryptedTags(muteListEvent)
if (storedDecryptedTags) {
tags.push(...storedDecryptedTags)
} else {
try {
const plainText = await nip04Decrypt(muteListEvent.pubkey, muteListEvent.content)
const contentTags = z.array(z.array(z.string())).parse(JSON.parse(plainText))
storage.setAccountMuteDecryptedTags(muteListEvent, contentTags)
tags.push(...contentTags.filter((tag) => tags.every((t) => !isSameTag(t, tag))))
} catch (error) {
console.error('Failed to decrypt mute list content', error)
}
}
}
return tags
}
const update = (event: Event, tags: string[][]) => {
const isNew = storage.setAccountMuteListEvent(event) const isNew = storage.setAccountMuteListEvent(event)
if (!isNew) return if (!isNew) return
storage.setAccountMuteDecryptedTags(event, tags)
setMuteListEvent(event) setMuteListEvent(event)
setTags(tags)
} }
const mutePubkey = async (pubkey: string) => { const mutePubkey = async (pubkey: string) => {
@@ -87,7 +92,7 @@ export function MuteListProvider({ children }: { children: React.ReactNode }) {
const cipherText = await nip04Encrypt(accountPubkey, JSON.stringify(newTags)) const cipherText = await nip04Encrypt(accountPubkey, JSON.stringify(newTags))
const newMuteListDraftEvent = createMuteListDraftEvent(muteListEvent?.tags ?? [], cipherText) const newMuteListDraftEvent = createMuteListDraftEvent(muteListEvent?.tags ?? [], cipherText)
const newMuteListEvent = await publish(newMuteListDraftEvent) const newMuteListEvent = await publish(newMuteListDraftEvent)
updateMuteListEvent(newMuteListEvent) update(newMuteListEvent, newTags)
} }
const unmutePubkey = async (pubkey: string) => { const unmutePubkey = async (pubkey: string) => {
@@ -100,7 +105,7 @@ export function MuteListProvider({ children }: { children: React.ReactNode }) {
cipherText cipherText
) )
const newMuteListEvent = await publish(newMuteListDraftEvent) const newMuteListEvent = await publish(newMuteListDraftEvent)
updateMuteListEvent(newMuteListEvent) update(newMuteListEvent, newTags)
} }
return ( return (

View File

@@ -29,6 +29,10 @@ class StorageService {
private accountRelayListEventMap: Record<string, Event | undefined> = {} // pubkey -> relayListEvent private accountRelayListEventMap: Record<string, Event | undefined> = {} // pubkey -> relayListEvent
private accountFollowListEventMap: Record<string, Event | undefined> = {} // pubkey -> followListEvent private accountFollowListEventMap: Record<string, Event | undefined> = {} // pubkey -> followListEvent
private accountMuteListEventMap: Record<string, Event | undefined> = {} // pubkey -> muteListEvent private accountMuteListEventMap: Record<string, Event | undefined> = {} // pubkey -> muteListEvent
private accountMuteDecryptedTagsMap: Record<
string,
{ id: string; tags: string[][] } | undefined
> = {} // pubkey -> { id, tags }
private accountProfileEventMap: Record<string, Event | undefined> = {} // pubkey -> profileEvent private accountProfileEventMap: Record<string, Event | undefined> = {} // pubkey -> profileEvent
constructor() { constructor() {
@@ -67,6 +71,12 @@ class StorageService {
this.accountMuteListEventMap = accountMuteListEventMapStr this.accountMuteListEventMap = accountMuteListEventMapStr
? JSON.parse(accountMuteListEventMapStr) ? JSON.parse(accountMuteListEventMapStr)
: {} : {}
const accountMuteDecryptedTagsMapStr = window.localStorage.getItem(
StorageKey.ACCOUNT_MUTE_DECRYPTED_TAGS_MAP
)
this.accountMuteDecryptedTagsMap = accountMuteDecryptedTagsMapStr
? JSON.parse(accountMuteDecryptedTagsMapStr)
: {}
const accountProfileEventMapStr = window.localStorage.getItem( const accountProfileEventMapStr = window.localStorage.getItem(
StorageKey.ACCOUNT_PROFILE_EVENT_MAP StorageKey.ACCOUNT_PROFILE_EVENT_MAP
) )
@@ -184,6 +194,7 @@ class StorageService {
delete this.accountFollowListEventMap[account.pubkey] delete this.accountFollowListEventMap[account.pubkey]
delete this.accountRelayListEventMap[account.pubkey] delete this.accountRelayListEventMap[account.pubkey]
delete this.accountMuteListEventMap[account.pubkey] delete this.accountMuteListEventMap[account.pubkey]
delete this.accountMuteDecryptedTagsMap[account.pubkey]
delete this.accountProfileEventMap[account.pubkey] delete this.accountProfileEventMap[account.pubkey]
window.localStorage.setItem(StorageKey.ACCOUNTS, JSON.stringify(this.accounts)) window.localStorage.setItem(StorageKey.ACCOUNTS, JSON.stringify(this.accounts))
window.localStorage.setItem( window.localStorage.setItem(
@@ -194,6 +205,10 @@ class StorageService {
StorageKey.ACCOUNT_MUTE_LIST_EVENT_MAP, StorageKey.ACCOUNT_MUTE_LIST_EVENT_MAP,
JSON.stringify(this.accountMuteListEventMap) JSON.stringify(this.accountMuteListEventMap)
) )
window.localStorage.setItem(
StorageKey.ACCOUNT_MUTE_DECRYPTED_TAGS_MAP,
JSON.stringify(this.accountMuteDecryptedTagsMap)
)
window.localStorage.setItem( window.localStorage.setItem(
StorageKey.ACCOUNT_RELAY_LIST_EVENT_MAP, StorageKey.ACCOUNT_RELAY_LIST_EVENT_MAP,
JSON.stringify(this.accountRelayListEventMap) JSON.stringify(this.accountRelayListEventMap)
@@ -276,6 +291,22 @@ class StorageService {
return true return true
} }
getAccountMuteDecryptedTags(muteListEvent: Event) {
const stored = this.accountMuteDecryptedTagsMap[muteListEvent.pubkey]
if (stored && stored.id === muteListEvent.id) {
return stored.tags
}
return null
}
setAccountMuteDecryptedTags(muteListEvent: Event, tags: string[][]) {
this.accountMuteDecryptedTagsMap[muteListEvent.pubkey] = { id: muteListEvent.id, tags }
window.localStorage.setItem(
StorageKey.ACCOUNT_MUTE_DECRYPTED_TAGS_MAP,
JSON.stringify(this.accountMuteDecryptedTagsMap)
)
}
getAccountProfileEvent(pubkey: string) { getAccountProfileEvent(pubkey: string) {
return this.accountProfileEventMap[pubkey] return this.accountProfileEventMap[pubkey]
} }