Skip to content

Commit

Permalink
fix: bugfix for encrypted contacts (#10530)
Browse files Browse the repository at this point in the history
* fix: self persona in search list

* fix: mf-4981

* fix: remove profiles

* perf: improve query profiles

* fix: mf-5095

* fix: query client

* chore: del console

* chore: fix url

* refactor: remove dead code.

* chore: code review

* fix: query client

* chore: code review

* chore: revert lock file

* chore: code rewiew

* fix: remove query client provider

* fix: end loading

* chore: code review

* chore: code review

---------

Co-authored-by: guanbinrui <52657989+guanbinrui@users.noreply.github.com>
  • Loading branch information
beyond009 and guanbinrui committed Aug 21, 2023
1 parent 852d9dc commit 265637c
Show file tree
Hide file tree
Showing 14 changed files with 229 additions and 180 deletions.
13 changes: 13 additions & 0 deletions packages/mask/background/database/persona/web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,19 @@ export async function updateRelationDB(
}
}

/** @internal */
export async function deletePersonaRelationDB(
persona: PersonaIdentifier,
linkedPersona: PersonaIdentifier,
t: RelationTransaction<'readwrite'>,
silent = false,
): Promise<void> {
const old = await t.objectStore('relations').get(IDBKeyRange.only([linkedPersona.toText(), persona.toText()]))
if (!old) return
await t.objectStore('relations').delete(IDBKeyRange.only([linkedPersona.toText(), persona.toText()]))
if (!silent) MaskMessages.events.relationsChanged.sendToAll([{ of: persona, reason: 'delete', favor: old.favor }])
}

