diff --git a/packages/modal/src/ui/components/Loader/Loader.tsx b/packages/modal/src/ui/components/Loader/Loader.tsx index 063175ec4..b5ae1b90d 100644 --- a/packages/modal/src/ui/components/Loader/Loader.tsx +++ b/packages/modal/src/ui/components/Loader/Loader.tsx @@ -1,4 +1,4 @@ -import { WALLET_CONNECTOR_TYPE } from "@web3auth/no-modal"; +import { log, WALLET_CONNECTOR_TYPE } from "@web3auth/no-modal"; import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; @@ -6,7 +6,7 @@ import { MODAL_STATUS } from "../../interfaces"; import i18n from "../../localeImport"; import Image from "../Image"; import SpinnerLoader from "../SpinnerLoader"; -import { AuthorizingStatusType, ConnectedStatusType, ConnectingStatusType, ErroredStatusType, LoaderProps } from "./Loader.type"; +import { AuthorizingStatusType, BlockedStatusType, ConnectedStatusType, ConnectingStatusType, ErroredStatusType, LoaderProps } from "./Loader.type"; /** * ConnectingStatus component @@ -83,6 +83,37 @@ function ErroredStatus(props: ErroredStatusType) { ); } +function BlockedStatus(props: BlockedStatusType) { + const { primaryMessage, secondaryMessage, buttonMessage } = props; + + const handleChangeWallet = () => { + // TODO: wire up real action in a follow-up commit + log.info("change wallet"); + }; + + return ( +
+ + + +

{primaryMessage}

+

{secondaryMessage}

+ +
+ ); +} + function AuthorizingStatus(props: AuthorizingStatusType) { const [t] = useTranslation(undefined, { i18n }); const { connector, externalWalletsConfig, handleMobileVerifyConnect } = props; @@ -213,6 +244,7 @@ function Loader(props: LoaderProps) { externalWalletsConfig, handleMobileVerifyConnect, hideSuccessScreen = false, + blockedUserConfig, onAcceptConsent, onDeclineConsent, privacyPolicy, @@ -255,6 +287,14 @@ function Loader(props: LoaderProps) { {modalStatus === MODAL_STATUS.ERRORED && } + {modalStatus === MODAL_STATUS.BLOCKED && blockedUserConfig && ( + + )} + {modalStatus === MODAL_STATUS.AUTHORIZING && ( ; @@ -12,6 +12,7 @@ export interface LoaderProps { isConnectAndSignAuthenticationMode: boolean; handleMobileVerifyConnect: (params: { connector: WALLET_CONNECTOR_TYPE }) => void; hideSuccessScreen?: boolean; + blockedUserConfig?: BlockedUserConfig; onAcceptConsent?: () => void | Promise; onDeclineConsent?: () => void | Promise; privacyPolicy?: string; @@ -25,3 +26,5 @@ export type ConnectedStatusType = Pick; export type ErroredStatusType = Pick; export type AuthorizingStatusType = Pick; + +export type BlockedStatusType = BlockedUserConfig; diff --git a/packages/modal/src/ui/containers/Root/Root.tsx b/packages/modal/src/ui/containers/Root/Root.tsx index d6bf756f7..f576b967f 100644 --- a/packages/modal/src/ui/containers/Root/Root.tsx +++ b/packages/modal/src/ui/containers/Root/Root.tsx @@ -224,6 +224,7 @@ function RootContent(props: RootProps) { externalWalletsConfig={modalState.externalWalletsConfig} handleMobileVerifyConnect={handleMobileVerifyConnect} hideSuccessScreen={hideSuccessScreen} + blockedUserConfig={modalState.blockedUserConfig} onAcceptConsent={handleAcceptConsent} onDeclineConsent={handleDeclineConsent} privacyPolicy={privacyPolicy} diff --git a/packages/modal/src/ui/containers/Widget/Widget.tsx b/packages/modal/src/ui/containers/Widget/Widget.tsx index d9cd3c946..b02dc52ff 100644 --- a/packages/modal/src/ui/containers/Widget/Widget.tsx +++ b/packages/modal/src/ui/containers/Widget/Widget.tsx @@ -50,6 +50,13 @@ function WidgetContent() { postLoadingMessage: "", }); } + if (modalState.status === MODAL_STATUS.BLOCKED) { + setModalState({ + ...modalState, + modalVisibility: false, + externalWalletsVisibility: false, + }); + } }; const showCloseIcon = useMemo(() => { @@ -57,6 +64,7 @@ function WidgetContent() { modalState.status === MODAL_STATUS.INITIALIZED || modalState.status === MODAL_STATUS.CONNECTED || modalState.status === MODAL_STATUS.ERRORED || + modalState.status === MODAL_STATUS.BLOCKED || modalState.status === MODAL_STATUS.AUTHORIZED ); }, [modalState.status]); diff --git a/packages/modal/src/ui/interfaces.ts b/packages/modal/src/ui/interfaces.ts index 43f713cf2..096ecea33 100644 --- a/packages/modal/src/ui/interfaces.ts +++ b/packages/modal/src/ui/interfaces.ts @@ -75,6 +75,16 @@ export interface UIConfig extends CoreUIConfig, LoginModalConfig { */ hideSuccessScreen?: boolean; + /** + * Configuration for the blocked user screen shown when a user is blocked by the dapp. + * All fields are optional and have defaults. + */ + blockedUserConfig?: { + primaryMessage?: string; + secondaryMessage?: string; + buttonMessage?: string; + }; + connectorListener: SafeEventEmitter; } @@ -122,6 +132,7 @@ export const MODAL_STATUS = { CONNECTED: "connected", CONNECTING: "connecting", ERRORED: "errored", + BLOCKED: "blocked", AUTHORIZING: "authorizing", AUTHORIZED: "authorized", CONSENT_REQUIRING: "consent_requiring", @@ -206,6 +217,15 @@ export interface ModalState { // Config State - set during initialization, rarely changes socialLoginsConfig: SocialLoginsConfig; externalWalletsConfig: Record; + + // Blocked user state + blockedUserConfig?: BlockedUserConfig; +} + +export interface BlockedUserConfig { + primaryMessage: string; + secondaryMessage: string; + buttonMessage: string; } export type SocialLoginEventType = { loginParams: ModalLoginParams }; diff --git a/packages/modal/src/ui/loginModal.tsx b/packages/modal/src/ui/loginModal.tsx index a324c7518..adebfec3f 100644 --- a/packages/modal/src/ui/loginModal.tsx +++ b/packages/modal/src/ui/loginModal.tsx @@ -563,6 +563,18 @@ export class LoginModal { listener.on(CONNECTOR_EVENTS.ERRORED, (error: Web3AuthError, loginMode: LoginModeType) => { log.error("error", error, error.message); if (loginMode === LOGIN_MODE.NO_MODAL) return; + if (error.code === 5120) { + this.setState({ + modalVisibility: true, + status: MODAL_STATUS.BLOCKED, + blockedUserConfig: { + primaryMessage: this.uiConfig.blockedUserConfig?.primaryMessage || "You cannot access the site.", + secondaryMessage: this.uiConfig.blockedUserConfig?.secondaryMessage || "Access to the site is restricted", + buttonMessage: this.uiConfig.blockedUserConfig?.buttonMessage || "Change wallet", + }, + }); + return; + } if (error.code === 5000) { if (this.uiConfig.displayErrorsOnModal) this.setState({ diff --git a/packages/no-modal/src/base/errors/index.ts b/packages/no-modal/src/base/errors/index.ts index d58507641..441ab6235 100644 --- a/packages/no-modal/src/base/errors/index.ts +++ b/packages/no-modal/src/base/errors/index.ts @@ -144,6 +144,7 @@ export class WalletLoginError extends Web3AuthError { 5117: "Unsupported operation", 5118: "useSFAKey flag is enabled but SFA key is not available", 5119: "User not logged in.", + 5120: "User is blocked by the application", }; public constructor(code: number, message?: string, cause?: unknown) { @@ -193,6 +194,10 @@ export class WalletLoginError extends Web3AuthError { public static userNotLoggedIn(extraMessage = "", cause?: unknown): IWeb3AuthError { return WalletLoginError.fromCode(5119, extraMessage, cause); } + + public static userBlocked(extraMessage = "", cause?: unknown): IWeb3AuthError { + return WalletLoginError.fromCode(5120, extraMessage, cause); + } } export class WalletOperationsError extends Web3AuthError {