feat: 💨
This commit is contained in:
@@ -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'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user