diff --git a/packages/dashboard/src/pages/Personas/components/PersonaCard/Row.tsx b/packages/dashboard/src/pages/Personas/components/PersonaCard/Row.tsx index a1d3248978e4..341f9d3f5f2e 100644 --- a/packages/dashboard/src/pages/Personas/components/PersonaCard/Row.tsx +++ b/packages/dashboard/src/pages/Personas/components/PersonaCard/Row.tsx @@ -88,7 +88,12 @@ export interface PersonaRowCardUIProps { profiles: ProfileInformation[] definedSocialNetworks: SocialNetwork[] publicKey: string - onConnect: (identifier: PersonaIdentifier, networkIdentifier: string, type?: 'local' | 'nextID') => void + onConnect: ( + identifier: PersonaIdentifier, + networkIdentifier: string, + type?: 'local' | 'nextID', + profile?: ProfileIdentifier, + ) => void onDisconnect: (identifier: ProfileIdentifier) => void onRename: (identifier: PersonaIdentifier, target: string, callback?: () => void) => Promise onDeleteBound: ( @@ -200,7 +205,9 @@ export const PersonaRowCardUI = memo((props) => { disableAdd={currentNetworkProfiles.length >= 5} isHideOperations={false} key={networkIdentifier} - onConnect={(type) => onConnect(identifier, networkIdentifier, type)} + onConnect={(type, profile) => + onConnect(identifier, networkIdentifier, type, profile) + } onDisconnect={onDisconnect} onDeleteBound={(profile: ProfileIdentifier) => { onDeleteBound(identifier, profile, networkIdentifier, NextIDAction.Delete) diff --git a/packages/dashboard/src/pages/Personas/components/PersonaCard/index.tsx b/packages/dashboard/src/pages/Personas/components/PersonaCard/index.tsx index f8fa8aef15a9..ad1badae8cb2 100644 --- a/packages/dashboard/src/pages/Personas/components/PersonaCard/index.tsx +++ b/packages/dashboard/src/pages/Personas/components/PersonaCard/index.tsx @@ -79,7 +79,12 @@ export const PersonaCard = memo((props) => { export interface PersonaCardUIProps extends PersonaCardProps { definedSocialNetworks: SocialNetwork[] - onConnect: (identifier: PersonaIdentifier, networkIdentifier: string, type?: 'local' | 'nextID') => void + onConnect: ( + identifier: PersonaIdentifier, + networkIdentifier: string, + type?: 'local' | 'nextID', + profile?: ProfileIdentifier, + ) => void onDisconnect: (identifier: ProfileIdentifier) => void verification?: NextIDPersonaBindings } @@ -121,7 +126,9 @@ export const PersonaCardUI = memo((props) => { proof={proof} isHideOperations key={networkIdentifier} - onConnect={(type) => onConnect(identifier, networkIdentifier, type)} + onConnect={(type, profile) => + onConnect(identifier, networkIdentifier, type, profile) + } onDisconnect={onDisconnect} profileIdentifiers={currentNetworkProfiles.map((x) => x.identifier)} networkIdentifier={networkIdentifier} diff --git a/packages/dashboard/src/pages/Personas/components/PersonaLine/index.tsx b/packages/dashboard/src/pages/Personas/components/PersonaLine/index.tsx index 2dd27e7fde5f..89ab6d457c13 100644 --- a/packages/dashboard/src/pages/Personas/components/PersonaLine/index.tsx +++ b/packages/dashboard/src/pages/Personas/components/PersonaLine/index.tsx @@ -77,7 +77,7 @@ export const UnconnectedPersonaLine = memo(({ onCon export interface ConnectedPersonaLineProps { isHideOperations: boolean - onConnect: (type: 'nextID' | 'local') => void + onConnect: (type: 'nextID' | 'local', profile?: ProfileIdentifier) => void onDisconnect: (identifier: ProfileIdentifier) => void onDeleteBound?: (profile: ProfileIdentifier) => void profileIdentifiers: ProfileIdentifier[] @@ -108,10 +108,10 @@ export const ConnectedPersonaLine = memo( const handleUserIdClick = async (network: string, userId: string) => { await openProfilePage(network, userId) } - const handleProofIconClick = (e: MouseEvent, proof: BindingProof | undefined) => { + const handleProofIconClick = (e: MouseEvent, proof: BindingProof | undefined, profile: ProfileIdentifier) => { e.stopPropagation() if (!proof || !proof.is_valid) { - onConnect('nextID') + onConnect('nextID', profile) } } @@ -138,7 +138,7 @@ export const ConnectedPersonaLine = memo( {profile.network === EnhanceableSite.Twitter && ( handleProofIconClick(e, isProved)}> + onClick={(e: MouseEvent) => handleProofIconClick(e, isProved, profile)}> {proof.loading ? ( ) : isProved?.is_valid ? ( diff --git a/packages/mask/shared-ui/locales/en-US.json b/packages/mask/shared-ui/locales/en-US.json index 9aac8c916855..b8342b4b448a 100644 --- a/packages/mask/shared-ui/locales/en-US.json +++ b/packages/mask/shared-ui/locales/en-US.json @@ -83,6 +83,7 @@ "setup_guide_connect_auto": "Connect", "setup_guide_connect_failed": "Re-Connect", "setup_guide_verify": "Verfiy", + "setup_guide_verify_should_change_profile": "Inconsistent Account", "setup_guide_verify_dismiss": "Don't show again.", "setup_guide_verify_checking": "Checking", "setup_guide_verify_post_not_found": "No verification post found", diff --git a/packages/mask/src/components/DataSource/useNextID.ts b/packages/mask/src/components/DataSource/useNextID.ts index 568cc85c39d4..9537838e36b9 100644 --- a/packages/mask/src/components/DataSource/useNextID.ts +++ b/packages/mask/src/components/DataSource/useNextID.ts @@ -22,11 +22,12 @@ export const usePersonaBoundPlatform = (personaPublicKey: string) => { let isOpenedVerifyDialog = false let isOpenedFromButton = false -const verifyPersona = (personaIdentifier?: PersonaIdentifier) => async () => { +const verifyPersona = (personaIdentifier?: PersonaIdentifier, username?: string) => async () => { if (!personaIdentifier) return currentSetupGuideStatus[activatedSocialNetworkUI.networkIdentifier].value = stringify({ status: SetupGuideStep.VerifyOnNextID, persona: personaIdentifier.toText(), + username, }) } @@ -107,7 +108,7 @@ export function useNextIDConnectStatus() { isOpenedVerifyDialog = true isOpenedFromButton = false return NextIDVerificationStatus.WaitingVerify - }, [username, enableNextID, lastStateRef.value, isOpenedVerifyDialog, currentPersonaIdentifier.value]) + }, [username, enableNextID, isOpenedVerifyDialog, currentPersonaIdentifier.value]) return { isVerified: VerificationStatus === NextIDVerificationStatus.Verified, @@ -119,7 +120,7 @@ export function useNextIDConnectStatus() { }, action: VerificationStatus === NextIDVerificationStatus.WaitingVerify - ? verifyPersona(personaConnectStatus.currentConnectedPersona?.identifier) + ? verifyPersona(personaConnectStatus.currentConnectedPersona?.identifier, lastState.username) : null, } } diff --git a/packages/mask/src/components/InjectedComponents/SetupGuide.tsx b/packages/mask/src/components/InjectedComponents/SetupGuide.tsx index b9d04a813f25..4122decacfec 100644 --- a/packages/mask/src/components/InjectedComponents/SetupGuide.tsx +++ b/packages/mask/src/components/InjectedComponents/SetupGuide.tsx @@ -71,6 +71,11 @@ function SetupGuideUI(props: SetupGuideUIProps) { lastState.username || (lastRecognized.identifier.isUnknown ? '' : lastRecognized.identifier.userId) const [username, setUsername] = useState(getUsername) + const disableVerify = + lastRecognized.identifier.isUnknown || !lastState.username + ? false + : lastRecognized.identifier.userId !== lastState.username + useEffect(() => { const handler = (val: SocialNetworkUI.CollectingCapabilities.IdentityResolved) => { if (username === '' && !val.identifier.isUnknown) setUsername(val.identifier.userId) @@ -259,6 +264,7 @@ function SetupGuideUI(props: SetupGuideUIProps) { onVerify={onVerify} onDone={onVerifyDone} onClose={onClose} + disableVerify={disableVerify} /> ) case SetupGuideStep.PinExtension: diff --git a/packages/mask/src/components/InjectedComponents/SetupGuide/VerifyNextID.tsx b/packages/mask/src/components/InjectedComponents/SetupGuide/VerifyNextID.tsx index 4e14d30fc99e..bb6b4cf019f4 100644 --- a/packages/mask/src/components/InjectedComponents/SetupGuide/VerifyNextID.tsx +++ b/packages/mask/src/components/InjectedComponents/SetupGuide/VerifyNextID.tsx @@ -17,6 +17,7 @@ interface VerifyNextIDProps extends Partial { personaIdentifier?: PersonaIdentifier network: string avatar?: string + disableVerify: boolean onUsernameChange?: (username: string) => void onVerify: () => Promise onDone?: () => void @@ -31,6 +32,7 @@ export const VerifyNextID = ({ onDone, onClose, network, + disableVerify, }: VerifyNextIDProps) => { const { t } = useI18N() @@ -86,13 +88,13 @@ export const VerifyNextID = ({ ({ overflow: 'hidden', }, button: { - width: 150, + minWidth: 150, height: 40, minHeight: 40, marginLeft: 0, diff --git a/packages/mask/src/extension/background-script/IdentityService.ts b/packages/mask/src/extension/background-script/IdentityService.ts index b8a22610b1b1..7117855f2157 100644 --- a/packages/mask/src/extension/background-script/IdentityService.ts +++ b/packages/mask/src/extension/background-script/IdentityService.ts @@ -23,6 +23,7 @@ import { ProfileInformation, PostIVIdentifier, RelationFavor, + NextIDAction, } from '@masknet/shared-base' import type { Persona, Profile } from '../../database/Persona/types' import { @@ -41,6 +42,7 @@ import { ProfileRecord, LinkedProfileDetails, RelationRecord, + detachProfileDB, } from '../../../background/database/persona/db' import { queryPersonasDB as queryPersonasFromIndexedDB, @@ -57,6 +59,7 @@ import { getCurrentPersonaIdentifier } from './SettingsService' import { MaskMessages } from '../../utils' import { first, orderBy } from 'lodash-unified' import { recover_ECDH_256k1_KeyPair_ByMnemonicWord } from '../../utils/mnemonic-code' +import { bindProof } from '@masknet/web3-providers' assertEnvironment(Environment.ManifestBackground) @@ -391,3 +394,24 @@ export async function queryPersonaByPrivateKey(privateKeyString: string) { return null } // #endregion + +export async function detachProfileWithNextID( + profile: ProfileIdentifier, + uuid: string, + personaPublicKey: string, + action: NextIDAction, + platform: string, + identity: string, + createdAt: string, + options?: { + walletSignature?: string + signature?: string + proofLocation?: string + }, +) { + await bindProof(uuid, personaPublicKey, NextIDAction.Delete, platform, identity, createdAt, { + signature: options?.signature, + }) + await detachProfileDB(profile) + MaskMessages.events.ownProofChanged.sendToAll(undefined) +} diff --git a/packages/mask/src/extension/background-script/SocialNetworkService.ts b/packages/mask/src/extension/background-script/SocialNetworkService.ts index 46b9a57e2633..bbdd64706fb2 100644 --- a/packages/mask/src/extension/background-script/SocialNetworkService.ts +++ b/packages/mask/src/extension/background-script/SocialNetworkService.ts @@ -5,7 +5,7 @@ import { requestSNSAdaptorPermission } from '../../social-network/utils/permissi import { currentSetupGuideStatus } from '../../settings/settings' import stringify from 'json-stable-stringify' import { SetupGuideStep } from '../../components/InjectedComponents/SetupGuide/types' -import type { PersonaIdentifier } from '@masknet/shared-base' +import type { PersonaIdentifier, ProfileIdentifier } from '@masknet/shared-base' import { delay } from '@dimensiondev/kit' export async function getDefinedSocialNetworkUIs() { @@ -15,7 +15,12 @@ export async function getDefinedSocialNetworkUIs() { } }) } -export async function connectSocialNetwork(identifier: PersonaIdentifier, network: string, type?: 'local' | 'nextID') { +export async function connectSocialNetwork( + identifier: PersonaIdentifier, + network: string, + type?: 'local' | 'nextID', + profile?: ProfileIdentifier, +) { const ui = await loadSocialNetworkUI(network) const home = ui.utils.getHomePage?.() if (!Flags.no_web_extension_dynamic_permission_request) { @@ -24,6 +29,7 @@ export async function connectSocialNetwork(identifier: PersonaIdentifier, networ currentSetupGuideStatus[network].value = stringify({ status: type === 'nextID' ? SetupGuideStep.VerifyOnNextID : SetupGuideStep.FindUsername, persona: identifier.toText(), + username: profile?.userId, }) await delay(100) home && browser.tabs.create({ active: true, url: home }) diff --git a/packages/mask/src/extension/popups/pages/Personas/PersonaSignRequest/index.tsx b/packages/mask/src/extension/popups/pages/Personas/PersonaSignRequest/index.tsx index b55204d6ac2b..8b0c92ab3b30 100644 --- a/packages/mask/src/extension/popups/pages/Personas/PersonaSignRequest/index.tsx +++ b/packages/mask/src/extension/popups/pages/Personas/PersonaSignRequest/index.tsx @@ -6,6 +6,7 @@ import { useNavigate, useLocation } from 'react-router-dom' import { ECKeyIdentifier, Identifier, PopupRoutes } from '@masknet/shared-base' import { useMyPersonas } from '../../../../../components/DataSource/useMyPersonas' import type { Persona } from '../../../../../database' +import { delay } from '@dimensiondev/kit' const useStyles = makeStyles()(() => ({ container: { @@ -103,7 +104,7 @@ const PersonaSignRequest = memo(() => { } }, [personas, location.search]) - const onSign = () => { + const onSign = async () => { if (!requestID || !selected) return MaskMessages.events.personaSignRequest.sendToBackgroundPage({ requestID, @@ -112,12 +113,15 @@ const PersonaSignRequest = memo(() => { ECKeyIdentifier, ).unwrap(), }) + + await delay(200) window.close() } - const onCancel = () => { + const onCancel = async () => { if (!requestID) return MaskMessages.events.personaSignRequest.sendToBackgroundPage({ requestID }) + await delay(200) window.close() } diff --git a/packages/mask/src/extension/popups/pages/Personas/components/ProfileList/index.tsx b/packages/mask/src/extension/popups/pages/Personas/components/ProfileList/index.tsx index ff2d7edfb23f..d8a681ee92f9 100644 --- a/packages/mask/src/extension/popups/pages/Personas/components/ProfileList/index.tsx +++ b/packages/mask/src/extension/popups/pages/Personas/components/ProfileList/index.tsx @@ -17,7 +17,7 @@ import { useAsyncFn, useAsyncRetry } from 'react-use' import Services from '../../../../../service' import { GrayMasks } from '@masknet/icons' import { DisconnectDialog } from '../DisconnectDialog' -import { bindProof, createPersonaPayload, queryExistedBindingByPersona } from '@masknet/web3-providers' +import { createPersonaPayload, queryExistedBindingByPersona } from '@masknet/web3-providers' import { delay } from '@dimensiondev/kit' import classNames from 'classnames' @@ -111,9 +111,14 @@ export const ProfileList = memo(() => { ) const [, onConnect] = useAsyncFn( - async (networkIdentifier: string) => { + async (networkIdentifier: string, type?: 'local' | 'nextID', profile?: ProfileIdentifier) => { if (currentPersona) { - await Services.SocialNetwork.connectSocialNetwork(currentPersona.identifier, networkIdentifier) + await Services.SocialNetwork.connectSocialNetwork( + currentPersona.identifier, + networkIdentifier, + type, + profile, + ) } }, [currentPersona], @@ -183,7 +188,9 @@ export const ProfileList = memo(() => { if (!signatureResult) return - await bindProof( + // workaround: should remove this service method + await Services.Identity.detachProfileWithNextID( + unbind.identifier, result.uuid, publicHexKey, NextIDAction.Delete, @@ -195,8 +202,6 @@ export const ProfileList = memo(() => { }, ) - await Services.Identity.detachProfile(unbind.identifier) - await delay(2000) setUnbind(null) refreshProfileList() @@ -234,7 +239,7 @@ interface MergedProfileInformation extends ProfileInformation { } export interface ProfileListUIProps { - onConnect: (networkIdentifier: string) => void + onConnect: (networkIdentifier: string, type?: 'local' | 'nextID', profile?: ProfileIdentifier) => void onDisconnect: ( identifier: ProfileIdentifier, is_valid?: boolean, @@ -296,7 +301,13 @@ export const ProfileListUI = memo( @{identifier.userId} {!is_valid && identifier.network === 'twitter.com' ? ( - + { + onConnect(identifier.network, 'nextID', identifier) + e.stopPropagation() + }}> {t('popups_persona_to_be_verified')} ) : null}