/** @internal */
export async function createOrUpdateRelationDB(
record: Omit<RelationRecord, 'network'>,
Expand Down
13 changes: 11 additions & 2 deletions packages/mask/background/services/identity/relation/update.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import type { PersonaIdentifier, ProfileIdentifier, RelationFavor } from '@masknet/shared-base'
import { createRelationsTransaction, updateRelationDB } from '../../../database/persona/db.js'
import { createRelationsTransaction, deletePersonaRelationDB, updateRelationDB } from '../../../database/persona/db.js'

export async function updateRelation(profile: ProfileIdentifier, linked: PersonaIdentifier, favor: RelationFavor) {
export async function updateRelation(
profile: ProfileIdentifier | PersonaIdentifier,
linked: PersonaIdentifier,
favor: RelationFavor,
) {
const t = await createRelationsTransaction()
await updateRelationDB({ profile, linked, favor }, t)
}

export async function deletePersonaRelation(persona: PersonaIdentifier, linked: PersonaIdentifier) {
const t = await createRelationsTransaction()
await deletePersonaRelationDB(persona, linked, t)
}
2 changes: 2 additions & 0 deletions packages/mask/shared-ui/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -1063,6 +1063,8 @@
"popups_encrypted_friends_no_friends": "No encrypted contacts, you can try searching.",
"popups_encrypted_friends_add_friends": "Add",
"popups_encrypted_friends_no_associated_accounts": "No associated accounts",
"popups_encrypted_friends_deleted_successfully": "Deleted successfully",
"popups_encrypted_friends_added_successfully": "Added successfully",
"popups_select_wallet": "Select Wallet",
"popups_switch_persona": "Switch Persona",
"popups_recovery": "Recovery",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export function SelectProfileUI(props: SelectProfileUIProps) {
.filter((x) => !frozenPubkeyList.includes(x.linkedPersona?.publicKeyAsHex!))
}, [keyword, frozenPubkeyList, fuse, items])

const profiles = uniqBy([...frozenSelected, ...items, ...results, ...value], (x) => x.identifier)
const profiles = uniqBy([...frozenSelected, ...results, ...value], (x) => x.identifier)

return (
<div className={classes.root}>
Expand Down
59 changes: 59 additions & 0 deletions packages/mask/src/extension/popups/hook/useFriendProfiles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
type ECKeyIdentifier,
type BindingProof,
NextIDPlatform,
type ProfileIdentifier,
EMPTY_LIST,
} from '@masknet/shared-base'
import { useCurrentPersona } from '../../../components/DataSource/useCurrentPersona.js'
import { useQuery } from '@tanstack/react-query'
import { NextIDProof } from '@masknet/web3-providers'
import { UnsupportedPlatforms, PlatformSort } from '../pages/Friends/common.js'

export type Friend = {
persona: ECKeyIdentifier
profile?: ProfileIdentifier
avatar?: string
}

export const profilesFilter = (x: BindingProof) => {
return (x.platform === NextIDPlatform.ENS && x.name.endsWith('.eth')) || !UnsupportedPlatforms.includes(x.platform)
}

export function useFriendProfiles(seen: boolean, nextId?: string, twitterId?: string) {
const currentPersona = useCurrentPersona()

const { data: profiles } = useQuery(
['profiles', currentPersona?.identifier.publicKeyAsHex, nextId],
async () => {
if (!nextId) return EMPTY_LIST
try {
return await NextIDProof.queryProfilesByPublicKey(nextId, 2)
} catch (error) {
return EMPTY_LIST
}
},
{
enabled: seen && !!nextId,
},
)
if (!profiles) return EMPTY_LIST
if (profiles.length === 0) {
if (twitterId) {
return [
{
platform: NextIDPlatform.Twitter,
identity: twitterId,
is_valid: true,
last_checked_at: '',
name: twitterId,
created_at: '',
},
]
} else {
return EMPTY_LIST
}
}
const filtered = profiles.filter(profilesFilter).sort((a, b) => PlatformSort[a.platform] - PlatformSort[b.platform])
return filtered
}
107 changes: 4 additions & 103 deletions packages/mask/src/extension/popups/hook/useFriends.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
import {
type ECKeyIdentifier,
EMPTY_LIST,
type BindingProof,
NextIDPlatform,
type ProfileIdentifier,
} from '@masknet/shared-base'
import { type ECKeyIdentifier, EMPTY_LIST, type BindingProof, type ProfileIdentifier } from '@masknet/shared-base'
import { useCurrentPersona } from '../../../components/DataSource/useCurrentPersona.js'
import Services from '../../../extension/service.js'
import { first } from 'lodash-es'
import { isProfileIdentifier } from '@masknet/shared'
import { useInfiniteQuery, useQuery } from '@tanstack/react-query'
import { NextIDProof } from '@masknet/web3-providers'
import { useEffect, useState } from 'react'

export type FriendsInformation = Friend & {
profiles: BindingProof[]
Expand All @@ -24,30 +16,10 @@ export type Friend = {
avatar?: string
}

export const PlatformSort: Record<NextIDPlatform, number> = {
[NextIDPlatform.Twitter]: 0,
[NextIDPlatform.GitHub]: 1,
[NextIDPlatform.Ethereum]: 2,
[NextIDPlatform.ENS]: 3,
[NextIDPlatform.LENS]: 4,
[NextIDPlatform.Keybase]: 5,
[NextIDPlatform.Farcaster]: 6,
[NextIDPlatform.SpaceId]: 7,
[NextIDPlatform.Unstoppable]: 8,
[NextIDPlatform.RSS3]: 9,
[NextIDPlatform.REDDIT]: 10,
[NextIDPlatform.SYBIL]: 11,
[NextIDPlatform.EthLeaderboard]: 12,
[NextIDPlatform.Bit]: 13,
[NextIDPlatform.CyberConnect]: 14,
[NextIDPlatform.NextID]: 15,
}

export function useFriendsPaged() {
const [isFetchingProfiles, setIsFetchingProfiles] = useState(false)
const currentPersona = useCurrentPersona()
const { data: records = EMPTY_LIST, isLoading: recordsLoading } = useQuery(
['relation-records', currentPersona],
['relation-records', currentPersona?.identifier.rawPublicKey],
async () => {
return Services.Identity.queryRelationPaged(
currentPersona?.identifier,
Expand All @@ -59,7 +31,7 @@ export function useFriendsPaged() {
)
},
)
const { data, hasNextPage, fetchNextPage, isLoading, isFetchingNextPage } = useInfiniteQuery({
const { data, hasNextPage, fetchNextPage, isLoading, isFetchingNextPage, refetch } = useInfiniteQuery({
queryKey: ['friends', currentPersona?.identifier.rawPublicKey],
enabled: !recordsLoading,
queryFn: async ({ pageParam = 0 }) => {
Expand Down Expand Up @@ -89,83 +61,12 @@ export function useFriendsPaged() {
return nextPageOffset
},
})
const { data: profilesArray, fetchNextPage: fetchNextProfilesPage } = useInfiniteQuery(
['nextid-profiles', currentPersona],
async ({ pageParam = 0 }) => {
const friends = data?.pages[Number(pageParam)].friends
if (!friends) return EMPTY_LIST
const allSettled = await Promise.allSettled(
friends.map((item) => {
const id = item.persona.publicKeyAsHex
return NextIDProof.queryProfilesByPublicKey(id, 2)
}),
)
const profiles: BindingProof[][] = allSettled.map((item, index) => {
if (item.status === 'rejected') {
if (friends[index].profile) {
return [
{
platform: NextIDPlatform.Twitter,
identity: friends[index].profile!.userId,
is_valid: true,
last_checked_at: '',
name: friends[index].profile!.userId,
created_at: '',
},
]
} else {
return []
}
}
const filtered = item.value
.filter((x) => {
return (
(x.platform === NextIDPlatform.ENS && x.name.endsWith('.eth')) ||
(x.platform !== NextIDPlatform.Bit &&
x.platform !== NextIDPlatform.CyberConnect &&
x.platform !== NextIDPlatform.REDDIT &&
x.platform !== NextIDPlatform.SYBIL &&
x.platform !== NextIDPlatform.EthLeaderboard &&
x.platform !== NextIDPlatform.NextID)
)
})
.sort((a, b) => PlatformSort[a.platform] - PlatformSort[b.platform])
return filtered
})
return profiles
},
{
enabled: !!data,
getNextPageParam: (_, allPages) => {
if (!data || allPages.length >= data.pages.length) return undefined
return allPages.length
},
},
)
useEffect(() => {
let isMounted = true
const fetchNext = async () => {
if (!data?.pages || !profilesArray) return
let profilesLength = profilesArray?.pages?.length
setIsFetchingProfiles(true)
while (profilesLength <= data.pages.length) {
await fetchNextProfilesPage()
profilesLength += 1
if (!isMounted) break
}
setIsFetchingProfiles(false)
}
if (!isFetchingProfiles) fetchNext()
return () => {
isMounted = false
}
}, [data, fetchNextProfilesPage, profilesArray])
return {
data,
isLoading: isLoading || recordsLoading,
hasNextPage,
fetchNextPage,
isFetchingNextPage,
profilesArray,
refetch,
}
}
62 changes: 28 additions & 34 deletions packages/mask/src/extension/popups/hook/useFriendsFromSearch.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useAsyncRetry } from 'react-use'
import { ECKeyIdentifier, EMPTY_LIST, type NextIDPersonaBindings } from '@masknet/shared-base'
import type { AsyncStateRetry } from 'react-use/lib/useAsyncRetry.js'
import { uniqBy } from 'lodash-es'
import { useMemo } from 'react'
import type { Friend } from './useFriends.js'
import { NextIDPlatform } from '@masknet/shared-base'
import { PlatformSort } from './useFriends.js'
import { useCurrentLinkedPersona } from '@masknet/shared'
import { profilesFilter } from './useFriendProfiles.js'
import { PlatformSort } from '../pages/Friends/common.js'

export type NextIDPersonaBindingsWithIdentifier = NextIDPersonaBindings & { linkedPersona: ECKeyIdentifier } & {
isLocal?: boolean
Expand All @@ -14,36 +14,30 @@ export function useFriendsFromSearch(
searchResult?: NextIDPersonaBindings[],
localList?: Friend[],
searchValue?: string,
): AsyncStateRetry<NextIDPersonaBindingsWithIdentifier[]> {
return useAsyncRetry(async () => {
): NextIDPersonaBindingsWithIdentifier[] {
const currentIdentifier = useCurrentLinkedPersona()
return useMemo(() => {
if (!searchResult?.length) return EMPTY_LIST
const profiles: NextIDPersonaBindingsWithIdentifier[] = searchResult.map((item, index) => {
const filtered = item.proofs.filter(
(x) =>
(x.platform === NextIDPlatform.ENS && x.name.endsWith('.eth')) ||
(x.platform !== NextIDPlatform.Bit &&
x.platform !== NextIDPlatform.CyberConnect &&
x.platform !== NextIDPlatform.REDDIT &&
x.platform !== NextIDPlatform.SYBIL &&
x.platform !== NextIDPlatform.EthLeaderboard &&
x.platform !== NextIDPlatform.NextID),
)
const identifier = ECKeyIdentifier.fromHexPublicKeyK256(item.persona).expect(
`${item.persona} should be a valid hex public key in k256`,
)
filtered.sort((a, b) => PlatformSort[a.platform] - PlatformSort[b.platform])
const searchItem = filtered.findIndex((x) => x.identity === searchValue || x.name === searchValue)
if (searchItem !== -1) filtered.unshift(filtered.splice(searchItem, 1)[0])
return {
proofs: uniqBy(filtered, ({ identity }) => identity),
linkedPersona: identifier,
activated_at: item.activated_at,
persona: item.persona,
isLocal: localList
? localList.some((x) => x.persona.publicKeyAsHex === identifier.publicKeyAsHex)
: false,
}
})
const profiles: NextIDPersonaBindingsWithIdentifier[] = searchResult
.filter((x) => x.persona !== currentIdentifier?.identifier.publicKeyAsHex)
.map((item) => {
const filtered = item.proofs.filter(profilesFilter)
const identifier = ECKeyIdentifier.fromHexPublicKeyK256(item.persona).expect(
`${item.persona} should be a valid hex public key in k256`,
)
filtered.sort((a, b) => PlatformSort[a.platform] - PlatformSort[b.platform])
const searchItem = filtered.findIndex((x) => x.identity === searchValue || x.name === searchValue)
if (searchItem !== -1) filtered.unshift(filtered.splice(searchItem, 1)[0])
return {
proofs: uniqBy(filtered, ({ identity }) => identity),
linkedPersona: identifier,
activated_at: item.activated_at,
persona: item.persona,
isLocal: localList
? localList.some((x) => x.persona.publicKeyAsHex === identifier.publicKeyAsHex)
: false,
}
})
return uniqBy(profiles, ({ linkedPersona }) => linkedPersona.publicKeyAsHex)
}, [searchResult, localList])
}, [searchResult, localList, currentIdentifier])
}

0 comments on commit 265637c

Please sign in to comment.