From 9a933aa81481a7675f7bc482981009c5237cb540 Mon Sep 17 00:00:00 2001 From: Sergey Kintsel Date: Sun, 19 May 2024 23:51:20 +0100 Subject: [PATCH 01/18] Add email login option --- src/GoogleAuth/GoogleAuth.test.tsx | 39 -------- src/GoogleAuth/GoogleAuth.tsx | 47 --------- src/GoogleAuth/getGoogleCredentials.ts | 49 ---------- src/GoogleAuth/index.tsx | 3 - .../parseTorusRedirectParams.test.ts | 35 ------- src/GoogleAuth/parseTorusRedirectParams.ts | 61 ------------ src/assets/icons/Envelope.tsx | 19 ++++ src/assets/icons/index.tsx | 1 + src/auth/LoginButton.tsx | 77 +++++++++++++++ src/auth/index.ts | 96 +++++++++++++++++++ src/auth/parseTorusRedirectParams.test.ts | 34 +++++++ src/auth/parseTorusRedirectParams.ts | 40 ++++++++ src/auth/types.ts | 1 + .../connectOrCreate/ConnectOrCreate.tsx | 11 ++- src/components/SendFlow/SignButton.tsx | 5 +- src/types/Account.ts | 2 +- src/utils/useDeeplinkHandler.ts | 2 +- 17 files changed, 280 insertions(+), 242 deletions(-) delete mode 100644 src/GoogleAuth/GoogleAuth.test.tsx delete mode 100644 src/GoogleAuth/GoogleAuth.tsx delete mode 100644 src/GoogleAuth/getGoogleCredentials.ts delete mode 100644 src/GoogleAuth/index.tsx delete mode 100644 src/GoogleAuth/parseTorusRedirectParams.test.ts delete mode 100644 src/GoogleAuth/parseTorusRedirectParams.ts create mode 100644 src/assets/icons/Envelope.tsx create mode 100644 src/auth/LoginButton.tsx create mode 100644 src/auth/index.ts create mode 100644 src/auth/parseTorusRedirectParams.test.ts create mode 100644 src/auth/parseTorusRedirectParams.ts create mode 100644 src/auth/types.ts diff --git a/src/GoogleAuth/GoogleAuth.test.tsx b/src/GoogleAuth/GoogleAuth.test.tsx deleted file mode 100644 index 50db0a5ecf..0000000000 --- a/src/GoogleAuth/GoogleAuth.test.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { getGoogleCredentials } from "./getGoogleCredentials"; -import { GoogleAuth } from "./GoogleAuth"; -import { act, render, screen, userEvent } from "../mocks/testUtils"; -import { mockToast } from "../mocks/toast"; - -jest.mock("./getGoogleCredentials"); - -describe("", () => { - it("calls the onAuth callback with the secret key and email", async () => { - const user = userEvent.setup(); - const authSpy = jest.fn(); - jest.mocked(getGoogleCredentials).mockResolvedValue({ - secretKey: "test", - email: "test@email.com", - }); - render(); - - await act(() => user.click(screen.getByTestId("google-auth-button"))); - - expect(authSpy).toHaveBeenCalledWith("test", "test@email.com"); - }); - - it("shows an error toast if the auth fails", async () => { - const user = userEvent.setup(); - const authSpy = jest.fn(); - jest.mocked(getGoogleCredentials).mockRejectedValue(new Error("test")); - render(); - - await act(() => user.click(screen.getByTestId("google-auth-button"))); - - expect(mockToast).toHaveBeenCalledWith({ - description: "test", - status: "error", - title: "Social login failed", - }); - - expect(authSpy).not.toHaveBeenCalled(); - }); -}); diff --git a/src/GoogleAuth/GoogleAuth.tsx b/src/GoogleAuth/GoogleAuth.tsx deleted file mode 100644 index 5ec62e9c35..0000000000 --- a/src/GoogleAuth/GoogleAuth.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { IconButton } from "@chakra-ui/react"; - -import { getGoogleCredentials } from "./getGoogleCredentials"; -import { GoogleIcon } from "../assets/icons"; -import colors from "../style/colors"; -import { useAsyncActionHandler } from "../utils/hooks/useAsyncActionHandler"; - -/** - * Component that's used for onboarding users with Google SSO. - * It's used on the onboarding page. - * It opens a popup window with Google SSO on a click. - * - * @param onAuth - callback function which is called with the secret key and email of the user - * on a successful authentication - */ -export const GoogleAuth: React.FC<{ onAuth: (secretKey: string, email: string) => void }> = ({ - onAuth, -}) => { - const { isLoading, handleAsyncAction } = useAsyncActionHandler(); - - const onClick = async () => - handleAsyncAction( - async () => { - const { secretKey, email } = await getGoogleCredentials(); - return onAuth(secretKey, email); - }, - { - title: "Social login failed", - } - ); - - return ( - } - isLoading={isLoading} - onClick={onClick} - size="lg" - variant="outline" - /> - ); -}; diff --git a/src/GoogleAuth/getGoogleCredentials.ts b/src/GoogleAuth/getGoogleCredentials.ts deleted file mode 100644 index 46d6c5dedd..0000000000 --- a/src/GoogleAuth/getGoogleCredentials.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Prefix, b58cencode, prefix } from "@taquito/utils"; -import CustomAuth from "@toruslabs/customauth"; - -import { withTimeout } from "../utils/withTimeout"; - -const LOGIN_TIMEOUT = 60 * 1000; // 1 minute -const WEB3_AUTH_CLIENT_ID = - "BBQoFIabI50S1-0QsGHGTM4qID_FDjja0ZxIxKPyFqc0El--M-EG0c2giaBYVTVVE6RC9WCUzCJyW24aJrR_Lzc"; -const SUB_VERIFIER_CLIENT_ID = - "1070572364808-d31nlkneam5ee6dr0tu28fjjbsdkfta5.apps.googleusercontent.com"; - -/** - * This function will open a popup window with the google auth page. - * Once the user authorizes the function will navigate the user back to Umami app. - * In the end we obtain the user's email and raw secret key. - * - * The function has a timeout to prevent infinite loading state. - */ -export const getGoogleCredentials = async () => - withTimeout(async () => { - const torus = new CustomAuth({ - web3AuthClientId: WEB3_AUTH_CLIENT_ID, - baseUrl: "https://umamiwallet.com/auth/v2.0.1/", - redirectPathName: "redirect.html", - redirectToOpener: true, - uxMode: "popup", - network: "mainnet", - }); - await torus.init({ skipSw: true }); - - const result = await torus.triggerAggregateLogin({ - verifierIdentifier: "tezos-google", - aggregateVerifierType: "single_id_verifier", - subVerifierDetailsArray: [ - { - clientId: SUB_VERIFIER_CLIENT_ID, - typeOfLogin: "google", - verifier: "umami", - }, - ], - }); - const privateKey = result.finalKeyData.privKey || result.oAuthKeyData.privKey; - const secretKey = b58cencode(privateKey, prefix[Prefix.SPSK]); - - return { - secretKey, - email: result.userInfo[0].email, - }; - }, LOGIN_TIMEOUT); diff --git a/src/GoogleAuth/index.tsx b/src/GoogleAuth/index.tsx deleted file mode 100644 index 4ce5431bb3..0000000000 --- a/src/GoogleAuth/index.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./GoogleAuth"; -export * from "./parseTorusRedirectParams"; -export * from "./getGoogleCredentials"; diff --git a/src/GoogleAuth/parseTorusRedirectParams.test.ts b/src/GoogleAuth/parseTorusRedirectParams.test.ts deleted file mode 100644 index 291a7209be..0000000000 --- a/src/GoogleAuth/parseTorusRedirectParams.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { parseTorusRedirectParams } from "./parseTorusRedirectParams"; - -// we get URL params in a compressed format generated by zipurl -const googleAuthDeeplink = - "umami://auth/H4sIAAAAAAAAA51VSY-zyBL8NV_f3ILCuM2hD2AWY1Ngs1OXFptZXAXYgFl-_QNP683oSXN5EqhQQWZlREYkRfHdU_txGuu56h8fcR5WVYq_n2lSPNO4-_nd-PnnR233nU6nJqp0KvDYEjn4rpZ1kdj8Nj3ibcQ0feJLhXY4AeSfGkQwjgt1p1ZuF3h0oxZDkRzxgByO2ApXhd62MIpTFQGuihRnjZuQZzYxcctENl82I-CIuJNaNlRcufh2_QNYwDLi7_IRdt9TCLjPkOJvwk808QeJCaaKvdoDOTz0Pe-r5xxTkg3QELMQCSMhcpOAjXzWaCHTmK9oCuyWUOdn6bjRrWA942Ezmzl7HISzEXDZGGn8cxCC-YZ9Y-Sn1OY4ykmUyN_iY6bMG-sJ2WvKP8wvlqcOznC2xk68lJtrhINrj81g1F6PVLWH8JAFZ96PLd5ULfl6VHwAC-umZkpTeUnc-hteq3OluMCBonfcR1p8MyzHfbTxd0rCAq-YqeZZ3wqcvp_rJq2K5P2Yd13T_mH4P0BermEYPrO6znAaNkX7Gddk2Qz7Ll-Wvk2fRXWrP__O-X8E_5bxUbz1kEdKvPbRcmaV1gu1VUnXoMPS91ImuigQqEhb5AUzErMtFO-MoZilIZ4ILN0SguWel7bNKNc9lTHEt36o1OfXnGeXvha36-cqu5iB61adHM0hnuuXBuQyABwdVeaskeQVgaRFFltGgFpT5Gm1plBHKMaDbscTnAPKEPmt5l1n6LFtCFgceBSNPAegozokR2cyFPQIyWlGSkcSRaI1Ig_xEfYIcC8ERpz4Oo6J_lrOxFF17Zfzu1XWge--rQBtnoGiw8AFmC5mg3Gg73CW-0jpeuTJne65WCfmBI8mDcuchKQpYmA-UWXmusXm8VH4G4vvzouNFjwshTyW0tZzrYXeSqeD1ValNCx0Ubq9nCdCAMUFnx1TsMwWptY25H_Z87hYVrn3ieKW4WHdX-2I2_VdDNwJAZfVlpatPCJv5P_te59BC3ZMQm_F-pclV54TmasDX6_XfI6n323APlLf3OqiXJm2SfRKf1lEppy1dsK-IqLjfxkbfUCCwiillcNZFwNm4XCNySPPWXtvL3xUyL9nFsB9ctRxtNZXCU3AmHRMnP_Rx1jDif0dLy69Yl36teLqk8NbJ6_A4oQriLcR0E_IQ5fI6VST1ptYQVV0x0pKTAdKuWrZCUgkdAglWU_tnLVmtI0xEjzP4eL5DjQPrvVXoY9whLl-GXdvjM4vv6tGEJG70BtZH_wXjxYufV3qatf3kcKVgTe-45C37UxKXXM2gX_9ByeLlt-cuNt4sRi0V21D-t37w1CEldms8cHMjwFxaF1MiGEv6xzTAYAUBOoMy8Wmdl7qM08bdlIE9tsjyzg6cZ9od94AxTneOj0QJ7MxzmdTGtHliTfb-8stwq87Omwa4Uc3klhEPw81DUb_Pu7I-W5Rzy32TiE851Dtg2SbZWluxRk2-ebr1Zqplmh7y27Vn6-dsG-S8HLazwJ57GN8TjmQXSl3o4YFk1wT2RkPD1JIynysqUIQdiBMVam7tKWYzUIapjo4uTaz6OVmBrg8VmehlmTuVOgJYiYoTAk9ieBQ9j61P1_IT7vvzsMk9Lv55YhfyebpnS8_nHjFvRb1lTen6eNySc4pNSpNtB11W6ppVxOaQpTy0Xi9DJ4NXLO1UnbsIWfxCneRuP12mXKeaZS7o4FHK6y-5K_4JsUd5I9q-sj7QKd2e0_js4_wm_povuO6atOq-3gV3wcy3hwanXzKPZnM8q8D2NQlrJtSItgevkJRPjjYtSy3aQ0_hy5zHRzQjpGMPbcyrcgJpnV4QbcJXJJ7kBEMh3ZDSMzWcDjDJWPoePfRdNXBwUnputJoY1SmivlYhtEdVsIc3PPacXEfys09off_AYBvCkASCAAA"; - -test("parseTorusRedirectParams", () => { - expect(parseTorusRedirectParams(googleAuthDeeplink)).toEqual({ - channel: "redirect_channel_u08xyxoznuq", - data: { - hashParams: { - access_token: - "ya29.a0AfB_byACE3Yyn5QTwmCqN8AXIKhl0ET2Zwc5MZBxmmFpd2-FKL1BgL37byYTsm0KrjUVbfi5WOqT3-zgqCBKOY9gxbLArwBYzflXOxAyeT990UdGbX4lHgGz-SrM5QeAqR75A0CUwKSxtDPj-QblYQulRYxLvqeITwaCgYKAXcSARISFQHGX2MiSfIgGpnWdcsX-ALohGiPMw0169", - authuser: "0", - expires_in: "3599", - hd: "trili.tech", - id_token: - "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFmNDBmMGE4ZWYzZDg4MDk3OGRjODJmMjVjM2VjMzE3YzZhNWI3ODEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiIxMDcwNTcyMzY0ODA4LWQzMW5sa25lYW01ZWU2ZHIwdHUyOGZqamJzZGtmdGE1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiYXVkIjoiMTA3MDU3MjM2NDgwOC1kMzFubGtuZWFtNWVlNmRyMHR1Mjhmampic2RrZnRhNS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsInN1YiI6IjEwODE0NTU3MDM2MDY0OTc0Mjg3MiIsImhkIjoidHJpbGkudGVjaCIsImVtYWlsIjoic2VyZ2V5LmtpbnRzZWxAdHJpbGkudGVjaCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJhdF9oYXNoIjoiUWNkT25qeXR4NDFnRTRmNnNvSmF0USIsIm5vbmNlIjoidTA4eHl4b3pudXEiLCJuYmYiOjE3MDUzNDY3NDgsIm5hbWUiOiJTZXJnZXkgS2ludHNlbCIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS9BQ2c4b2NJZWZPbUtIR1NpcGZnbklGemRUMEhISTd2dEZCaEFNeTh5SzZ4clZBWWU9czk2LWMiLCJnaXZlbl9uYW1lIjoiU2VyZ2V5IiwiZmFtaWx5X25hbWUiOiJLaW50c2VsIiwibG9jYWxlIjoiZW4tR0IiLCJpYXQiOjE3MDUzNDcwNDgsImV4cCI6MTcwNTM1MDY0OCwianRpIjoiYzAxYmU1NDdmOTU1Nzc1Y2M0M2IzMjI1NThjNzA1OTdiYTIxMDc2MiJ9.Z6K-2GUHftNYDyRpOKKRExZPrl-4kvVia7kZC-pB_NOdcDZ_qIeYxXkx6mKkS0r4lWJaMKhMIuYd4ggehScglRAp7vsReLdL8STsI_76B8pdaPJ8zBmq8clKe92gQ0V-Iai3dQdFUxCqmiEGzHo0iBB62aeIEtPsjDgzBeaeN2JVT3uYmfRYljHnKBoEF9JiNdZ3yMByd1yD2CjuX08KPm_s8tKwyBu6zvUD7d-rWKP_9DQluLbunWzeeqPPdKe0xGpb4xNTEo1VLBpiDEhxOvvOA5YVRsSe5xuM9SAG9PE984ZCIWROj6HOlxSan7F7cfEctMAHIeqhuYN068WLAg", - prompt: "consent", - scope: - "email%20profile%20openid%20https://www.googleapis.com/auth/userinfo.email%20https://www.googleapis.com/auth/userinfo.profile", - state: - "eyJpbnN0YW5jZUlkIjoidTA4eHl4b3pudXEiLCJ2ZXJpZmllciI6InVtYW1pIiwidHlwZU9mTG9naW4iOiJnb29nbGUiLCJyZWRpcmVjdFRvT3BlbmVyIjp0cnVlfQ%253D%253D", - token_type: "Bearer", - }, - instanceParams: { - instanceId: "u08xyxoznuq", - redirectToOpener: "true", - typeOfLogin: "google", - verifier: "umami", - }, - }, - error: null, - }); -}); diff --git a/src/GoogleAuth/parseTorusRedirectParams.ts b/src/GoogleAuth/parseTorusRedirectParams.ts deleted file mode 100644 index ba2d50d2e4..0000000000 --- a/src/GoogleAuth/parseTorusRedirectParams.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * This function parses the deeplink url we get on a successful social auth. - * - * The parameters are built by - * https://github.com/trilitech/umami-web/blob/main/public/auth/v2.x/redirect.html - * - * which is essentially a copy of - * https://github.com/torusresearch/CustomAuth/blob/master/serviceworker/redirect.html - */ - -import { invert } from "lodash"; -import { unzipurl } from "zipurl"; - -/** - * these search params are used to reduce the size of the URL - * which has a limit in some browsers set to 2k characters - */ -const PARAMS_MAPPING = { - at: "access_token", - a: "authuser", - ei: "expires_in", - it: "id_token", - ii: "instanceId", - p: "prompt", - sc: "scope", - st: "state", - vi: "version_info", -}; - -const PARAMS_REVERSE_MAPPING = invert(PARAMS_MAPPING); - -export const parseTorusRedirectParams = (url: string) => { - const params = new URLSearchParams(unzipurl(url.replace("umami://auth/", ""))); - - const assignValues = (acc: any, key: string) => { - acc[key] = params.get(PARAMS_REVERSE_MAPPING[key]) ?? params.get(key); - return acc; - }; - - const instanceParams = { - verifier: "umami", - redirectToOpener: "true", - typeOfLogin: "google", - ...assignValues({}, "instanceId"), - }; - - const hashParams = { - hd: "trili.tech", - token_type: "Bearer", - ...["state", "access_token", "expires_in", "scope", "id_token", "authuser", "prompt"].reduce( - assignValues, - {} - ), - }; - - return { - channel: `redirect_channel_${instanceParams.instanceId}`, - data: { instanceParams, hashParams }, - ...assignValues({}, "error"), - }; -}; diff --git a/src/assets/icons/Envelope.tsx b/src/assets/icons/Envelope.tsx new file mode 100644 index 0000000000..eefe1b3cb1 --- /dev/null +++ b/src/assets/icons/Envelope.tsx @@ -0,0 +1,19 @@ +import { Icon, IconProps } from "@chakra-ui/react"; + +export const EnvelopeIcon: React.FC = props => ( + + + +); diff --git a/src/assets/icons/index.tsx b/src/assets/icons/index.tsx index 18c159f757..5d28987d64 100644 --- a/src/assets/icons/index.tsx +++ b/src/assets/icons/index.tsx @@ -18,6 +18,7 @@ export * from "./Document"; export * from "./DoubleCheckmark"; export * from "./Download"; export * from "./EditAccount"; +export * from "./Envelope"; export * from "./Exclamation"; export * from "./ExitArrow"; export * from "./ExternalLink"; diff --git a/src/auth/LoginButton.tsx b/src/auth/LoginButton.tsx new file mode 100644 index 0000000000..2e60409a70 --- /dev/null +++ b/src/auth/LoginButton.tsx @@ -0,0 +1,77 @@ +import { IconButton } from "@chakra-ui/react"; +import { minutesToMilliseconds } from "date-fns"; + +import { EnvelopeIcon, GoogleIcon } from "../assets/icons"; +import colors from "../style/colors"; +import { useAsyncActionHandler } from "../utils/hooks/useAsyncActionHandler"; +import { withTimeout } from "../utils/withTimeout"; + +import { Auth, IDP } from "."; + +const LOGIN_TIMEOUT = minutesToMilliseconds(1); + +/** + * Component that's used for onboarding users with Google SSO. + * It's used on the onboarding page. + * It opens a popup window with Google SSO on a click. + * + * @param onAuth - callback function which is called with the secret key and email of the user + * on a successful authentication + */ +export const LoginButton: React.FC<{ + idp: IDP; + onAuth: ({ secretKey, email }: { secretKey: string; email: string }) => void; +}> = ({ idp, onAuth }) => { + const { isLoading, handleAsyncAction } = useAsyncActionHandler(); + + const onClick = () => + handleAsyncAction( + () => withTimeout(() => Auth.for(idp).getCredentials().then(onAuth), LOGIN_TIMEOUT), + { title: "Social login failed" } + ); + + switch (idp) { + case "google": + return ; + case "email": + return ; + } +}; + +const GoogleButton: React.FC<{ isLoading: boolean; onClick: () => void }> = ({ + isLoading, + onClick, +}) => ( + } + isLoading={isLoading} + onClick={onClick} + size="lg" + variant="outline" + /> +); + +const EmailButton: React.FC<{ isLoading: boolean; onClick: () => void }> = ({ + isLoading, + onClick, +}) => ( + } + isLoading={isLoading} + onClick={onClick} + size="lg" + variant="outline" + /> +); diff --git a/src/auth/index.ts b/src/auth/index.ts new file mode 100644 index 0000000000..c16ee0d10f --- /dev/null +++ b/src/auth/index.ts @@ -0,0 +1,96 @@ +import { Prefix, b58cencode, prefix } from "@taquito/utils"; +import CustomAuth, { TorusAggregateLoginResponse, TorusLoginResponse } from "@toruslabs/customauth"; + +import { IDP } from "./types"; +export * from "./parseTorusRedirectParams"; +export * from "./types"; + +const WEB3_AUTH_CLIENT_ID = + "BBQoFIabI50S1-0QsGHGTM4qID_FDjja0ZxIxKPyFqc0El--M-EG0c2giaBYVTVVE6RC9WCUzCJyW24aJrR_Lzc"; +const GOOGLE_SUB_VERIFIER_CLIENT_ID = + "1070572364808-d31nlkneam5ee6dr0tu28fjjbsdkfta5.apps.googleusercontent.com"; +const EMAIL_SUB_VERIFIER_CLIENT_ID = "LTg6fVsacafGmhv14TZlrWF1EavwQoDZ"; + +export abstract class Auth { + static for(idp: IDP) { + switch (idp) { + case "google": + return new GoogleAuth(); + case "email": + return new EmailAuth(); + } + } + + protected async getTorusClient(): Promise { + const torus = new CustomAuth({ + web3AuthClientId: WEB3_AUTH_CLIENT_ID, + baseUrl: "https://umamiwallet.com/auth/v2.0.1/", + redirectPathName: "redirect.html?full_params=true", // TODO: fix this once we have a proper redirect page + redirectToOpener: true, + uxMode: "popup", + network: "mainnet", + }); + + await torus.init({ skipSw: true }); + + return torus; + } + + protected abstract login(): Promise; + + async getCredentials(): Promise<{ + secretKey: string; + email: string; + }> { + const loginResult = await this.login(); + const privateKey = loginResult.finalKeyData.privKey || loginResult.oAuthKeyData.privKey; + const secretKey = b58cencode(privateKey, prefix[Prefix.SPSK]); + + const email = Array.isArray(loginResult.userInfo) + ? loginResult.userInfo[0].email + : loginResult.userInfo.email; + + return { secretKey, email }; + } +} + +export class GoogleAuth extends Auth { + override async login() { + const client = await this.getTorusClient(); + + return await client.triggerAggregateLogin({ + verifierIdentifier: "tezos-google", + aggregateVerifierType: "single_id_verifier", + subVerifierDetailsArray: [ + { + clientId: GOOGLE_SUB_VERIFIER_CLIENT_ID, + typeOfLogin: "google", + verifier: "umami", + }, + ], + }); + } +} + +export class EmailAuth extends Auth { + override async login() { + const client = await this.getTorusClient(); + + return await client.triggerAggregateLogin({ + verifierIdentifier: "tezos-google", + aggregateVerifierType: "single_id_verifier", + subVerifierDetailsArray: [ + { + verifier: "web-kukai-email", + typeOfLogin: "jwt", + clientId: EMAIL_SUB_VERIFIER_CLIENT_ID, + jwtParams: { + connection: "", + verifierIdField: "name", + domain: "https://kukai.eu.auth0.com", + }, + }, + ], + }); + } +} diff --git a/src/auth/parseTorusRedirectParams.test.ts b/src/auth/parseTorusRedirectParams.test.ts new file mode 100644 index 0000000000..b7f1a2b4f8 --- /dev/null +++ b/src/auth/parseTorusRedirectParams.test.ts @@ -0,0 +1,34 @@ +import { parseTorusRedirectParams } from "."; + +// we get URL params in a compressed format generated by zipurl +const googleAuthDeeplink = + "umami://auth/channel=redirect_channel_zorelm9guc&instanceId=zorelm9guc&verifier=umami-email&typeOfLogin=jwt&redirectToOpener=true&access_token=eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwiaXNzIjoiaHR0cHM6Ly9rdWthaS5ldS5hdXRoMC5jb20vIn0..Isiag-vr2cBKB29w.hKsMfMUA835G7-kcF60tDo8UZW6Hbkjj9iwUEyqg-2bBMZTiG2DJTq8wNsPFjP2eE_bmjrNw7KI6dLv-LMAeocvWtVCJ13NhuPqZCcZGoD0rlVSV9RPSLaUmv47Gxi08zzZjvCmUntYCSQoPMu4XFyIpauHkQwRqmpM7fdlYPIj7KSeU2opa_rkurlggBFBQ70PSZpyYvTWYexsjeMYUr5YkZSlg3553SbkDOCjkmTg0FEeAXZhBQEBziAE9Gy4bawOG0YoedTAnpHEI3qoNg4z0m26dudIMVbelNjW8s6eWUqUK.o2yHQOTA9RFM7lj5L_fylA&scope=openid%2520profile%2520email&expires_in=7200&token_type=Bearer&state=eyJpbnN0YW5jZUlkIjoiem9yZWxtOWd1YyIsInZlcmlmaWVyIjoidW1hbWktZW1haWwiLCJ0eXBlT2ZMb2dpbiI6Imp3dCIsInJlZGlyZWN0VG9PcGVuZXIiOnRydWV9&id_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InFsZ25uU0F6SWE1SkFRTkdud2dEbCJ9.eyJuaWNrbmFtZSI6InNlcmpvbnlhIiwibmFtZSI6InNlcmpvbnlhQGdtYWlsLmNvbSIsInBpY3R1cmUiOiJodHRwczovL3MuZ3JhdmF0YXIuY29tL2F2YXRhci8yODVhZWQ0MGQ4ZWRjZWNjN2VkNDZiODFiZTQwMjhhYT9zPTQ4MCZyPXBnJmQ9aHR0cHMlM0ElMkYlMkZjZG4uYXV0aDAuY29tJTJGYXZhdGFycyUyRnNlLnBuZyIsInVwZGF0ZWRfYXQiOiIyMDI0LTA1LTE5VDIyOjQwOjUwLjg4M1oiLCJlbWFpbCI6InNlcmpvbnlhQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpc3MiOiJodHRwczovL2t1a2FpLmV1LmF1dGgwLmNvbS8iLCJhdWQiOiJMVGc2ZlZzYWNhZkdtaHYxNFRabHJXRjFFYXZ3UW9EWiIsImlhdCI6MTcxNjE1ODQ1MSwiZXhwIjoxNzE2MTk0NDUxLCJzdWIiOiJlbWFpbHw2NjRhMzVlNjk0MTU2YzgxNmU5MzRjZDciLCJhdXRoX3RpbWUiOjE3MTYxNTg0NTAsImF0X2hhc2giOiJtQkRMR1drNGxRcnJMeE1pRnFPczRBIiwic2lkIjoiRnI1UkZuTmF0TWw2azA1YlFFUGNwZk1TVmdYU2R4MjUiLCJub25jZSI6InpvcmVsbTlndWMifQ.IaSqav42wd980FvjgWFY0HJf93wfwScnor7ocDV1BSLjYAmsL8cRJDvWs_rzPiduaQqQFTcbUSWY6hqMawY7-oPL5-RkEeqcub8M6UxRU_z_NTWYToZLIszblNbRoK0dybFc3Kq9D-dVsJhwVYmp9VzycD0DbDvQeBwbuA3GB1OxMhiB7n6__APCLSfrDSWQF4_uzg636pEtGMHboxJf8h_CpEYgnpHvMkOVAJjDq3UmPJgUB8-fFkyO2x_Q9E8Sbe-3Wp2F9M3QQ-MGxZiBMPTi0eemPoFySZId9yUGaAWYb2xYv8Ysk2b0Cu3XUcr1awibDjJRvgoJ_AhNmH330Q&full_params=true"; + +test("parseTorusRedirectParams", () => { + expect(parseTorusRedirectParams(googleAuthDeeplink)).toEqual({ + channel: "redirect_channel_zorelm9guc", + data: { + hashParams: { + access_token: + "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwiaXNzIjoiaHR0cHM6Ly9rdWthaS5ldS5hdXRoMC5jb20vIn0..Isiag-vr2cBKB29w.hKsMfMUA835G7-kcF60tDo8UZW6Hbkjj9iwUEyqg-2bBMZTiG2DJTq8wNsPFjP2eE_bmjrNw7KI6dLv-LMAeocvWtVCJ13NhuPqZCcZGoD0rlVSV9RPSLaUmv47Gxi08zzZjvCmUntYCSQoPMu4XFyIpauHkQwRqmpM7fdlYPIj7KSeU2opa_rkurlggBFBQ70PSZpyYvTWYexsjeMYUr5YkZSlg3553SbkDOCjkmTg0FEeAXZhBQEBziAE9Gy4bawOG0YoedTAnpHEI3qoNg4z0m26dudIMVbelNjW8s6eWUqUK.o2yHQOTA9RFM7lj5L_fylA", + authuser: null, + expires_in: "7200", + hd: null, + id_token: + "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InFsZ25uU0F6SWE1SkFRTkdud2dEbCJ9.eyJuaWNrbmFtZSI6InNlcmpvbnlhIiwibmFtZSI6InNlcmpvbnlhQGdtYWlsLmNvbSIsInBpY3R1cmUiOiJodHRwczovL3MuZ3JhdmF0YXIuY29tL2F2YXRhci8yODVhZWQ0MGQ4ZWRjZWNjN2VkNDZiODFiZTQwMjhhYT9zPTQ4MCZyPXBnJmQ9aHR0cHMlM0ElMkYlMkZjZG4uYXV0aDAuY29tJTJGYXZhdGFycyUyRnNlLnBuZyIsInVwZGF0ZWRfYXQiOiIyMDI0LTA1LTE5VDIyOjQwOjUwLjg4M1oiLCJlbWFpbCI6InNlcmpvbnlhQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpc3MiOiJodHRwczovL2t1a2FpLmV1LmF1dGgwLmNvbS8iLCJhdWQiOiJMVGc2ZlZzYWNhZkdtaHYxNFRabHJXRjFFYXZ3UW9EWiIsImlhdCI6MTcxNjE1ODQ1MSwiZXhwIjoxNzE2MTk0NDUxLCJzdWIiOiJlbWFpbHw2NjRhMzVlNjk0MTU2YzgxNmU5MzRjZDciLCJhdXRoX3RpbWUiOjE3MTYxNTg0NTAsImF0X2hhc2giOiJtQkRMR1drNGxRcnJMeE1pRnFPczRBIiwic2lkIjoiRnI1UkZuTmF0TWw2azA1YlFFUGNwZk1TVmdYU2R4MjUiLCJub25jZSI6InpvcmVsbTlndWMifQ.IaSqav42wd980FvjgWFY0HJf93wfwScnor7ocDV1BSLjYAmsL8cRJDvWs_rzPiduaQqQFTcbUSWY6hqMawY7-oPL5-RkEeqcub8M6UxRU_z_NTWYToZLIszblNbRoK0dybFc3Kq9D-dVsJhwVYmp9VzycD0DbDvQeBwbuA3GB1OxMhiB7n6__APCLSfrDSWQF4_uzg636pEtGMHboxJf8h_CpEYgnpHvMkOVAJjDq3UmPJgUB8-fFkyO2x_Q9E8Sbe-3Wp2F9M3QQ-MGxZiBMPTi0eemPoFySZId9yUGaAWYb2xYv8Ysk2b0Cu3XUcr1awibDjJRvgoJ_AhNmH330Q", + prompt: null, + scope: "openid%20profile%20email", + state: + "eyJpbnN0YW5jZUlkIjoiem9yZWxtOWd1YyIsInZlcmlmaWVyIjoidW1hbWktZW1haWwiLCJ0eXBlT2ZMb2dpbiI6Imp3dCIsInJlZGlyZWN0VG9PcGVuZXIiOnRydWV9", + token_type: "Bearer", + }, + instanceParams: { + instanceId: "zorelm9guc", + redirectToOpener: "true", + typeOfLogin: "jwt", + verifier: "umami-email", + }, + }, + error: null, + }); +}); diff --git a/src/auth/parseTorusRedirectParams.ts b/src/auth/parseTorusRedirectParams.ts new file mode 100644 index 0000000000..cb6cd7c4da --- /dev/null +++ b/src/auth/parseTorusRedirectParams.ts @@ -0,0 +1,40 @@ +/** + * This function parses the deeplink url we get on a successful social auth. + * + * The parameters are built by + * https://github.com/trilitech/umami-web/blob/main/public/auth/v2.x/redirect.html + * + * which is essentially a copy of + * https://github.com/torusresearch/CustomAuth/blob/master/serviceworker/redirect.html + */ + +export const parseTorusRedirectParams = (url: string) => { + const params = new URLSearchParams(url.replace("umami://auth/", "")); + const instanceParams = { + instanceId: params.get("instanceId"), + verifier: params.get("verifier"), + typeOfLogin: params.get("typeOfLogin"), + redirectToOpener: params.get("redirectToOpener"), + }; + + const hashParams = { + state: params.get("state"), + access_token: params.get("access_token"), + token_type: params.get("token_type"), + expires_in: params.get("expires_in"), + scope: params.get("scope"), + id_token: params.get("id_token"), + authuser: params.get("authuser"), + hd: params.get("hd"), + prompt: params.get("prompt"), + }; + + const data = { instanceParams, hashParams }; + const result = { + channel: params.get("channel"), + data: data, + error: params.get("error"), + }; + + return result; +}; diff --git a/src/auth/types.ts b/src/auth/types.ts new file mode 100644 index 0000000000..f74179123c --- /dev/null +++ b/src/auth/types.ts @@ -0,0 +1 @@ +export type IDP = "google" | "email"; diff --git a/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx b/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx index 48266c0a52..7293932a07 100644 --- a/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx +++ b/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx @@ -1,8 +1,8 @@ import { Button, Divider, Flex, Text, VStack, useToast } from "@chakra-ui/react"; import { WalletPlusIcon } from "../../../assets/icons"; +import { LoginButton } from "../../../auth/LoginButton"; import { IS_DEV } from "../../../env"; -import { GoogleAuth } from "../../../GoogleAuth"; import colors from "../../../style/colors"; import { useRestoreSocial } from "../../../utils/hooks/setAccountDataHooks"; import { useAsyncActionHandler } from "../../../utils/hooks/useAsyncActionHandler"; @@ -21,9 +21,9 @@ export const ConnectOrCreate = ({ const restoreSocial = useRestoreSocial(); const toast = useToast(); - const onSocialAuth = (sk: string, email: string) => + const onSocialAuth = ({ secretKey, email }: { secretKey: string; email: string }) => handleAsyncAction(async () => { - const { pk, pkh } = await getPublicKeyPairFromSk(sk); + const { pk, pkh } = await getPublicKeyPairFromSk(secretKey); restoreSocial(pk, pkh, email); toast({ description: `Successfully added ${email} account`, status: "success" }); closeModal(); @@ -66,7 +66,10 @@ export const ConnectOrCreate = ({ - + + + + ); diff --git a/src/components/SendFlow/SignButton.tsx b/src/components/SendFlow/SignButton.tsx index 8698c3745f..253ec9e3e5 100644 --- a/src/components/SendFlow/SignButton.tsx +++ b/src/components/SendFlow/SignButton.tsx @@ -4,12 +4,13 @@ import type { BatchWalletOperation } from "@taquito/taquito/dist/types/wallet/ba import React from "react"; import { FormProvider, useForm } from "react-hook-form"; -import { getGoogleCredentials } from "../../GoogleAuth"; +import { Auth } from "../../auth"; import { ImplicitAccount, LedgerAccount, MnemonicAccount, SecretKeyAccount, + SocialAccount, } from "../../types/Account"; import { Network } from "../../types/Network"; import { useGetSecretKey } from "../../utils/hooks/getAccountDataHooks"; @@ -63,7 +64,7 @@ export const SignButton: React.FC<{ const onSocialSign = async () => handleAsyncAction(async () => { - const { secretKey } = await getGoogleCredentials(); + const { secretKey } = await Auth.for((signer as SocialAccount).idp).getCredentials(); return onSubmit(await makeToolkit({ type: "social", secretKey, network })); }); diff --git a/src/types/Account.ts b/src/types/Account.ts index 74c5e91cb6..dbb4f09162 100644 --- a/src/types/Account.ts +++ b/src/types/Account.ts @@ -6,7 +6,7 @@ import { Multisig } from "../utils/multisig/types"; export type SocialAccount = { label: string; type: "social"; - idp: "google"; + idp: "google" | "email"; address: ImplicitAddress; pk: string; }; diff --git a/src/utils/useDeeplinkHandler.ts b/src/utils/useDeeplinkHandler.ts index 4bcf14c0f6..5e95ce0bfe 100644 --- a/src/utils/useDeeplinkHandler.ts +++ b/src/utils/useDeeplinkHandler.ts @@ -2,7 +2,7 @@ import { useToast } from "@chakra-ui/react"; import { useEffect, useRef } from "react"; import { useAddPeer } from "./beacon/beacon"; -import { parseTorusRedirectParams } from "../GoogleAuth"; +import { parseTorusRedirectParams } from "../auth"; export const useDeeplinkHandler = () => { const toast = useToast(); From 99236ca8c1075aa301e096cf317dd37982bcf507 Mon Sep 17 00:00:00 2001 From: Sergey Kintsel Date: Mon, 20 May 2024 13:17:20 +0100 Subject: [PATCH 02/18] Improve electron app debugging experience --- package.json | 2 +- vite.config.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 14d72fd409..986b01d6f9 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "preview": "vite preview", "electron:package:linux": "electron-builder -l", "electron:package:mac": "electron-builder -m", - "electron:package:mac:debug": "yarn electron:package:mac -c electron-builder.dev.yml", + "electron:package:mac:debug": "DEBUG=true yarn build && sed -i='' 's@devTools: false@devTools: true@g' build/electron.js && yarn electron:package:mac -c electron-builder.dev.yml && rm -rf build", "electron:package:win": "electron-builder -w", "electron:start": "electronmon .", "docs": "typedoc --tsconfig tsconfig.e2e.json", diff --git a/vite.config.ts b/vite.config.ts index fcae5a1dae..26858c0f37 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,3 +1,5 @@ +import process from "process"; + import react from "@vitejs/plugin-react"; import { defineConfig } from "vite"; import { checker } from "vite-plugin-checker"; @@ -18,6 +20,7 @@ export default defineConfig({ }), react(), checker({ + enableBuild: false, overlay: false, terminal: true, typescript: true, @@ -30,5 +33,6 @@ export default defineConfig({ build: { outDir: "build", chunkSizeWarningLimit: 5 * 1024, // 5MB + sourcemap: process.env.DEBUG === "true", }, }); From ac35f65b475334cefc8ec4226aaf5068ac2e52b1 Mon Sep 17 00:00:00 2001 From: Sergey Kintsel Date: Mon, 20 May 2024 13:31:09 +0100 Subject: [PATCH 03/18] Add Reddit authentication --- src/assets/icons/Reddit.tsx | 21 ++++++ src/assets/icons/index.tsx | 1 + src/auth/EmailAuth.ts | 27 ++++++++ src/auth/GoogleAuth.ts | 22 +++++++ src/auth/LoginButton.tsx | 25 ++++++- src/auth/RedditAuth.ts | 26 ++++++++ src/auth/constants.ts | 1 + src/auth/index.ts | 66 +++++-------------- src/auth/types.ts | 2 +- .../connectOrCreate/ConnectOrCreate.tsx | 7 +- 10 files changed, 142 insertions(+), 56 deletions(-) create mode 100644 src/assets/icons/Reddit.tsx create mode 100644 src/auth/EmailAuth.ts create mode 100644 src/auth/GoogleAuth.ts create mode 100644 src/auth/RedditAuth.ts create mode 100644 src/auth/constants.ts diff --git a/src/assets/icons/Reddit.tsx b/src/assets/icons/Reddit.tsx new file mode 100644 index 0000000000..2a67de94ce --- /dev/null +++ b/src/assets/icons/Reddit.tsx @@ -0,0 +1,21 @@ +import { Icon, IconProps } from "@chakra-ui/react"; + +export const RedditIcon: React.FC = props => ( + + + + +); diff --git a/src/assets/icons/index.tsx b/src/assets/icons/index.tsx index 5d28987d64..7e3a77389e 100644 --- a/src/assets/icons/index.tsx +++ b/src/assets/icons/index.tsx @@ -48,6 +48,7 @@ export * from "./OutgoingArrow"; export * from "./OutlineExclamationCircle"; export * from "./Pen"; export * from "./Plus"; +export * from "./Reddit"; export * from "./RefreshClock"; export * from "./Reload"; export * from "./Rotate"; diff --git a/src/auth/EmailAuth.ts b/src/auth/EmailAuth.ts new file mode 100644 index 0000000000..f6e188f02e --- /dev/null +++ b/src/auth/EmailAuth.ts @@ -0,0 +1,27 @@ +import { Auth, IDP, JWT_AUTH_DOMAIN } from "."; + +export class EmailAuth extends Auth { + clientId = "LTg6fVsacafGmhv14TZlrWF1EavwQoDZ"; + idpName: IDP = "email"; + + protected override async login() { + const client = await this.getTorusClient(); + + return client.triggerAggregateLogin({ + verifierIdentifier: "tezos-google", + aggregateVerifierType: "single_id_verifier", + subVerifierDetailsArray: [ + { + verifier: "web-kukai-email", + typeOfLogin: "jwt", + clientId: this.clientId, + jwtParams: { + connection: "", + verifierIdField: "name", + domain: JWT_AUTH_DOMAIN, + }, + }, + ], + }); + } +} diff --git a/src/auth/GoogleAuth.ts b/src/auth/GoogleAuth.ts new file mode 100644 index 0000000000..9808c9060f --- /dev/null +++ b/src/auth/GoogleAuth.ts @@ -0,0 +1,22 @@ +import { Auth, IDP } from "."; + +export class GoogleAuth extends Auth { + idpName: IDP = "google"; + clientId = "1070572364808-d31nlkneam5ee6dr0tu28fjjbsdkfta5.apps.googleusercontent.com"; + + protected override async login() { + const client = await this.getTorusClient(); + + return client.triggerAggregateLogin({ + verifierIdentifier: "tezos-google", + aggregateVerifierType: "single_id_verifier", + subVerifierDetailsArray: [ + { + clientId: this.clientId, + typeOfLogin: "google", + verifier: "umami", + }, + ], + }); + } +} diff --git a/src/auth/LoginButton.tsx b/src/auth/LoginButton.tsx index 2e60409a70..c87fc538cc 100644 --- a/src/auth/LoginButton.tsx +++ b/src/auth/LoginButton.tsx @@ -1,7 +1,7 @@ import { IconButton } from "@chakra-ui/react"; import { minutesToMilliseconds } from "date-fns"; -import { EnvelopeIcon, GoogleIcon } from "../assets/icons"; +import { EnvelopeIcon, GoogleIcon, RedditIcon } from "../assets/icons"; import colors from "../style/colors"; import { useAsyncActionHandler } from "../utils/hooks/useAsyncActionHandler"; import { withTimeout } from "../utils/withTimeout"; @@ -20,7 +20,7 @@ const LOGIN_TIMEOUT = minutesToMilliseconds(1); */ export const LoginButton: React.FC<{ idp: IDP; - onAuth: ({ secretKey, email }: { secretKey: string; email: string }) => void; + onAuth: ({ secretKey, name }: { secretKey: string; name: string }) => void; }> = ({ idp, onAuth }) => { const { isLoading, handleAsyncAction } = useAsyncActionHandler(); @@ -35,6 +35,8 @@ export const LoginButton: React.FC<{ return ; case "email": return ; + case "reddit": + return ; } }; @@ -75,3 +77,22 @@ const EmailButton: React.FC<{ isLoading: boolean; onClick: () => void }> = ({ variant="outline" /> ); + +const RedditButton: React.FC<{ isLoading: boolean; onClick: () => void }> = ({ + isLoading, + onClick, +}) => ( + } + isLoading={isLoading} + onClick={onClick} + size="lg" + variant="outline" + /> +); diff --git a/src/auth/RedditAuth.ts b/src/auth/RedditAuth.ts new file mode 100644 index 0000000000..74071ad525 --- /dev/null +++ b/src/auth/RedditAuth.ts @@ -0,0 +1,26 @@ +import { Auth, IDP, JWT_AUTH_DOMAIN } from "."; + +export class RedditAuth extends Auth { + idpName: IDP = "reddit"; + clientId = "zyQ9tnKfdg3VNyj6MGhZq4dHbBzbmEvl"; + protected override async login() { + const client = await this.getTorusClient(); + + return client.triggerAggregateLogin({ + verifierIdentifier: "tezos-reddit", + aggregateVerifierType: "single_id_verifier", + subVerifierDetailsArray: [ + { + verifier: "web-kukai", + typeOfLogin: "jwt", + clientId: this.clientId, + jwtParams: { + connection: "Reddit", + verifierIdField: "name", + domain: JWT_AUTH_DOMAIN, + }, + }, + ], + }); + } +} diff --git a/src/auth/constants.ts b/src/auth/constants.ts new file mode 100644 index 0000000000..4d43c38e52 --- /dev/null +++ b/src/auth/constants.ts @@ -0,0 +1 @@ +export const JWT_AUTH_DOMAIN = "https://kukai.eu.auth0.com"; diff --git a/src/auth/index.ts b/src/auth/index.ts index c16ee0d10f..0e1683d653 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -1,23 +1,28 @@ import { Prefix, b58cencode, prefix } from "@taquito/utils"; import CustomAuth, { TorusAggregateLoginResponse, TorusLoginResponse } from "@toruslabs/customauth"; -import { IDP } from "./types"; +import { EmailAuth } from "./EmailAuth"; +import { GoogleAuth } from "./GoogleAuth"; +import { RedditAuth } from "./RedditAuth"; +import type { IDP } from "./types"; export * from "./parseTorusRedirectParams"; export * from "./types"; +export * from "./constants"; const WEB3_AUTH_CLIENT_ID = "BBQoFIabI50S1-0QsGHGTM4qID_FDjja0ZxIxKPyFqc0El--M-EG0c2giaBYVTVVE6RC9WCUzCJyW24aJrR_Lzc"; -const GOOGLE_SUB_VERIFIER_CLIENT_ID = - "1070572364808-d31nlkneam5ee6dr0tu28fjjbsdkfta5.apps.googleusercontent.com"; -const EMAIL_SUB_VERIFIER_CLIENT_ID = "LTg6fVsacafGmhv14TZlrWF1EavwQoDZ"; - export abstract class Auth { + abstract idpName: IDP; + abstract clientId: string; + static for(idp: IDP) { switch (idp) { case "google": return new GoogleAuth(); case "email": return new EmailAuth(); + case "reddit": + return new RedditAuth(); } } @@ -40,57 +45,18 @@ export abstract class Auth { async getCredentials(): Promise<{ secretKey: string; - email: string; + name: string; }> { const loginResult = await this.login(); const privateKey = loginResult.finalKeyData.privKey || loginResult.oAuthKeyData.privKey; const secretKey = b58cencode(privateKey, prefix[Prefix.SPSK]); - const email = Array.isArray(loginResult.userInfo) - ? loginResult.userInfo[0].email - : loginResult.userInfo.email; - - return { secretKey, email }; - } -} + const userInfo = Array.isArray(loginResult.userInfo) + ? loginResult.userInfo[0] + : loginResult.userInfo; -export class GoogleAuth extends Auth { - override async login() { - const client = await this.getTorusClient(); + const accountName = userInfo.email || userInfo.name || this.idpName; - return await client.triggerAggregateLogin({ - verifierIdentifier: "tezos-google", - aggregateVerifierType: "single_id_verifier", - subVerifierDetailsArray: [ - { - clientId: GOOGLE_SUB_VERIFIER_CLIENT_ID, - typeOfLogin: "google", - verifier: "umami", - }, - ], - }); - } -} - -export class EmailAuth extends Auth { - override async login() { - const client = await this.getTorusClient(); - - return await client.triggerAggregateLogin({ - verifierIdentifier: "tezos-google", - aggregateVerifierType: "single_id_verifier", - subVerifierDetailsArray: [ - { - verifier: "web-kukai-email", - typeOfLogin: "jwt", - clientId: EMAIL_SUB_VERIFIER_CLIENT_ID, - jwtParams: { - connection: "", - verifierIdField: "name", - domain: "https://kukai.eu.auth0.com", - }, - }, - ], - }); + return { secretKey, name: accountName }; } } diff --git a/src/auth/types.ts b/src/auth/types.ts index f74179123c..c8b4c5e14e 100644 --- a/src/auth/types.ts +++ b/src/auth/types.ts @@ -1 +1 @@ -export type IDP = "google" | "email"; +export type IDP = "google" | "email" | "reddit"; diff --git a/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx b/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx index 7293932a07..af19600d60 100644 --- a/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx +++ b/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx @@ -21,11 +21,11 @@ export const ConnectOrCreate = ({ const restoreSocial = useRestoreSocial(); const toast = useToast(); - const onSocialAuth = ({ secretKey, email }: { secretKey: string; email: string }) => + const onSocialAuth = ({ secretKey, name }: { secretKey: string; name: string }) => handleAsyncAction(async () => { const { pk, pkh } = await getPublicKeyPairFromSk(secretKey); - restoreSocial(pk, pkh, email); - toast({ description: `Successfully added ${email} account`, status: "success" }); + restoreSocial(pk, pkh, name); + toast({ description: `Successfully added ${name} account`, status: "success" }); closeModal(); }); @@ -69,6 +69,7 @@ export const ConnectOrCreate = ({ + From cbc8dbc913ffc80cb61678d5f2ba52214a091dcd Mon Sep 17 00:00:00 2001 From: Sergey Kintsel Date: Mon, 20 May 2024 14:03:03 +0100 Subject: [PATCH 04/18] Improve auth code structure --- src/auth/Auth.ts | 46 +++++++++++++++++++ src/auth/EmailAuth.ts | 4 +- src/auth/GoogleAuth.ts | 3 +- src/auth/LoginButton.tsx | 6 +-- src/auth/RedditAuth.ts | 4 +- src/auth/forIDP.ts | 15 +++++++ src/auth/index.ts | 61 +------------------------- src/components/SendFlow/SignButton.tsx | 4 +- 8 files changed, 76 insertions(+), 67 deletions(-) create mode 100644 src/auth/Auth.ts create mode 100644 src/auth/forIDP.ts diff --git a/src/auth/Auth.ts b/src/auth/Auth.ts new file mode 100644 index 0000000000..23096d5319 --- /dev/null +++ b/src/auth/Auth.ts @@ -0,0 +1,46 @@ +import { Prefix, b58cencode, prefix } from "@taquito/utils"; +import CustomAuth, { TorusAggregateLoginResponse, TorusLoginResponse } from "@toruslabs/customauth"; + +import { IDP } from "./types"; + +const WEB3_AUTH_CLIENT_ID = + "BBQoFIabI50S1-0QsGHGTM4qID_FDjja0ZxIxKPyFqc0El--M-EG0c2giaBYVTVVE6RC9WCUzCJyW24aJrR_Lzc"; + +export abstract class Auth { + abstract idpName: IDP; + abstract clientId: string; + + protected async getTorusClient(): Promise { + const torus = new CustomAuth({ + web3AuthClientId: WEB3_AUTH_CLIENT_ID, + baseUrl: "https://umamiwallet.com/auth/v2.0.1/", + redirectPathName: "redirect.html?full_params=true", // TODO: fix this once we have a proper redirect page + redirectToOpener: true, + uxMode: "popup", + network: "mainnet", + }); + + await torus.init({ skipSw: true }); + + return torus; + } + + protected abstract login(): Promise; + + async getCredentials(): Promise<{ + secretKey: string; + name: string; + }> { + const loginResult = await this.login(); + const privateKey = loginResult.finalKeyData.privKey || loginResult.oAuthKeyData.privKey; + const secretKey = b58cencode(privateKey, prefix[Prefix.SPSK]); + + const userInfo = Array.isArray(loginResult.userInfo) + ? loginResult.userInfo[0] + : loginResult.userInfo; + + const accountName = userInfo.email || userInfo.name || this.idpName; + + return { secretKey, name: accountName }; + } +} diff --git a/src/auth/EmailAuth.ts b/src/auth/EmailAuth.ts index f6e188f02e..c6df7a878a 100644 --- a/src/auth/EmailAuth.ts +++ b/src/auth/EmailAuth.ts @@ -1,4 +1,6 @@ -import { Auth, IDP, JWT_AUTH_DOMAIN } from "."; +import { Auth } from "./Auth"; +import { JWT_AUTH_DOMAIN } from "./constants"; +import type { IDP } from "./types"; export class EmailAuth extends Auth { clientId = "LTg6fVsacafGmhv14TZlrWF1EavwQoDZ"; diff --git a/src/auth/GoogleAuth.ts b/src/auth/GoogleAuth.ts index 9808c9060f..ea39dc51e9 100644 --- a/src/auth/GoogleAuth.ts +++ b/src/auth/GoogleAuth.ts @@ -1,4 +1,5 @@ -import { Auth, IDP } from "."; +import { Auth } from "./Auth"; +import type { IDP } from "./types"; export class GoogleAuth extends Auth { idpName: IDP = "google"; diff --git a/src/auth/LoginButton.tsx b/src/auth/LoginButton.tsx index c87fc538cc..1d20c01ea1 100644 --- a/src/auth/LoginButton.tsx +++ b/src/auth/LoginButton.tsx @@ -1,13 +1,13 @@ import { IconButton } from "@chakra-ui/react"; import { minutesToMilliseconds } from "date-fns"; +import * as Auth from "./forIDP"; +import type { IDP } from "./types"; import { EnvelopeIcon, GoogleIcon, RedditIcon } from "../assets/icons"; import colors from "../style/colors"; import { useAsyncActionHandler } from "../utils/hooks/useAsyncActionHandler"; import { withTimeout } from "../utils/withTimeout"; -import { Auth, IDP } from "."; - const LOGIN_TIMEOUT = minutesToMilliseconds(1); /** @@ -26,7 +26,7 @@ export const LoginButton: React.FC<{ const onClick = () => handleAsyncAction( - () => withTimeout(() => Auth.for(idp).getCredentials().then(onAuth), LOGIN_TIMEOUT), + () => withTimeout(() => Auth.forIDP(idp).getCredentials().then(onAuth), LOGIN_TIMEOUT), { title: "Social login failed" } ); diff --git a/src/auth/RedditAuth.ts b/src/auth/RedditAuth.ts index 74071ad525..f4b485f487 100644 --- a/src/auth/RedditAuth.ts +++ b/src/auth/RedditAuth.ts @@ -1,4 +1,6 @@ -import { Auth, IDP, JWT_AUTH_DOMAIN } from "."; +import { Auth } from "./Auth"; +import { JWT_AUTH_DOMAIN } from "./constants"; +import type { IDP } from "./types"; export class RedditAuth extends Auth { idpName: IDP = "reddit"; diff --git a/src/auth/forIDP.ts b/src/auth/forIDP.ts new file mode 100644 index 0000000000..e87c88c39f --- /dev/null +++ b/src/auth/forIDP.ts @@ -0,0 +1,15 @@ +import { EmailAuth } from "./EmailAuth"; +import { GoogleAuth } from "./GoogleAuth"; +import { RedditAuth } from "./RedditAuth"; +import type { IDP } from "./types"; + +export const forIDP = (idp: IDP) => { + switch (idp) { + case "google": + return new GoogleAuth(); + case "email": + return new EmailAuth(); + case "reddit": + return new RedditAuth(); + } +}; diff --git a/src/auth/index.ts b/src/auth/index.ts index 0e1683d653..2c6796d3b6 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -1,62 +1,5 @@ -import { Prefix, b58cencode, prefix } from "@taquito/utils"; -import CustomAuth, { TorusAggregateLoginResponse, TorusLoginResponse } from "@toruslabs/customauth"; - -import { EmailAuth } from "./EmailAuth"; -import { GoogleAuth } from "./GoogleAuth"; -import { RedditAuth } from "./RedditAuth"; -import type { IDP } from "./types"; +export * from "./Auth"; export * from "./parseTorusRedirectParams"; export * from "./types"; export * from "./constants"; - -const WEB3_AUTH_CLIENT_ID = - "BBQoFIabI50S1-0QsGHGTM4qID_FDjja0ZxIxKPyFqc0El--M-EG0c2giaBYVTVVE6RC9WCUzCJyW24aJrR_Lzc"; -export abstract class Auth { - abstract idpName: IDP; - abstract clientId: string; - - static for(idp: IDP) { - switch (idp) { - case "google": - return new GoogleAuth(); - case "email": - return new EmailAuth(); - case "reddit": - return new RedditAuth(); - } - } - - protected async getTorusClient(): Promise { - const torus = new CustomAuth({ - web3AuthClientId: WEB3_AUTH_CLIENT_ID, - baseUrl: "https://umamiwallet.com/auth/v2.0.1/", - redirectPathName: "redirect.html?full_params=true", // TODO: fix this once we have a proper redirect page - redirectToOpener: true, - uxMode: "popup", - network: "mainnet", - }); - - await torus.init({ skipSw: true }); - - return torus; - } - - protected abstract login(): Promise; - - async getCredentials(): Promise<{ - secretKey: string; - name: string; - }> { - const loginResult = await this.login(); - const privateKey = loginResult.finalKeyData.privKey || loginResult.oAuthKeyData.privKey; - const secretKey = b58cencode(privateKey, prefix[Prefix.SPSK]); - - const userInfo = Array.isArray(loginResult.userInfo) - ? loginResult.userInfo[0] - : loginResult.userInfo; - - const accountName = userInfo.email || userInfo.name || this.idpName; - - return { secretKey, name: accountName }; - } -} +export * from "./forIDP"; diff --git a/src/components/SendFlow/SignButton.tsx b/src/components/SendFlow/SignButton.tsx index 253ec9e3e5..8334d97dc3 100644 --- a/src/components/SendFlow/SignButton.tsx +++ b/src/components/SendFlow/SignButton.tsx @@ -4,7 +4,7 @@ import type { BatchWalletOperation } from "@taquito/taquito/dist/types/wallet/ba import React from "react"; import { FormProvider, useForm } from "react-hook-form"; -import { Auth } from "../../auth"; +import * as Auth from "../../auth"; import { ImplicitAccount, LedgerAccount, @@ -64,7 +64,7 @@ export const SignButton: React.FC<{ const onSocialSign = async () => handleAsyncAction(async () => { - const { secretKey } = await Auth.for((signer as SocialAccount).idp).getCredentials(); + const { secretKey } = await Auth.forIDP((signer as SocialAccount).idp).getCredentials(); return onSubmit(await makeToolkit({ type: "social", secretKey, network })); }); From 8e3b13867fdbacf286c9d76605ef5516e9ce1a61 Mon Sep 17 00:00:00 2001 From: Sergey Kintsel Date: Mon, 20 May 2024 17:29:56 +0100 Subject: [PATCH 05/18] Move social login buttons into onboarding flow --- src/auth/Auth.ts | 4 +- src/auth/LoginButton.tsx | 98 ------------------- .../connectOrCreate/ConnectOrCreate.tsx | 95 +++++++----------- .../OnboardWithEmailButton.tsx | 25 +++++ .../OnboardWithGoogleButton.tsx | 25 +++++ .../OnboardWithRedditButton.tsx | 25 +++++ .../connectOrCreate/useOnboardWithSocial.tsx | 44 +++++++++ src/env.ts | 2 +- src/types/Account.ts | 3 +- src/utils/hooks/setAccountDataHooks.ts | 27 +++-- .../{helper.test.ts => helpers.test.ts} | 0 11 files changed, 178 insertions(+), 170 deletions(-) delete mode 100644 src/auth/LoginButton.tsx create mode 100644 src/components/Onboarding/connectOrCreate/OnboardWithEmailButton.tsx create mode 100644 src/components/Onboarding/connectOrCreate/OnboardWithGoogleButton.tsx create mode 100644 src/components/Onboarding/connectOrCreate/OnboardWithRedditButton.tsx create mode 100644 src/components/Onboarding/connectOrCreate/useOnboardWithSocial.tsx rename src/utils/multisig/{helper.test.ts => helpers.test.ts} (100%) diff --git a/src/auth/Auth.ts b/src/auth/Auth.ts index 23096d5319..a812602dc3 100644 --- a/src/auth/Auth.ts +++ b/src/auth/Auth.ts @@ -1,4 +1,4 @@ -import { Prefix, b58cencode, prefix } from "@taquito/utils"; +import { b58cencode, prefix } from "@taquito/utils"; import CustomAuth, { TorusAggregateLoginResponse, TorusLoginResponse } from "@toruslabs/customauth"; import { IDP } from "./types"; @@ -33,7 +33,7 @@ export abstract class Auth { }> { const loginResult = await this.login(); const privateKey = loginResult.finalKeyData.privKey || loginResult.oAuthKeyData.privKey; - const secretKey = b58cencode(privateKey, prefix[Prefix.SPSK]); + const secretKey = b58cencode(privateKey, prefix.spsk); const userInfo = Array.isArray(loginResult.userInfo) ? loginResult.userInfo[0] diff --git a/src/auth/LoginButton.tsx b/src/auth/LoginButton.tsx deleted file mode 100644 index 1d20c01ea1..0000000000 --- a/src/auth/LoginButton.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { IconButton } from "@chakra-ui/react"; -import { minutesToMilliseconds } from "date-fns"; - -import * as Auth from "./forIDP"; -import type { IDP } from "./types"; -import { EnvelopeIcon, GoogleIcon, RedditIcon } from "../assets/icons"; -import colors from "../style/colors"; -import { useAsyncActionHandler } from "../utils/hooks/useAsyncActionHandler"; -import { withTimeout } from "../utils/withTimeout"; - -const LOGIN_TIMEOUT = minutesToMilliseconds(1); - -/** - * Component that's used for onboarding users with Google SSO. - * It's used on the onboarding page. - * It opens a popup window with Google SSO on a click. - * - * @param onAuth - callback function which is called with the secret key and email of the user - * on a successful authentication - */ -export const LoginButton: React.FC<{ - idp: IDP; - onAuth: ({ secretKey, name }: { secretKey: string; name: string }) => void; -}> = ({ idp, onAuth }) => { - const { isLoading, handleAsyncAction } = useAsyncActionHandler(); - - const onClick = () => - handleAsyncAction( - () => withTimeout(() => Auth.forIDP(idp).getCredentials().then(onAuth), LOGIN_TIMEOUT), - { title: "Social login failed" } - ); - - switch (idp) { - case "google": - return ; - case "email": - return ; - case "reddit": - return ; - } -}; - -const GoogleButton: React.FC<{ isLoading: boolean; onClick: () => void }> = ({ - isLoading, - onClick, -}) => ( - } - isLoading={isLoading} - onClick={onClick} - size="lg" - variant="outline" - /> -); - -const EmailButton: React.FC<{ isLoading: boolean; onClick: () => void }> = ({ - isLoading, - onClick, -}) => ( - } - isLoading={isLoading} - onClick={onClick} - size="lg" - variant="outline" - /> -); - -const RedditButton: React.FC<{ isLoading: boolean; onClick: () => void }> = ({ - isLoading, - onClick, -}) => ( - } - isLoading={isLoading} - onClick={onClick} - size="lg" - variant="outline" - /> -); diff --git a/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx b/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx index af19600d60..53a3dc02f4 100644 --- a/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx +++ b/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx @@ -1,12 +1,11 @@ -import { Button, Divider, Flex, Text, VStack, useToast } from "@chakra-ui/react"; +import { Button, Divider, Flex, Text, VStack } from "@chakra-ui/react"; +import { OnboardWithEmailButton } from "./OnboardWithEmailButton"; +import { OnboardWithGoogleButton } from "./OnboardWithGoogleButton"; +import { OnboardWithRedditButton } from "./OnboardWithRedditButton"; import { WalletPlusIcon } from "../../../assets/icons"; -import { LoginButton } from "../../../auth/LoginButton"; import { IS_DEV } from "../../../env"; import colors from "../../../style/colors"; -import { useRestoreSocial } from "../../../utils/hooks/setAccountDataHooks"; -import { useAsyncActionHandler } from "../../../utils/hooks/useAsyncActionHandler"; -import { getPublicKeyPairFromSk } from "../../../utils/tezos"; import { ModalContentWrapper } from "../ModalContentWrapper"; import { OnboardingStep } from "../OnboardingStep"; @@ -16,62 +15,42 @@ export const ConnectOrCreate = ({ }: { goToStep: (step: OnboardingStep) => void; closeModal: () => void; -}) => { - const { handleAsyncAction } = useAsyncActionHandler(); - const restoreSocial = useRestoreSocial(); - const toast = useToast(); - - const onSocialAuth = ({ secretKey, name }: { secretKey: string; name: string }) => - handleAsyncAction(async () => { - const { pk, pkh } = await getPublicKeyPairFromSk(secretKey); - restoreSocial(pk, pkh, name); - toast({ description: `Successfully added ${name} account`, status: "success" }); - closeModal(); - }); - - return ( - } title="Connect or Create Account"> - - +}) => ( + } title="Connect or Create Account"> + + + + {IS_DEV && ( - {IS_DEV && ( - - )} - - - - Continue with Google - - - - - - - - - - - ); -}; + )} + + + + Continue with Google + + + + + + + + + + +); diff --git a/src/components/Onboarding/connectOrCreate/OnboardWithEmailButton.tsx b/src/components/Onboarding/connectOrCreate/OnboardWithEmailButton.tsx new file mode 100644 index 0000000000..0f77532197 --- /dev/null +++ b/src/components/Onboarding/connectOrCreate/OnboardWithEmailButton.tsx @@ -0,0 +1,25 @@ +import { IconButton } from "@chakra-ui/react"; + +import { useOnboardWithSocial } from "./useOnboardWithSocial"; +import { EnvelopeIcon } from "../../../assets/icons"; +import colors from "../../../style/colors"; + +export const OnboardWithEmailButton = ({ onAuth }: { onAuth: () => void }) => { + const { isLoading, onboard } = useOnboardWithSocial("email", onAuth); + + return ( + } + isLoading={isLoading} + onClick={onboard} + size="lg" + variant="outline" + /> + ); +}; diff --git a/src/components/Onboarding/connectOrCreate/OnboardWithGoogleButton.tsx b/src/components/Onboarding/connectOrCreate/OnboardWithGoogleButton.tsx new file mode 100644 index 0000000000..f4eed4402f --- /dev/null +++ b/src/components/Onboarding/connectOrCreate/OnboardWithGoogleButton.tsx @@ -0,0 +1,25 @@ +import { IconButton } from "@chakra-ui/react"; + +import { useOnboardWithSocial } from "./useOnboardWithSocial"; +import { GoogleIcon } from "../../../assets/icons"; +import colors from "../../../style/colors"; + +export const OnboardWithGoogleButton = ({ onAuth }: { onAuth: () => void }) => { + const { isLoading, onboard } = useOnboardWithSocial("google", onAuth); + + return ( + } + isLoading={isLoading} + onClick={onboard} + size="lg" + variant="outline" + /> + ); +}; diff --git a/src/components/Onboarding/connectOrCreate/OnboardWithRedditButton.tsx b/src/components/Onboarding/connectOrCreate/OnboardWithRedditButton.tsx new file mode 100644 index 0000000000..b46c60bca6 --- /dev/null +++ b/src/components/Onboarding/connectOrCreate/OnboardWithRedditButton.tsx @@ -0,0 +1,25 @@ +import { IconButton } from "@chakra-ui/react"; + +import { useOnboardWithSocial } from "./useOnboardWithSocial"; +import { RedditIcon } from "../../../assets/icons"; +import colors from "../../../style/colors"; + +export const OnboardWithRedditButton = ({ onAuth }: { onAuth: () => void }) => { + const { isLoading, onboard } = useOnboardWithSocial("reddit", onAuth); + + return ( + } + isLoading={isLoading} + onClick={onboard} + size="lg" + variant="outline" + /> + ); +}; diff --git a/src/components/Onboarding/connectOrCreate/useOnboardWithSocial.tsx b/src/components/Onboarding/connectOrCreate/useOnboardWithSocial.tsx new file mode 100644 index 0000000000..9b1c4ff6eb --- /dev/null +++ b/src/components/Onboarding/connectOrCreate/useOnboardWithSocial.tsx @@ -0,0 +1,44 @@ +import { useToast } from "@chakra-ui/react"; +import { minutesToMilliseconds } from "date-fns"; +import { useCallback } from "react"; + +import * as Auth from "../../../auth"; +import { useRestoreSocial } from "../../../utils/hooks/setAccountDataHooks"; +import { useAsyncActionHandler } from "../../../utils/hooks/useAsyncActionHandler"; +import { getPublicKeyPairFromSk } from "../../../utils/tezos"; +import { withTimeout } from "../../../utils/withTimeout"; + +const LOGIN_TIMEOUT = minutesToMilliseconds(1); + +/** + * Hook to onboard with a social identity provider. + * Handles the authentication process and adds the account to the user's wallet. + * + * @param idp - the identity provider to use for authentication + * @param onAuth - callback which will be called on a successful authentication + */ +export const useOnboardWithSocial = (idp: Auth.IDP, onAuth: () => void) => { + const toast = useToast(); + const { isLoading, handleAsyncAction } = useAsyncActionHandler(); + const restoreSocial = useRestoreSocial(); + + const onboard = useCallback( + () => + handleAsyncAction( + async () => { + const { secretKey, name } = await withTimeout( + () => Auth.forIDP(idp).getCredentials(), + LOGIN_TIMEOUT + ); + const { pk, pkh } = await getPublicKeyPairFromSk(secretKey); + restoreSocial(pk, pkh, name, idp); + toast({ description: `Successfully added ${name} account`, status: "success" }); + onAuth(); + }, + { title: "Social login failed" } + ), + [idp, toast, handleAsyncAction, restoreSocial, onAuth] + ); + + return { isLoading, onboard }; +}; diff --git a/src/env.ts b/src/env.ts index 164e1b4d13..c95ef1abc4 100644 --- a/src/env.ts +++ b/src/env.ts @@ -1 +1 @@ -export const IS_DEV = import.meta.env.DEV; +export const IS_DEV = import.meta.env.DEV || import.meta.env.DEBUG; diff --git a/src/types/Account.ts b/src/types/Account.ts index dbb4f09162..c5229605ef 100644 --- a/src/types/Account.ts +++ b/src/types/Account.ts @@ -1,12 +1,13 @@ import { Curves } from "@taquito/signer"; import { ImplicitAddress } from "./Address"; +import { IDP } from "../auth"; import { Multisig } from "../utils/multisig/types"; export type SocialAccount = { label: string; type: "social"; - idp: "google" | "email"; + idp: IDP; address: ImplicitAddress; pk: string; }; diff --git a/src/utils/hooks/setAccountDataHooks.ts b/src/utils/hooks/setAccountDataHooks.ts index 022bc9da63..f24580ed8f 100644 --- a/src/utils/hooks/setAccountDataHooks.ts +++ b/src/utils/hooks/setAccountDataHooks.ts @@ -1,3 +1,4 @@ +import { useCallback } from "react"; import { useDispatch } from "react-redux"; import { @@ -9,6 +10,7 @@ import { import { useGetNextAvailableAccountLabels } from "./labelsHooks"; import { useSelectedNetwork } from "./networkHooks"; import { useRemoveDependenciesAndMultisigs } from "./removeAccountDependenciesHooks"; +import { IDP } from "../../auth"; import { ImplicitAccount, LedgerAccount, @@ -174,16 +176,21 @@ export const useRestoreLedger = () => { export const useRestoreSocial = () => { const dispatch = useAppDispatch(); - return (pk: string, pkh: string, label: string) => { - const account: SocialAccount = { - type: "social", - pk: pk, - address: { type: "implicit", pkh }, - idp: "google", - label, - }; - dispatch(addAccount(account)); - }; + const restore = useCallback( + (pk: string, pkh: string, label: string, idp: IDP) => { + const account: SocialAccount = { + type: "social", + pk: pk, + address: { type: "implicit", pkh }, + idp, + label, + }; + dispatch(addAccount(account)); + }, + [dispatch] + ); + + return restore; }; /** diff --git a/src/utils/multisig/helper.test.ts b/src/utils/multisig/helpers.test.ts similarity index 100% rename from src/utils/multisig/helper.test.ts rename to src/utils/multisig/helpers.test.ts From 331f532af211c3800e3d62dc85854d6a5540cc85 Mon Sep 17 00:00:00 2001 From: Sergey Kintsel Date: Mon, 20 May 2024 17:45:36 +0100 Subject: [PATCH 06/18] Add Facebook social login --- src/assets/icons/Facebook.tsx | 25 +++++++++++++++++++ src/assets/icons/index.tsx | 1 + src/auth/Auth.ts | 2 +- src/auth/FacebookAuth.ts | 20 +++++++++++++++ src/auth/forIDP.ts | 3 +++ src/auth/types.ts | 2 +- .../connectOrCreate/ConnectOrCreate.tsx | 2 ++ .../OnboardWithFacebookButton.tsx | 25 +++++++++++++++++++ 8 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 src/assets/icons/Facebook.tsx create mode 100644 src/auth/FacebookAuth.ts create mode 100644 src/components/Onboarding/connectOrCreate/OnboardWithFacebookButton.tsx diff --git a/src/assets/icons/Facebook.tsx b/src/assets/icons/Facebook.tsx new file mode 100644 index 0000000000..f5a0da5461 --- /dev/null +++ b/src/assets/icons/Facebook.tsx @@ -0,0 +1,25 @@ +import { Icon, IconProps } from "@chakra-ui/react"; + +export const FacebookIcon: React.FC = props => ( + + + + +); diff --git a/src/assets/icons/index.tsx b/src/assets/icons/index.tsx index 7e3a77389e..31c78a5860 100644 --- a/src/assets/icons/index.tsx +++ b/src/assets/icons/index.tsx @@ -26,6 +26,7 @@ export * from "./Eye"; export * from "./EyeSlash"; export * from "./FA1.2"; export * from "./FA2"; +export * from "./Facebook"; export * from "./Feedback"; export * from "./Fetching"; export * from "./FileArrowDown"; diff --git a/src/auth/Auth.ts b/src/auth/Auth.ts index a812602dc3..60ec1c84af 100644 --- a/src/auth/Auth.ts +++ b/src/auth/Auth.ts @@ -14,7 +14,7 @@ export abstract class Auth { const torus = new CustomAuth({ web3AuthClientId: WEB3_AUTH_CLIENT_ID, baseUrl: "https://umamiwallet.com/auth/v2.0.1/", - redirectPathName: "redirect.html?full_params=true", // TODO: fix this once we have a proper redirect page + redirectPathName: "redirect.html", redirectToOpener: true, uxMode: "popup", network: "mainnet", diff --git a/src/auth/FacebookAuth.ts b/src/auth/FacebookAuth.ts new file mode 100644 index 0000000000..242f919ff1 --- /dev/null +++ b/src/auth/FacebookAuth.ts @@ -0,0 +1,20 @@ +import { Auth } from "./Auth"; +import type { IDP } from "./types"; + +export class FacebookAuth extends Auth { + idpName: IDP = "facebook"; + clientId = "523634882377310"; + + protected override async login() { + const client = await this.getTorusClient(); + + return client.triggerLogin({ + verifier: "tezos-facebook", + clientId: this.clientId, + typeOfLogin: "facebook", + jwtParams: { + scope: "public_profile email", + }, + }); + } +} diff --git a/src/auth/forIDP.ts b/src/auth/forIDP.ts index e87c88c39f..48c711c796 100644 --- a/src/auth/forIDP.ts +++ b/src/auth/forIDP.ts @@ -1,4 +1,5 @@ import { EmailAuth } from "./EmailAuth"; +import { FacebookAuth } from "./FacebookAuth"; import { GoogleAuth } from "./GoogleAuth"; import { RedditAuth } from "./RedditAuth"; import type { IDP } from "./types"; @@ -11,5 +12,7 @@ export const forIDP = (idp: IDP) => { return new EmailAuth(); case "reddit": return new RedditAuth(); + case "facebook": + return new FacebookAuth(); } }; diff --git a/src/auth/types.ts b/src/auth/types.ts index c8b4c5e14e..82a813edcc 100644 --- a/src/auth/types.ts +++ b/src/auth/types.ts @@ -1 +1 @@ -export type IDP = "google" | "email" | "reddit"; +export type IDP = "google" | "email" | "reddit" | "facebook"; diff --git a/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx b/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx index 53a3dc02f4..2f8f71e972 100644 --- a/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx +++ b/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx @@ -1,6 +1,7 @@ import { Button, Divider, Flex, Text, VStack } from "@chakra-ui/react"; import { OnboardWithEmailButton } from "./OnboardWithEmailButton"; +import { OnboardWithFacebookButton } from "./OnboardWithFacebookButton"; import { OnboardWithGoogleButton } from "./OnboardWithGoogleButton"; import { OnboardWithRedditButton } from "./OnboardWithRedditButton"; import { WalletPlusIcon } from "../../../assets/icons"; @@ -50,6 +51,7 @@ export const ConnectOrCreate = ({ + diff --git a/src/components/Onboarding/connectOrCreate/OnboardWithFacebookButton.tsx b/src/components/Onboarding/connectOrCreate/OnboardWithFacebookButton.tsx new file mode 100644 index 0000000000..7bf13439de --- /dev/null +++ b/src/components/Onboarding/connectOrCreate/OnboardWithFacebookButton.tsx @@ -0,0 +1,25 @@ +import { IconButton } from "@chakra-ui/react"; + +import { useOnboardWithSocial } from "./useOnboardWithSocial"; +import { FacebookIcon } from "../../../assets/icons"; +import colors from "../../../style/colors"; + +export const OnboardWithFacebookButton = ({ onAuth }: { onAuth: () => void }) => { + const { isLoading, onboard } = useOnboardWithSocial("facebook", onAuth); + + return ( + } + isLoading={isLoading} + onClick={onboard} + size="lg" + variant="outline" + /> + ); +}; From b3b6260b74f3f6855e76f636445716528299a270 Mon Sep 17 00:00:00 2001 From: Sergey Kintsel Date: Mon, 20 May 2024 18:07:27 +0100 Subject: [PATCH 07/18] Add Twitter social login --- src/assets/icons/Twitter.tsx | 17 +++++++++++++ src/assets/icons/index.tsx | 1 + src/auth/TwitterAuth.ts | 21 ++++++++++++++++ src/auth/forIDP.ts | 3 +++ src/auth/types.ts | 2 +- .../connectOrCreate/ConnectOrCreate.tsx | 2 ++ .../OnboardWithTwitterButton.tsx | 25 +++++++++++++++++++ 7 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 src/assets/icons/Twitter.tsx create mode 100644 src/auth/TwitterAuth.ts create mode 100644 src/components/Onboarding/connectOrCreate/OnboardWithTwitterButton.tsx diff --git a/src/assets/icons/Twitter.tsx b/src/assets/icons/Twitter.tsx new file mode 100644 index 0000000000..2bfddd9849 --- /dev/null +++ b/src/assets/icons/Twitter.tsx @@ -0,0 +1,17 @@ +import { Icon, IconProps } from "@chakra-ui/react"; + +export const TwitterIcon: React.FC = props => ( + + + +); diff --git a/src/assets/icons/index.tsx b/src/assets/icons/index.tsx index 31c78a5860..89d1ee1895 100644 --- a/src/assets/icons/index.tsx +++ b/src/assets/icons/index.tsx @@ -66,3 +66,4 @@ export * from "./Verified"; export * from "./WalletPlus"; export * from "./Warning"; export * from "./XMark"; +export * from "./Twitter"; diff --git a/src/auth/TwitterAuth.ts b/src/auth/TwitterAuth.ts new file mode 100644 index 0000000000..4128b27ec1 --- /dev/null +++ b/src/auth/TwitterAuth.ts @@ -0,0 +1,21 @@ +import { Auth } from "./Auth"; +import { JWT_AUTH_DOMAIN } from "./constants"; +import type { IDP } from "./types"; + +export class TwitterAuth extends Auth { + idpName: IDP = "twitter"; + clientId = "3aCoxh3pw8g8JeFsdlJNUGwdgtLwdwgE"; + + protected override async login() { + const client = await this.getTorusClient(); + + return client.triggerLogin({ + verifier: "tezos-twitter", + clientId: this.clientId, + typeOfLogin: "twitter", + jwtParams: { + domain: JWT_AUTH_DOMAIN, + }, + }); + } +} diff --git a/src/auth/forIDP.ts b/src/auth/forIDP.ts index 48c711c796..0d2fc19723 100644 --- a/src/auth/forIDP.ts +++ b/src/auth/forIDP.ts @@ -2,6 +2,7 @@ import { EmailAuth } from "./EmailAuth"; import { FacebookAuth } from "./FacebookAuth"; import { GoogleAuth } from "./GoogleAuth"; import { RedditAuth } from "./RedditAuth"; +import { TwitterAuth } from "./TwitterAuth"; import type { IDP } from "./types"; export const forIDP = (idp: IDP) => { @@ -14,5 +15,7 @@ export const forIDP = (idp: IDP) => { return new RedditAuth(); case "facebook": return new FacebookAuth(); + case "twitter": + return new TwitterAuth(); } }; diff --git a/src/auth/types.ts b/src/auth/types.ts index 82a813edcc..15629238bd 100644 --- a/src/auth/types.ts +++ b/src/auth/types.ts @@ -1 +1 @@ -export type IDP = "google" | "email" | "reddit" | "facebook"; +export type IDP = "google" | "email" | "reddit" | "facebook" | "twitter"; diff --git a/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx b/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx index 2f8f71e972..9dcfb2d434 100644 --- a/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx +++ b/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx @@ -4,6 +4,7 @@ import { OnboardWithEmailButton } from "./OnboardWithEmailButton"; import { OnboardWithFacebookButton } from "./OnboardWithFacebookButton"; import { OnboardWithGoogleButton } from "./OnboardWithGoogleButton"; import { OnboardWithRedditButton } from "./OnboardWithRedditButton"; +import { OnboardWithTwitterButton } from "./OnboardWithTwitterButton"; import { WalletPlusIcon } from "../../../assets/icons"; import { IS_DEV } from "../../../env"; import colors from "../../../style/colors"; @@ -52,6 +53,7 @@ export const ConnectOrCreate = ({ + diff --git a/src/components/Onboarding/connectOrCreate/OnboardWithTwitterButton.tsx b/src/components/Onboarding/connectOrCreate/OnboardWithTwitterButton.tsx new file mode 100644 index 0000000000..7c311905aa --- /dev/null +++ b/src/components/Onboarding/connectOrCreate/OnboardWithTwitterButton.tsx @@ -0,0 +1,25 @@ +import { IconButton } from "@chakra-ui/react"; + +import { useOnboardWithSocial } from "./useOnboardWithSocial"; +import { TwitterIcon } from "../../../assets/icons"; +import colors from "../../../style/colors"; + +export const OnboardWithTwitterButton = ({ onAuth }: { onAuth: () => void }) => { + const { isLoading, onboard } = useOnboardWithSocial("twitter", onAuth); + + return ( + } + isLoading={isLoading} + onClick={onboard} + size="lg" + variant="outline" + /> + ); +}; From 729d53ccdeb0222bb8c07066718f721c03a8eb59 Mon Sep 17 00:00:00 2001 From: Sergey Kintsel Date: Mon, 20 May 2024 18:29:57 +0100 Subject: [PATCH 08/18] Set correct social logins order --- src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx b/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx index 9dcfb2d434..95280f6d8c 100644 --- a/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx +++ b/src/components/Onboarding/connectOrCreate/ConnectOrCreate.tsx @@ -50,10 +50,10 @@ export const ConnectOrCreate = ({ - - + + From cfdc7c182c0dccf633faa0f355cbe9775074e874 Mon Sep 17 00:00:00 2001 From: Sergey Kintsel Date: Mon, 20 May 2024 18:30:12 +0100 Subject: [PATCH 09/18] Add social fake accounts --- src/components/Onboarding/FakeAccount.tsx | 47 +++++++++++++++-------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/components/Onboarding/FakeAccount.tsx b/src/components/Onboarding/FakeAccount.tsx index 385eee42b3..25b417f5f0 100644 --- a/src/components/Onboarding/FakeAccount.tsx +++ b/src/components/Onboarding/FakeAccount.tsx @@ -1,42 +1,53 @@ /* istanbul ignore file */ -import { Button, FormControl, FormLabel, Input, Text } from "@chakra-ui/react"; +import { Button, FormControl, FormLabel, Input } from "@chakra-ui/react"; import { RpcClient } from "@taquito/rpc"; import { useForm } from "react-hook-form"; import { ModalContentWrapper } from "./ModalContentWrapper"; import { WalletPlusIcon } from "../../assets/icons"; -import { MAINNET } from "../../types/Network"; +import type { IDP } from "../../auth"; +import { GHOSTNET } from "../../types/Network"; import { defaultDerivationPathTemplate, makeDerivationPath, } from "../../utils/account/derivationPathUtils"; -import { useRestoreLedger } from "../../utils/hooks/setAccountDataHooks"; +import { useRestoreLedger, useRestoreSocial } from "../../utils/hooks/setAccountDataHooks"; export const FakeAccount = ({ onClose }: { onClose: () => void }) => { const { register, handleSubmit, formState: { errors }, - } = useForm<{ pkh: string; name: string }>({ mode: "onBlur" }); + } = useForm<{ pkh: string; name: string; idp?: string }>({ mode: "onBlur" }); const restoreLedger = useRestoreLedger(); + const restoreSocial = useRestoreSocial(); - const onSubmit = async ({ pkh, name }: { pkh: string; name: string }) => { - const rpc = new RpcClient(MAINNET.rpcUrl); + const onSubmit = async ({ pkh, name, idp }: { pkh: string; name: string; idp?: IDP }) => { + if (idp && idp.length > 0) { + if (!["google", "facebook", "twitter", "reddit", "email"].includes(idp)) { + throw new Error("Invalid IDP"); + } + } + const rpc = new RpcClient(GHOSTNET.rpcUrl); const managerKey = await rpc.getManagerKey(pkh); const pk = typeof managerKey === "string" ? managerKey : managerKey.key; - restoreLedger( - defaultDerivationPathTemplate, - makeDerivationPath(defaultDerivationPathTemplate, 0), - pk, - pkh, - name - ); + + if (idp) { + restoreSocial(pk, pkh, name, idp); + } else { + restoreLedger( + defaultDerivationPathTemplate, + makeDerivationPath(defaultDerivationPathTemplate, 0), + pk, + pkh, + name + ); + } onClose(); }; return ( } title="Add a Fake Account">
- It will be restored as a ledger account Address void }) => { placeholder="Please enter the account address" /> - + Name void }) => { placeholder="Please enter the account name" /> - From a5eb38654e4cb821b372674189de22748627ca9f Mon Sep 17 00:00:00 2001 From: Sergey Kintsel Date: Tue, 21 May 2024 10:41:12 +0100 Subject: [PATCH 10/18] Fix social login icons style --- src/assets/icons/Twitter.tsx | 7 ++----- src/components/AccountTile/AccountTile.tsx | 8 ++++---- .../connectOrCreate/OnboardWithEmailButton.tsx | 11 ++++------- .../connectOrCreate/OnboardWithFacebookButton.tsx | 8 +------- .../connectOrCreate/OnboardWithGoogleButton.tsx | 8 +------- .../connectOrCreate/OnboardWithRedditButton.tsx | 8 +------- .../connectOrCreate/OnboardWithTwitterButton.tsx | 11 ++++------- src/style/theme/button.ts | 10 ++++++++++ 8 files changed, 27 insertions(+), 44 deletions(-) diff --git a/src/assets/icons/Twitter.tsx b/src/assets/icons/Twitter.tsx index 2bfddd9849..648e2f9451 100644 --- a/src/assets/icons/Twitter.tsx +++ b/src/assets/icons/Twitter.tsx @@ -4,14 +4,11 @@ export const TwitterIcon: React.FC = props => ( - + ); diff --git a/src/components/AccountTile/AccountTile.tsx b/src/components/AccountTile/AccountTile.tsx index 24ed045f93..9b7f538528 100644 --- a/src/components/AccountTile/AccountTile.tsx +++ b/src/components/AccountTile/AccountTile.tsx @@ -23,11 +23,11 @@ export const AccountTileBase: React.FC< > = ({ icon, leftElement, rightElement, ...flexProps }) => ( diff --git a/src/components/Onboarding/connectOrCreate/OnboardWithEmailButton.tsx b/src/components/Onboarding/connectOrCreate/OnboardWithEmailButton.tsx index 0f77532197..6c7ead17cb 100644 --- a/src/components/Onboarding/connectOrCreate/OnboardWithEmailButton.tsx +++ b/src/components/Onboarding/connectOrCreate/OnboardWithEmailButton.tsx @@ -9,17 +9,14 @@ export const OnboardWithEmailButton = ({ onAuth }: { onAuth: () => void }) => { return ( } + icon={} isLoading={isLoading} onClick={onboard} - size="lg" - variant="outline" + variant="socialLogin" /> ); }; diff --git a/src/components/Onboarding/connectOrCreate/OnboardWithFacebookButton.tsx b/src/components/Onboarding/connectOrCreate/OnboardWithFacebookButton.tsx index 7bf13439de..095f083769 100644 --- a/src/components/Onboarding/connectOrCreate/OnboardWithFacebookButton.tsx +++ b/src/components/Onboarding/connectOrCreate/OnboardWithFacebookButton.tsx @@ -2,24 +2,18 @@ import { IconButton } from "@chakra-ui/react"; import { useOnboardWithSocial } from "./useOnboardWithSocial"; import { FacebookIcon } from "../../../assets/icons"; -import colors from "../../../style/colors"; export const OnboardWithFacebookButton = ({ onAuth }: { onAuth: () => void }) => { const { isLoading, onboard } = useOnboardWithSocial("facebook", onAuth); return ( } isLoading={isLoading} onClick={onboard} - size="lg" - variant="outline" + variant="socialLogin" /> ); }; diff --git a/src/components/Onboarding/connectOrCreate/OnboardWithGoogleButton.tsx b/src/components/Onboarding/connectOrCreate/OnboardWithGoogleButton.tsx index f4eed4402f..d5e6bd516f 100644 --- a/src/components/Onboarding/connectOrCreate/OnboardWithGoogleButton.tsx +++ b/src/components/Onboarding/connectOrCreate/OnboardWithGoogleButton.tsx @@ -2,24 +2,18 @@ import { IconButton } from "@chakra-ui/react"; import { useOnboardWithSocial } from "./useOnboardWithSocial"; import { GoogleIcon } from "../../../assets/icons"; -import colors from "../../../style/colors"; export const OnboardWithGoogleButton = ({ onAuth }: { onAuth: () => void }) => { const { isLoading, onboard } = useOnboardWithSocial("google", onAuth); return ( } isLoading={isLoading} onClick={onboard} - size="lg" - variant="outline" + variant="socialLogin" /> ); }; diff --git a/src/components/Onboarding/connectOrCreate/OnboardWithRedditButton.tsx b/src/components/Onboarding/connectOrCreate/OnboardWithRedditButton.tsx index b46c60bca6..ad18471114 100644 --- a/src/components/Onboarding/connectOrCreate/OnboardWithRedditButton.tsx +++ b/src/components/Onboarding/connectOrCreate/OnboardWithRedditButton.tsx @@ -2,24 +2,18 @@ import { IconButton } from "@chakra-ui/react"; import { useOnboardWithSocial } from "./useOnboardWithSocial"; import { RedditIcon } from "../../../assets/icons"; -import colors from "../../../style/colors"; export const OnboardWithRedditButton = ({ onAuth }: { onAuth: () => void }) => { const { isLoading, onboard } = useOnboardWithSocial("reddit", onAuth); return ( } isLoading={isLoading} onClick={onboard} - size="lg" - variant="outline" + variant="socialLogin" /> ); }; diff --git a/src/components/Onboarding/connectOrCreate/OnboardWithTwitterButton.tsx b/src/components/Onboarding/connectOrCreate/OnboardWithTwitterButton.tsx index 7c311905aa..90e011c5dd 100644 --- a/src/components/Onboarding/connectOrCreate/OnboardWithTwitterButton.tsx +++ b/src/components/Onboarding/connectOrCreate/OnboardWithTwitterButton.tsx @@ -9,17 +9,14 @@ export const OnboardWithTwitterButton = ({ onAuth }: { onAuth: () => void }) => return ( } + icon={} isLoading={isLoading} onClick={onboard} - size="lg" - variant="outline" + variant="socialLogin" /> ); }; diff --git a/src/style/theme/button.ts b/src/style/theme/button.ts index 0ea2257ae8..cfe136cbd3 100644 --- a/src/style/theme/button.ts +++ b/src/style/theme/button.ts @@ -136,6 +136,16 @@ export const buttonTheme = defineStyleConfig({ stroke: colors.green, }, }, + socialLogin: { + width: "48px", + height: "48px", + background: "white", + borderWidth: "0", + borderRadius: "full", + _hover: { + background: colors.gray[600], + }, + }, }, defaultProps: { variant: "primary", From e73e132c63d1f7937f51f9b8ed91383b6c18c986 Mon Sep 17 00:00:00 2001 From: Sergey Kintsel Date: Tue, 21 May 2024 12:12:59 +0100 Subject: [PATCH 11/18] Update account tile to support new social logins --- src/assets/icons/{Envelope.tsx => Email.tsx} | 3 +- src/assets/icons/Facebook.tsx | 1 + src/assets/icons/Google.tsx | 1 + src/assets/icons/Reddit.tsx | 1 + src/assets/icons/Social.tsx | 1 + src/assets/icons/Twitter.tsx | 1 + src/assets/icons/index.tsx | 4 +- .../AccountDrawer/AccountDrawerDisplay.tsx | 4 +- .../MultisigSignerTile.tsx | 4 +- .../AccountTile/AccountTile.test.tsx | 2 +- src/components/AccountTile/AccountTile.tsx | 2 +- .../AccountTile/AccountTileIcon.test.tsx | 58 +++++++++++-------- .../AccountTile/AccountTileIcon.tsx | 51 ++++++++++++---- .../AddressTile/AddressTileIcon.test.tsx | 15 ++--- .../AddressTile/AddressTileIcon.tsx | 3 +- src/components/Identicon.tsx | 6 +- .../connectOrCreate/ConnectOrCreate.tsx | 2 +- .../OnboardWithEmailButton.tsx | 4 +- src/mocks/factories.ts | 5 +- 19 files changed, 106 insertions(+), 62 deletions(-) rename src/assets/icons/{Envelope.tsx => Email.tsx} (87%) diff --git a/src/assets/icons/Envelope.tsx b/src/assets/icons/Email.tsx similarity index 87% rename from src/assets/icons/Envelope.tsx rename to src/assets/icons/Email.tsx index eefe1b3cb1..b6102137d8 100644 --- a/src/assets/icons/Envelope.tsx +++ b/src/assets/icons/Email.tsx @@ -1,10 +1,11 @@ import { Icon, IconProps } from "@chakra-ui/react"; -export const EnvelopeIcon: React.FC = props => ( +export const EmailIcon: React.FC = props => ( = props => ( width="28px" height="28px" fill="none" + data-testid="facebook-icon" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg" {...props} diff --git a/src/assets/icons/Google.tsx b/src/assets/icons/Google.tsx index 5237ceb528..ed39fdfba3 100644 --- a/src/assets/icons/Google.tsx +++ b/src/assets/icons/Google.tsx @@ -5,6 +5,7 @@ export const GoogleIcon: React.FC = props => ( width="24px" height="24px" fill="none" + data-testid="google-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" {...props} diff --git a/src/assets/icons/Reddit.tsx b/src/assets/icons/Reddit.tsx index 2a67de94ce..b957c8b86d 100644 --- a/src/assets/icons/Reddit.tsx +++ b/src/assets/icons/Reddit.tsx @@ -5,6 +5,7 @@ export const RedditIcon: React.FC = props => ( width="24px" height="24px" fill="none" + data-testid="reddit-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" {...props} diff --git a/src/assets/icons/Social.tsx b/src/assets/icons/Social.tsx index e81c525bd2..3cb5faf421 100644 --- a/src/assets/icons/Social.tsx +++ b/src/assets/icons/Social.tsx @@ -1,5 +1,6 @@ import { Icon, IconProps } from "@chakra-ui/react"; +// TODO: remove export const SocialIcon = (props: IconProps) => ( = props => ( width="20px" height="18px" fill="black" + data-testid="twitter-icon" viewBox="0 0 20 18" xmlns="http://www.w3.org/2000/svg" {...props} diff --git a/src/assets/icons/index.tsx b/src/assets/icons/index.tsx index 89d1ee1895..b799f54739 100644 --- a/src/assets/icons/index.tsx +++ b/src/assets/icons/index.tsx @@ -18,7 +18,7 @@ export * from "./Document"; export * from "./DoubleCheckmark"; export * from "./Download"; export * from "./EditAccount"; -export * from "./Envelope"; +export * from "./Email"; export * from "./Exclamation"; export * from "./ExitArrow"; export * from "./ExternalLink"; @@ -60,10 +60,10 @@ export * from "./Tez"; export * from "./ThreeDots"; export * from "./Token"; export * from "./Trash"; +export * from "./Twitter"; export * from "./UnknownContact"; export * from "./USB"; export * from "./Verified"; export * from "./WalletPlus"; export * from "./Warning"; export * from "./XMark"; -export * from "./Twitter"; diff --git a/src/components/AccountDrawer/AccountDrawerDisplay.tsx b/src/components/AccountDrawer/AccountDrawerDisplay.tsx index 7c00c0d8af..900aaf469b 100644 --- a/src/components/AccountDrawer/AccountDrawerDisplay.tsx +++ b/src/components/AccountDrawer/AccountDrawerDisplay.tsx @@ -17,7 +17,6 @@ import { getLastDelegation } from "../../utils/tezos"; import { accountIconGradient } from "../AccountTile/AccountTile"; import { AccountTileIcon } from "../AccountTile/AccountTileIcon"; import { AddressPill } from "../AddressPill/AddressPill"; -import { useAddressKind } from "../AddressTile/useAddressKind"; import { BuyTezForm } from "../BuyTez/BuyTezForm"; import { DynamicModalContext } from "../DynamicModal"; import { FormPage as DelegationFormPage } from "../SendFlow/Delegation/FormPage"; @@ -73,7 +72,6 @@ export const AccountDrawerDisplay: React.FC = ({ }) => { const isMultisig = account.type === "multisig"; const { openWith } = useContext(DynamicModalContext); - const addressKind = useAddressKind(account.address); const network = useSelectedNetwork(); const [delegation, setDelegation] = useState(null); @@ -106,7 +104,7 @@ export const AccountDrawerDisplay: React.FC = ({ })} data-testid={`account-card-${account.address.pkh}`} > - + {account.label} diff --git a/src/components/AccountDrawer/AssetsPanel/MultisigPendingOperations/MultisigSignerTile.tsx b/src/components/AccountDrawer/AssetsPanel/MultisigPendingOperations/MultisigSignerTile.tsx index 1a9ade7ed9..fff308a9ef 100644 --- a/src/components/AccountDrawer/AssetsPanel/MultisigPendingOperations/MultisigSignerTile.tsx +++ b/src/components/AccountDrawer/AssetsPanel/MultisigPendingOperations/MultisigSignerTile.tsx @@ -13,7 +13,7 @@ import { useAsyncActionHandler } from "../../../../utils/hooks/useAsyncActionHan import { MultisigOperation } from "../../../../utils/multisig/types"; import { estimate } from "../../../../utils/tezos"; import { AccountTileBase, LabelAndAddress } from "../../../AccountTile/AccountTile"; -import { AccountTileIcon } from "../../../AccountTile/AccountTileIcon"; +import { AddressTileIcon } from "../../../AddressTile/AddressTileIcon"; import { useAddressKind } from "../../../AddressTile/useAddressKind"; import { DynamicModalContext } from "../../../DynamicModal"; import { SignPage } from "../../../SendFlow/Multisig/SignPage"; @@ -81,7 +81,7 @@ export const MultisigSignerTile: React.FC<{ padding="15px" borderRadius="8px" backgroundColor={colors.gray[700]} - icon={} + icon={} leftElement={} rightElement={ ", () => { }, { balance: "3", - iconTestId: "social-icon", + iconTestId: "google-icon", account: mockSocialAccount(0), }, { diff --git a/src/components/AccountTile/AccountTile.tsx b/src/components/AccountTile/AccountTile.tsx index 9b7f538528..70529500cc 100644 --- a/src/components/AccountTile/AccountTile.tsx +++ b/src/components/AccountTile/AccountTile.tsx @@ -135,7 +135,7 @@ export const AccountTile: React.FC<{ padding={0} border="none" data-testid={`account-tile-${pkh}` + (isSelected ? "-selected" : "")} - icon={} + icon={} leftElement={} rightElement={ diff --git a/src/components/AccountTile/AccountTileIcon.test.tsx b/src/components/AccountTile/AccountTileIcon.test.tsx index 2038c71586..74b9946569 100644 --- a/src/components/AccountTile/AccountTileIcon.test.tsx +++ b/src/components/AccountTile/AccountTileIcon.test.tsx @@ -1,41 +1,49 @@ import { AccountTileIcon } from "./AccountTileIcon"; +import { IDP } from "../../auth"; +import { + mockLedgerAccount, + mockMnemonicAccount, + mockMultisigAccount, + mockSocialAccount, +} from "../../mocks/factories"; +import { addAccount } from "../../mocks/helpers"; import { render, screen } from "../../mocks/testUtils"; -import { AddressKind } from "../AddressTile/types"; +import { Account } from "../../types/Account"; -const fixture = (addressKind: AddressKind) => ; +const fixture = (account: Account) => ; -describe("", () => { - it("displays the mnemonic icon", () => { - render(fixture({ type: "mnemonic", pkh: "tz1", label: "label" })); - expect(screen.getByTestId("identicon")).toBeInTheDocument(); +describe("", () => { + it.each(["mnemonic", "secret_key"])("displays the %s icon", () => { + render(fixture(mockMnemonicAccount(0))); + expect(screen.getByTestId("identicon")).toBeVisible(); }); - it("displays the social icon", () => { - render(fixture({ type: "social", pkh: "tz1", label: "label" })); - expect(screen.getByTestId("social-icon")).toBeInTheDocument(); + it.each([ + "google" as const, + "facebook" as const, + "twitter" as const, + "reddit" as const, + "email" as const, + ])("displays the %s social icon", (idp: IDP) => { + render(fixture(mockSocialAccount(0, "account label", idp))); + expect(screen.getByTestId(`${idp}-icon`)).toBeVisible(); }); it("displays the ledger icon", () => { - render(fixture({ type: "ledger", pkh: "tz1", label: "label" })); - expect(screen.getByTestId("ledger-icon")).toBeInTheDocument(); + const account = mockLedgerAccount(0); + addAccount(account); + + render(fixture(account)); + + expect(screen.getByTestId("ledger-icon")).toBeVisible(); }); it("displays the multisig icon", () => { - render(fixture({ type: "multisig", pkh: "tz1", label: "label" })); - expect(screen.getByTestId("key-icon")).toBeInTheDocument(); - }); + const account = mockMultisigAccount(0); + addAccount(account); - it("displays the baker icon", () => { - render(fixture({ type: "baker", pkh: "tz1", label: "label" })); - expect(screen.getByTestId("baker-icon")).toBeInTheDocument(); - }); + render(fixture(account)); - it("displays the contact icon", () => { - render(fixture({ type: "contact", pkh: "tz1", label: "label" })); - expect(screen.getByTestId("contact-icon")).toBeInTheDocument(); - }); - it("displays the unknown icon", () => { - render(fixture({ type: "unknown", pkh: "tz1", label: null })); - expect(screen.getByTestId("unknown-contact-icon")).toBeInTheDocument(); + expect(screen.getByTestId("key-icon")).toBeVisible(); }); }); diff --git a/src/components/AccountTile/AccountTileIcon.tsx b/src/components/AccountTile/AccountTileIcon.tsx index d50d13426a..670b5c6b2e 100644 --- a/src/components/AccountTile/AccountTileIcon.tsx +++ b/src/components/AccountTile/AccountTileIcon.tsx @@ -1,12 +1,15 @@ import { Flex } from "@chakra-ui/react"; +import { EmailIcon, FacebookIcon, GoogleIcon, RedditIcon, TwitterIcon } from "../../assets/icons"; import colors from "../../style/colors"; +import { Account } from "../../types/Account"; import { AddressTileIcon } from "../AddressTile/AddressTileIcon"; -import { AddressKind } from "../AddressTile/types"; +import { useAddressKind } from "../AddressTile/useAddressKind"; import { Identicon } from "../Identicon"; -export const AccountTileIcon: React.FC<{ addressKind: AddressKind }> = ({ addressKind }) => { - switch (addressKind.type) { +export const AccountTileIcon: React.FC<{ account: Account }> = ({ account }) => { + const addressKind = useAddressKind(account.address); + switch (account.type) { case "secret_key": case "mnemonic": return ( @@ -14,28 +17,54 @@ export const AccountTileIcon: React.FC<{ addressKind: AddressKind }> = ({ addres width="48px" height="48px" padding="8px" - address={addressKind.pkh} + address={account.address} identiconSize={32} /> ); - case "social": - case "contact": + case "social": { + let icon; + switch (account.idp) { + case "google": + icon = ; + break; + case "facebook": + icon = ; + break; + case "twitter": + icon = ; + break; + case "reddit": + icon = ; + break; + case "email": + icon = ; + } + return ( + + {icon} + + ); + } case "ledger": case "multisig": - case "unknown": - case "baker": { - const bg = addressKind.type === "social" ? "white" : colors.gray[500]; return ( ); - } } }; diff --git a/src/components/AddressTile/AddressTileIcon.test.tsx b/src/components/AddressTile/AddressTileIcon.test.tsx index e8e286fb44..dcde7e98bc 100644 --- a/src/components/AddressTile/AddressTileIcon.test.tsx +++ b/src/components/AddressTile/AddressTileIcon.test.tsx @@ -1,42 +1,43 @@ import { AddressTileIcon } from "./AddressTileIcon"; import { AddressKind } from "./types"; +import { mockContractAddress, mockImplicitAddress } from "../../mocks/factories"; import { render, screen } from "../../mocks/testUtils"; const fixture = (addressKind: AddressKind) => ; describe("", () => { it("displays the mnemonic icon", () => { - render(fixture({ type: "mnemonic", pkh: "tz1", label: "label" })); + render(fixture({ ...mockImplicitAddress(0), type: "mnemonic", label: "label" })); expect(screen.getByTestId("identicon")).toBeInTheDocument(); }); it("displays the social icon", () => { - render(fixture({ type: "social", pkh: "tz1", label: "label" })); + render(fixture({ ...mockImplicitAddress(0), type: "social", label: "label" })); expect(screen.getByTestId("social-icon")).toBeInTheDocument(); }); it("displays the ledger icon", () => { - render(fixture({ type: "ledger", pkh: "tz1", label: "label" })); + render(fixture({ ...mockImplicitAddress(0), type: "ledger", label: "label" })); expect(screen.getByTestId("ledger-icon")).toBeInTheDocument(); }); it("displays the multisig icon", () => { - render(fixture({ type: "multisig", pkh: "tz1", label: "label" })); + render(fixture({ ...mockContractAddress(0), type: "multisig", label: "label" })); expect(screen.getByTestId("key-icon")).toBeInTheDocument(); }); it("displays the baker icon", () => { - render(fixture({ type: "baker", pkh: "tz1", label: "label" })); + render(fixture({ ...mockImplicitAddress(0), type: "baker", label: "label" })); expect(screen.getByTestId("baker-icon")).toBeInTheDocument(); }); it("displays the contact icon", () => { - render(fixture({ type: "contact", pkh: "tz1", label: "label" })); + render(fixture({ ...mockImplicitAddress(0), type: "contact", label: "label" })); expect(screen.getByTestId("contact-icon")).toBeInTheDocument(); }); it("displays the unknown icon", () => { - render(fixture({ type: "unknown", pkh: "tz1", label: null })); + render(fixture({ ...mockImplicitAddress(0), type: "unknown", label: null })); expect(screen.getByTestId("unknown-contact-icon")).toBeInTheDocument(); }); }); diff --git a/src/components/AddressTile/AddressTileIcon.tsx b/src/components/AddressTile/AddressTileIcon.tsx index d43ddd9ca0..428d76dddd 100644 --- a/src/components/AddressTile/AddressTileIcon.tsx +++ b/src/components/AddressTile/AddressTileIcon.tsx @@ -9,6 +9,7 @@ import { UnknownContactIcon, } from "../../assets/icons"; import colors from "../../style/colors"; +import { parsePkh } from "../../types/Address"; import { Identicon } from "../Identicon"; const baseIconProps = { @@ -46,7 +47,7 @@ export const AddressTileIcon: React.FC<{ width={sizeInPx} height={sizeInPx} padding="5px" - address={addressKind.pkh} + address={parsePkh(addressKind.pkh)} identiconSize={identiconSize} /> ); diff --git a/src/components/Identicon.tsx b/src/components/Identicon.tsx index e48e4ae9e1..438696251f 100644 --- a/src/components/Identicon.tsx +++ b/src/components/Identicon.tsx @@ -3,13 +3,13 @@ import md5 from "md5"; import React from "react"; import { ReactIdenticon } from "./ReactIdenticon"; -import { RawPkh } from "../types/Address"; +import { Address, RawPkh } from "../types/Address"; export const color = (address: RawPkh) => `#${md5(address).slice(0, 6)}`; export const Identicon: React.FC< { - address: RawPkh; + address: Address; identiconSize: number; } & ChakraProps > = ({ address, identiconSize, ...props }) => ( @@ -28,7 +28,7 @@ export const Identicon: React.FC< - Continue with Google + Continue with social diff --git a/src/components/Onboarding/connectOrCreate/OnboardWithEmailButton.tsx b/src/components/Onboarding/connectOrCreate/OnboardWithEmailButton.tsx index 6c7ead17cb..bd8fbdd715 100644 --- a/src/components/Onboarding/connectOrCreate/OnboardWithEmailButton.tsx +++ b/src/components/Onboarding/connectOrCreate/OnboardWithEmailButton.tsx @@ -1,7 +1,7 @@ import { IconButton } from "@chakra-ui/react"; import { useOnboardWithSocial } from "./useOnboardWithSocial"; -import { EnvelopeIcon } from "../../../assets/icons"; +import { EmailIcon } from "../../../assets/icons"; import colors from "../../../style/colors"; export const OnboardWithEmailButton = ({ onAuth }: { onAuth: () => void }) => { @@ -13,7 +13,7 @@ export const OnboardWithEmailButton = ({ onAuth }: { onAuth: () => void }) => { _hover={{ color: "white", background: colors.gray[600] }} aria-label="Email SSO" data-testid="login-button-email" - icon={} + icon={} isLoading={isLoading} onClick={onboard} variant="socialLogin" diff --git a/src/mocks/factories.ts b/src/mocks/factories.ts index 8249a86bc9..ab68490417 100644 --- a/src/mocks/factories.ts +++ b/src/mocks/factories.ts @@ -1,6 +1,7 @@ import { MichelsonV1Expression } from "@taquito/rpc"; import { DelegationOperation } from "@tzkt/sdk-api"; +import { IDP } from "../auth"; import { Account, ImplicitAccount, @@ -149,12 +150,12 @@ export const mockSecretKeyAccount = (index: number, label?: string): SecretKeyAc pk: mockPk(index), }); -export const mockSocialAccount = (index: number, label?: string): SocialAccount => ({ +export const mockSocialAccount = (index: number, label?: string, idp?: IDP): SocialAccount => ({ type: "social", label: label || mockAccountLabel(index), address: mockImplicitAddress(index), pk: mockPk(index), - idp: "google", + idp: idp || "google", }); export const mockLedgerAccount = (index: number, label?: string): LedgerAccount => { From dba52e7bf21ede4b70860c31326cb4c82206b164 Mon Sep 17 00:00:00 2001 From: Sergey Kintsel Date: Tue, 21 May 2024 12:16:54 +0100 Subject: [PATCH 12/18] Fix account filter overlap with account icons --- src/components/Identicon.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Identicon.tsx b/src/components/Identicon.tsx index 438696251f..3fcf2e726e 100644 --- a/src/components/Identicon.tsx +++ b/src/components/Identicon.tsx @@ -19,7 +19,7 @@ export const Identicon: React.FC< borderRadius: "4px", }, }} - zIndex={10} + zIndex={3} background="white" borderRadius="4px" data-testid="identicon" From addf75e3f05e298d419d270bc608eccd189ed007 Mon Sep 17 00:00:00 2001 From: Sergey Kintsel Date: Tue, 21 May 2024 12:29:57 +0100 Subject: [PATCH 13/18] Set correct gradient for new social logins --- src/components/AccountTile/AccountTile.tsx | 36 ++++++++++++++-------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/components/AccountTile/AccountTile.tsx b/src/components/AccountTile/AccountTile.tsx index 70529500cc..f60b442cca 100644 --- a/src/components/AccountTile/AccountTile.tsx +++ b/src/components/AccountTile/AccountTile.tsx @@ -55,6 +55,28 @@ export const LabelAndAddress: React.FC<{ label: string | null; pkh: string }> = const MAX_NFT_COUNT = 7; +const accountIconGradientColor = (account: Account) => { + switch (account.type) { + case "mnemonic": + case "secret_key": + return identiconColor(account.address.pkh); + case "ledger": + case "multisig": + return colors.gray[450]; + case "social": + switch (account.idp) { + case "facebook": + return "#1977F2"; + case "google": + case "reddit": + return "#EA4335"; + case "email": + case "twitter": + return colors.gray[450]; + } + } +}; + export const accountIconGradient = ({ account, radius, @@ -70,19 +92,7 @@ export const accountIconGradient = ({ mainBackgroundColor?: string; opacity?: string; }) => { - let color: string; - switch (account.type) { - case "mnemonic": - case "secret_key": - color = identiconColor(account.address.pkh); - break; - case "ledger": - case "multisig": - color = colors.gray[450]; - break; - case "social": - color = "#EA4335"; - } + let color = accountIconGradientColor(account); color += opacity; From 52286ef5a49d6e13fad95b47fe1b589567da0c3f Mon Sep 17 00:00:00 2001 From: Sergey Kintsel Date: Tue, 21 May 2024 12:32:29 +0100 Subject: [PATCH 14/18] Use AccountTileIcon on tokens page --- src/components/AccountTile/AccountTileIcon.tsx | 7 +++++-- src/views/tokens/AccountTokens.tsx | 6 ++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/AccountTile/AccountTileIcon.tsx b/src/components/AccountTile/AccountTileIcon.tsx index 670b5c6b2e..71b516fb82 100644 --- a/src/components/AccountTile/AccountTileIcon.tsx +++ b/src/components/AccountTile/AccountTileIcon.tsx @@ -7,7 +7,10 @@ import { AddressTileIcon } from "../AddressTile/AddressTileIcon"; import { useAddressKind } from "../AddressTile/useAddressKind"; import { Identicon } from "../Identicon"; -export const AccountTileIcon: React.FC<{ account: Account }> = ({ account }) => { +export const AccountTileIcon: React.FC<{ account: Account; identiconSize?: number }> = ({ + account, + identiconSize = 32, +}) => { const addressKind = useAddressKind(account.address); switch (account.type) { case "secret_key": @@ -18,7 +21,7 @@ export const AccountTileIcon: React.FC<{ account: Account }> = ({ account }) => height="48px" padding="8px" address={account.address} - identiconSize={32} + identiconSize={identiconSize} /> ); case "social": { diff --git a/src/views/tokens/AccountTokens.tsx b/src/views/tokens/AccountTokens.tsx index 07ee453756..e995b0697e 100644 --- a/src/views/tokens/AccountTokens.tsx +++ b/src/views/tokens/AccountTokens.tsx @@ -16,9 +16,8 @@ import { TokenNameWithIcon } from "./TokenNameWithIcon"; import { TokenIcon } from "../../assets/icons"; import { AccountBalance } from "../../components/AccountBalance"; import { accountIconGradient } from "../../components/AccountTile/AccountTile"; +import { AccountTileIcon } from "../../components/AccountTile/AccountTileIcon"; import { AddressPill } from "../../components/AddressPill/AddressPill"; -import { AddressTileIcon } from "../../components/AddressTile/AddressTileIcon"; -import { useAddressKind } from "../../components/AddressTile/useAddressKind"; import { DynamicModalContext } from "../../components/DynamicModal"; import { SendButton } from "../../components/SendButton"; import { FormPage as SendTokenFormPage } from "../../components/SendFlow/Token/FormPage"; @@ -33,7 +32,6 @@ const Header: React.FC<{ account: Account; }> = ({ account }) => { const { address, label } = account; - const addressKind = useAddressKind(address); return ( - + From 73c623d360dd19ec57a1c462487c1294b25c9940 Mon Sep 17 00:00:00 2001 From: Sergey Kintsel Date: Tue, 21 May 2024 15:57:46 +0100 Subject: [PATCH 15/18] Fix address icon sizes --- src/WelcomeScreen.tsx | 2 +- src/assets/icons/Key.tsx | 3 + src/assets/icons/Social.tsx | 49 ----- src/assets/icons/index.tsx | 1 - .../AccountDrawer/AccountDrawerDisplay.tsx | 2 +- .../MultisigSignerTile.tsx | 2 +- src/components/AccountTile/AccountTile.tsx | 2 +- .../AccountTile/AccountTileIcon.test.tsx | 2 +- .../AccountTile/AccountTileIcon.tsx | 206 ++++++++++++------ .../AddressPill/AddressPillIcon.test.tsx | 11 +- .../AddressPill/AddressPillIcon.tsx | 4 +- src/components/AddressPill/types.ts | 24 +- src/components/AddressTile/AddressTile.tsx | 2 +- .../AddressTile/AddressTileIcon.test.tsx | 66 ++++-- .../AddressTile/AddressTileIcon.tsx | 98 ++++----- .../AddressTile/AddressTileIconSize.ts | 1 + src/components/AddressTile/types.ts | 32 +-- .../restoreMnemonic/RestoreMnemonic.tsx | 2 +- .../restoreSecretKey/RestoreSecretKey.tsx | 6 +- .../showSeedphrase/ShowSeedphrase.tsx | 2 +- src/views/batch/AccountSmallTile.test.tsx | 6 +- src/views/batch/AccountSmallTile.tsx | 20 +- src/views/home/AccountsList.tsx | 2 +- src/views/tokens/AccountTokens.tsx | 2 +- 24 files changed, 281 insertions(+), 266 deletions(-) delete mode 100644 src/assets/icons/Social.tsx create mode 100644 src/components/AddressTile/AddressTileIconSize.ts diff --git a/src/WelcomeScreen.tsx b/src/WelcomeScreen.tsx index 46cee8b112..7d63a4dfa4 100644 --- a/src/WelcomeScreen.tsx +++ b/src/WelcomeScreen.tsx @@ -44,7 +44,7 @@ const SliderItems: Item[] = [ ), - icon: , + icon: , image: MultisigImage, }, { diff --git a/src/assets/icons/Key.tsx b/src/assets/icons/Key.tsx index ff1cb0c78d..9c605a8235 100644 --- a/src/assets/icons/Key.tsx +++ b/src/assets/icons/Key.tsx @@ -1,10 +1,13 @@ import { Icon, IconProps } from "@chakra-ui/react"; +import colors from "../../style/colors"; + export const KeyIcon: React.FC = props => ( ( - - - - - - - - - - - - -); diff --git a/src/assets/icons/index.tsx b/src/assets/icons/index.tsx index b799f54739..4f497c6f4a 100644 --- a/src/assets/icons/index.tsx +++ b/src/assets/icons/index.tsx @@ -55,7 +55,6 @@ export * from "./Reload"; export * from "./Rotate"; export * from "./Slash"; export * from "./Sliders"; -export * from "./Social"; export * from "./Tez"; export * from "./ThreeDots"; export * from "./Token"; diff --git a/src/components/AccountDrawer/AccountDrawerDisplay.tsx b/src/components/AccountDrawer/AccountDrawerDisplay.tsx index 900aaf469b..73ea1a92df 100644 --- a/src/components/AccountDrawer/AccountDrawerDisplay.tsx +++ b/src/components/AccountDrawer/AccountDrawerDisplay.tsx @@ -104,7 +104,7 @@ export const AccountDrawerDisplay: React.FC = ({ })} data-testid={`account-card-${account.address.pkh}`} > - + {account.label} diff --git a/src/components/AccountDrawer/AssetsPanel/MultisigPendingOperations/MultisigSignerTile.tsx b/src/components/AccountDrawer/AssetsPanel/MultisigPendingOperations/MultisigSignerTile.tsx index fff308a9ef..d1d4a61584 100644 --- a/src/components/AccountDrawer/AssetsPanel/MultisigPendingOperations/MultisigSignerTile.tsx +++ b/src/components/AccountDrawer/AssetsPanel/MultisigPendingOperations/MultisigSignerTile.tsx @@ -81,7 +81,7 @@ export const MultisigSignerTile: React.FC<{ padding="15px" borderRadius="8px" backgroundColor={colors.gray[700]} - icon={} + icon={} leftElement={} rightElement={ } + icon={} leftElement={} rightElement={ diff --git a/src/components/AccountTile/AccountTileIcon.test.tsx b/src/components/AccountTile/AccountTileIcon.test.tsx index 74b9946569..916c93811b 100644 --- a/src/components/AccountTile/AccountTileIcon.test.tsx +++ b/src/components/AccountTile/AccountTileIcon.test.tsx @@ -10,7 +10,7 @@ import { addAccount } from "../../mocks/helpers"; import { render, screen } from "../../mocks/testUtils"; import { Account } from "../../types/Account"; -const fixture = (account: Account) => ; +const fixture = (account: Account) => ; describe("", () => { it.each(["mnemonic", "secret_key"])("displays the %s icon", () => { diff --git a/src/components/AccountTile/AccountTileIcon.tsx b/src/components/AccountTile/AccountTileIcon.tsx index 71b516fb82..93a7e2f2ee 100644 --- a/src/components/AccountTile/AccountTileIcon.tsx +++ b/src/components/AccountTile/AccountTileIcon.tsx @@ -1,73 +1,149 @@ -import { Flex } from "@chakra-ui/react"; +import { memo } from "react"; -import { EmailIcon, FacebookIcon, GoogleIcon, RedditIcon, TwitterIcon } from "../../assets/icons"; +import { + EmailIcon, + FacebookIcon, + GoogleIcon, + KeyIcon, + LedgerIcon, + RedditIcon, + TwitterIcon, +} from "../../assets/icons"; import colors from "../../style/colors"; import { Account } from "../../types/Account"; -import { AddressTileIcon } from "../AddressTile/AddressTileIcon"; -import { useAddressKind } from "../AddressTile/useAddressKind"; +import { AddressTileIconSize } from "../AddressTile/AddressTileIconSize"; import { Identicon } from "../Identicon"; -export const AccountTileIcon: React.FC<{ account: Account; identiconSize?: number }> = ({ - account, - identiconSize = 32, -}) => { - const addressKind = useAddressKind(account.address); - switch (account.type) { - case "secret_key": - case "mnemonic": - return ( - - ); - case "social": { - let icon; - switch (account.idp) { - case "google": - icon = ; - break; - case "facebook": - icon = ; - break; - case "twitter": - icon = ; - break; - case "reddit": - icon = ; - break; - case "email": - icon = ; +const SIZES = { + lg: { + defaults: { + width: "48px", + height: "48px", + borderRadius: "4px", + }, + mnemonic: { + padding: "8px", + identiconSize: 32, + }, + ledger: { + padding: "10px", + }, + secret_key: { + padding: "8px", + identiconSize: 32, + }, + multisig: { + padding: "10px", + }, + social: { + google: { + paddingX: "10.28px", + paddingY: "10px", + }, + facebook: { + padding: "9px", + }, + twitter: { + paddingX: "12.78px", + paddingY: "13.5px", + }, + reddit: { + padding: "10px", + }, + email: { + padding: "10px", + }, + }, + }, + sm: { + defaults: { + width: "30px", + height: "30px", + borderRadius: "4px", + }, + mnemonic: { + padding: "5px", + identiconSize: 20, + }, + ledger: { + padding: "5px", + }, + secret_key: { + padding: "5px", + identiconSize: 20, + }, + multisig: { + padding: "5px", + }, + social: { + google: { + paddingX: "6.425px", + paddingY: "6.25px", + }, + facebook: { + padding: "6px", + }, + twitter: { + paddingX: "7.5px", + paddingY: "8px", + }, + reddit: { + padding: "6px", + }, + email: { + padding: "6px", + }, + }, + }, +} as const; + +/** + * Displays an icon for an account based on its type. + * + * @param account - The account to display the icon for. + * @param size - The size of the icon. + */ +export const AccountTileIcon: React.FC<{ account: Account; size: AddressTileIconSize }> = memo( + ({ account, size }) => { + const sizeObj = SIZES[size]; + const defaults = sizeObj.defaults; + + switch (account.type) { + case "secret_key": + return ; + case "mnemonic": + return ; + case "ledger": + return ; + case "multisig": + return ( + + ); + case "social": { + switch (account.idp) { + case "google": + return ; + + case "facebook": + return ; + + case "twitter": + return ; + + case "reddit": + return ; + + case "email": + return ( + + ); + } } - return ( - - {icon} - - ); } - case "ledger": - case "multisig": - return ( - - - - ); } -}; +); diff --git a/src/components/AddressPill/AddressPillIcon.test.tsx b/src/components/AddressPill/AddressPillIcon.test.tsx index c61e33ddf9..7b48fd489d 100644 --- a/src/components/AddressPill/AddressPillIcon.test.tsx +++ b/src/components/AddressPill/AddressPillIcon.test.tsx @@ -1,15 +1,20 @@ import { LeftIcon, RightIcon } from "./AddressPillIcon"; -import { AddressKindType } from "./types"; import { mockFA2Address } from "../../mocks/addressKind"; import { mockContractAddress } from "../../mocks/factories"; import { render, screen } from "../../mocks/testUtils"; describe("AddressPill Icons", () => { - it.each(["multisig", "fa1.2", "fa2", "baker", "contact"])("renders %s left icon", type => { + it.each([ + "multisig" as const, + "fa1.2" as const, + "fa2" as const, + "baker" as const, + "contact" as const, + ])("renders %s left icon", type => { render( ; } - const knownTypes: AddressKindType[] = ["implicit", "multisig", "baker"]; + const knownTypes: AddressKind["type"][] = ["implicit", "multisig", "baker"]; if (knownTypes.includes(type) || addressExistsInContacts(pkh)) { return null; diff --git a/src/components/AddressPill/types.ts b/src/components/AddressPill/types.ts index 1388f30ab9..89847e4e6c 100644 --- a/src/components/AddressPill/types.ts +++ b/src/components/AddressPill/types.ts @@ -6,33 +6,21 @@ import { UnknownAddress, } from "../AddressTile/types"; -export type AddressKindType = - | "implicit" - | "multisig" - | "fa1.2" - | "fa2" - | "baker" - | "contact" - | "unknown"; - -type AddressKindBase = { - type: AddressKindType; - pkh: RawPkh; - label: string | null; -}; - -export type OwnedImplicitAddress = AddressKindBase & { +export type OwnedImplicitAddress = { type: "implicit"; + pkh: RawPkh; label: string; }; -export type FA12Address = AddressKindBase & { +export type FA12Address = { type: "fa1.2"; + pkh: RawPkh; label: null; }; -export type FA2Address = AddressKindBase & { +export type FA2Address = { type: "fa2"; + pkh: RawPkh; label: null; }; diff --git a/src/components/AddressTile/AddressTile.tsx b/src/components/AddressTile/AddressTile.tsx index 12904df0f4..dc208a3e04 100644 --- a/src/components/AddressTile/AddressTile.tsx +++ b/src/components/AddressTile/AddressTile.tsx @@ -37,7 +37,7 @@ export const AddressTile: React.FC<{ address: Address; hideBalance?: boolean } & {...flexProps} > - + {addressKind.type === "unknown" ? ( diff --git a/src/components/AddressTile/AddressTileIcon.test.tsx b/src/components/AddressTile/AddressTileIcon.test.tsx index dcde7e98bc..4458ab976d 100644 --- a/src/components/AddressTile/AddressTileIcon.test.tsx +++ b/src/components/AddressTile/AddressTileIcon.test.tsx @@ -1,43 +1,75 @@ import { AddressTileIcon } from "./AddressTileIcon"; import { AddressKind } from "./types"; -import { mockContractAddress, mockImplicitAddress } from "../../mocks/factories"; +import type { IDP } from "../../auth"; +import { + mockImplicitAddress, + mockLedgerAccount, + mockMnemonicAccount, + mockMultisigAccount, + mockSecretKeyAccount, + mockSocialAccount, +} from "../../mocks/factories"; +import { addAccount } from "../../mocks/helpers"; import { render, screen } from "../../mocks/testUtils"; -const fixture = (addressKind: AddressKind) => ; +const fixture = (addressKind: AddressKind) => ( + +); describe("", () => { - it("displays the mnemonic icon", () => { - render(fixture({ ...mockImplicitAddress(0), type: "mnemonic", label: "label" })); - expect(screen.getByTestId("identicon")).toBeInTheDocument(); + const label = "some label"; + + it.each([mockMnemonicAccount(0), mockSecretKeyAccount(0)])("displays the $type icon", account => { + addAccount(account); + render(fixture({ ...account, pkh: account.address.pkh })); + expect(screen.getByTestId("identicon")).toBeVisible(); }); - it("displays the social icon", () => { - render(fixture({ ...mockImplicitAddress(0), type: "social", label: "label" })); - expect(screen.getByTestId("social-icon")).toBeInTheDocument(); + it.each([ + "google" as const, + "facebook" as const, + "twitter" as const, + "reddit" as const, + "email" as const, + ])("displays the %s social icon", (idp: IDP) => { + const account = mockSocialAccount(0, "account label", idp); + addAccount(account); + + render(fixture({ ...account.address, type: "social", label })); + + expect(screen.getByTestId(`${idp}-icon`)).toBeVisible(); }); it("displays the ledger icon", () => { - render(fixture({ ...mockImplicitAddress(0), type: "ledger", label: "label" })); - expect(screen.getByTestId("ledger-icon")).toBeInTheDocument(); + const account = mockLedgerAccount(0); + addAccount(account); + + render(fixture({ ...account.address, type: "ledger", label })); + + expect(screen.getByTestId("ledger-icon")).toBeVisible(); }); it("displays the multisig icon", () => { - render(fixture({ ...mockContractAddress(0), type: "multisig", label: "label" })); - expect(screen.getByTestId("key-icon")).toBeInTheDocument(); + const account = mockMultisigAccount(0); + addAccount(account); + + render(fixture({ ...account.address, type: "multisig", label })); + + expect(screen.getByTestId("key-icon")).toBeVisible(); }); it("displays the baker icon", () => { - render(fixture({ ...mockImplicitAddress(0), type: "baker", label: "label" })); - expect(screen.getByTestId("baker-icon")).toBeInTheDocument(); + render(fixture({ ...mockImplicitAddress(0), type: "baker", label })); + expect(screen.getByTestId("baker-icon")).toBeVisible(); }); it("displays the contact icon", () => { - render(fixture({ ...mockImplicitAddress(0), type: "contact", label: "label" })); - expect(screen.getByTestId("contact-icon")).toBeInTheDocument(); + render(fixture({ ...mockImplicitAddress(0), type: "contact", label })); + expect(screen.getByTestId("contact-icon")).toBeVisible(); }); it("displays the unknown icon", () => { render(fixture({ ...mockImplicitAddress(0), type: "unknown", label: null })); - expect(screen.getByTestId("unknown-contact-icon")).toBeInTheDocument(); + expect(screen.getByTestId("unknown-contact-icon")).toBeVisible(); }); }); diff --git a/src/components/AddressTile/AddressTileIcon.tsx b/src/components/AddressTile/AddressTileIcon.tsx index 428d76dddd..f6e23411dd 100644 --- a/src/components/AddressTile/AddressTileIcon.tsx +++ b/src/components/AddressTile/AddressTileIcon.tsx @@ -1,87 +1,77 @@ -import { AspectRatio, Image } from "@chakra-ui/react"; +import { Image } from "@chakra-ui/react"; +import { memo } from "react"; +import { AddressTileIconSize } from "./AddressTileIconSize"; import { AddressKind } from "./types"; -import { - ContactIcon, - KeyIcon, - LedgerIcon, - SocialIcon, - UnknownContactIcon, -} from "../../assets/icons"; +import { ContactIcon, UnknownContactIcon } from "../../assets/icons"; import colors from "../../style/colors"; -import { parsePkh } from "../../types/Address"; -import { Identicon } from "../Identicon"; +import { useGetOwnedAccountSafe } from "../../utils/hooks/getAccountDataHooks"; +import { AccountTileIcon } from "../AccountTile/AccountTileIcon"; const baseIconProps = { stroke: colors.gray[400], borderRadius: "4px", - padding: "5px", background: colors.gray[500], }; -type AddressTileIconSize = "sm" | "md" | "lg"; - +/** + * Displays an icon for an address based on its kind. + * Can be used with arbitrary addresses, not just accounts. + * Though for accounts it'll reuse {@link AccountTileIcon}. + * + * @param addressKind - The address to display the icon for. + * @param size - The size of the icon. + */ export const AddressTileIcon: React.FC<{ addressKind: AddressKind; - size?: AddressTileIconSize; - identiconSize?: number; // only used for secret_key and mnemonic -}> = ({ addressKind, size = "sm", identiconSize = 20 }) => { + size: AddressTileIconSize; +}> = memo(({ addressKind, size }) => { + const getAccount = useGetOwnedAccountSafe(); + const account = getAccount(addressKind.pkh); + + if (account) { + return ; + } + let sizeInPx; + let padding; switch (size) { case "sm": sizeInPx = "30px"; - break; - case "md": - sizeInPx = "38.5px"; + padding = "5px"; break; case "lg": - sizeInPx = "45.5px"; - break; + sizeInPx = "48px"; + padding = "10px"; } switch (addressKind.type) { - case "secret_key": - case "mnemonic": + case "contact": return ( - + ); - case "social": + case "unknown": return ( - ); - case "ledger": - return ; - case "multisig": - return ; - case "contact": - return ; - case "unknown": - return ; - case "baker": { - const bakerLogoUrl = `https://services.tzkt.io/v1/avatars/${addressKind.pkh}`; + case "baker": return ( - - - + src={`https://services.tzkt.io/v1/avatars/${addressKind.pkh}`} + /> ); - } + case "secret_key": + case "mnemonic": + case "social": + case "ledger": + case "multisig": + return null; // impossible state } -}; +}); diff --git a/src/components/AddressTile/AddressTileIconSize.ts b/src/components/AddressTile/AddressTileIconSize.ts new file mode 100644 index 0000000000..7116371bbf --- /dev/null +++ b/src/components/AddressTile/AddressTileIconSize.ts @@ -0,0 +1 @@ +export type AddressTileIconSize = "sm" | "lg"; diff --git a/src/components/AddressTile/types.ts b/src/components/AddressTile/types.ts index 75046f7c64..9794e4c767 100644 --- a/src/components/AddressTile/types.ts +++ b/src/components/AddressTile/types.ts @@ -1,29 +1,13 @@ import { RawPkh } from "../../types/Address"; -type AddressKindType = - | "mnemonic" - | "secret_key" - | "social" - | "ledger" - | "multisig" - | "baker" - | "contact" - | "unknown"; - -type AddressKindBase = { - type: AddressKindType; - pkh: RawPkh; - label: string | null; -}; - -type MnemonicAddress = AddressKindBase & { type: "mnemonic"; label: string }; -type SecretKeyAddress = AddressKindBase & { type: "secret_key"; label: string }; -type SocialAddress = AddressKindBase & { type: "social"; label: string }; -type LedgerAddress = AddressKindBase & { type: "ledger"; label: string }; -export type OwnedMultisigAddress = AddressKindBase & { type: "multisig"; label: string }; -export type BakerAddress = AddressKindBase & { type: "baker"; label: string }; -export type ContactAddress = AddressKindBase & { type: "contact"; label: string }; -export type UnknownAddress = AddressKindBase & { type: "unknown"; label: null }; +type MnemonicAddress = { type: "mnemonic"; pkh: RawPkh; label: string }; +type SecretKeyAddress = { type: "secret_key"; pkh: RawPkh; label: string }; +type SocialAddress = { type: "social"; pkh: RawPkh; label: string }; +type LedgerAddress = { type: "ledger"; pkh: RawPkh; label: string }; +export type OwnedMultisigAddress = { type: "multisig"; pkh: RawPkh; label: string }; +export type BakerAddress = { type: "baker"; pkh: RawPkh; label: string }; +export type ContactAddress = { type: "contact"; pkh: RawPkh; label: string }; +export type UnknownAddress = { type: "unknown"; pkh: RawPkh; label: null }; export type OwnedAddress = | MnemonicAddress diff --git a/src/components/Onboarding/restoreMnemonic/RestoreMnemonic.tsx b/src/components/Onboarding/restoreMnemonic/RestoreMnemonic.tsx index 9c69009261..f93ba5123e 100644 --- a/src/components/Onboarding/restoreMnemonic/RestoreMnemonic.tsx +++ b/src/components/Onboarding/restoreMnemonic/RestoreMnemonic.tsx @@ -76,7 +76,7 @@ export const RestoreMnemonic = ({ goToStep }: { goToStep: (step: OnboardingStep) return ( } + icon={} subtitle="Please fill in the Seed Phrase in sequence." title="Import Seed Phrase" > diff --git a/src/components/Onboarding/restoreSecretKey/RestoreSecretKey.tsx b/src/components/Onboarding/restoreSecretKey/RestoreSecretKey.tsx index 88f0dc0ac6..3670e9abc5 100644 --- a/src/components/Onboarding/restoreSecretKey/RestoreSecretKey.tsx +++ b/src/components/Onboarding/restoreSecretKey/RestoreSecretKey.tsx @@ -4,7 +4,6 @@ import { useState } from "react"; import { FormProvider, useForm } from "react-hook-form"; import { KeyIcon } from "../../../assets/icons"; -import colors from "../../../style/colors"; import { useAsyncActionHandler } from "../../../utils/hooks/useAsyncActionHandler"; import { isEncryptedSecretKeyPrefix } from "../../../utils/redux/thunks/secretKeyAccount"; import { PasswordInput } from "../../PasswordInput"; @@ -60,10 +59,7 @@ export const RestoreSecretKey = ({ goToStep }: { goToStep: (step: OnboardingStep }); return ( - } - title="Insert Secret Key" - > + } title="Insert Secret Key">
diff --git a/src/components/Onboarding/showSeedphrase/ShowSeedphrase.tsx b/src/components/Onboarding/showSeedphrase/ShowSeedphrase.tsx index 4b6e72fb2a..fb0f6f682e 100644 --- a/src/components/Onboarding/showSeedphrase/ShowSeedphrase.tsx +++ b/src/components/Onboarding/showSeedphrase/ShowSeedphrase.tsx @@ -14,7 +14,7 @@ export const ShowSeedphrase = ({ account: ShowSeedphraseStep["account"]; }) => ( } + icon={} subtitle="Please record the following 24 words in sequence in order to restore it in the future." title="Record Seed Phrase" > diff --git a/src/views/batch/AccountSmallTile.test.tsx b/src/views/batch/AccountSmallTile.test.tsx index c60f446551..6b4609c805 100644 --- a/src/views/batch/AccountSmallTile.test.tsx +++ b/src/views/batch/AccountSmallTile.test.tsx @@ -6,12 +6,12 @@ import { formatPkh } from "../../utils/format"; import { assetsActions } from "../../utils/redux/slices/assetsSlice"; import { store } from "../../utils/redux/store"; -beforeEach(() => addAccount(mockMnemonicAccount(1, "Test account label"))); +const account = mockMnemonicAccount(1, "Test account label"); + +beforeEach(() => addAccount(account)); describe("", () => { it("shows account label", () => { - const account = mockImplicitAccount(1); - render(); expect(screen.getByTestId("account-small-tile-label")).toHaveTextContent("Test account label"); diff --git a/src/views/batch/AccountSmallTile.tsx b/src/views/batch/AccountSmallTile.tsx index 3c82b842f7..eaa0427e68 100644 --- a/src/views/batch/AccountSmallTile.tsx +++ b/src/views/batch/AccountSmallTile.tsx @@ -1,12 +1,10 @@ import { Flex, FlexProps, Heading, Text } from "@chakra-ui/react"; -import { AddressTileIcon } from "../../components/AddressTile/AddressTileIcon"; -import { useAddressKind } from "../../components/AddressTile/useAddressKind"; +import { AccountTileIcon } from "../../components/AccountTile/AccountTileIcon"; import colors from "../../style/colors"; import { Account } from "../../types/Account"; import { formatPkh, prettyTezAmount } from "../../utils/format"; import { useGetAccountBalance } from "../../utils/hooks/assetsHooks"; -import { useAllAccounts } from "../../utils/hooks/getAccountDataHooks"; /** * Component used to display account info for batched transactions. @@ -16,17 +14,9 @@ import { useAllAccounts } from "../../utils/hooks/getAccountDataHooks"; * @param account - Implicit or Multisig account. * @param flexProps - Flex properties to define component style. */ -export const AccountSmallTile = ({ - account: { address }, - ...flexProps -}: { account: Account } & FlexProps) => { - const account = useAllAccounts().find(a => a.address.pkh === address.pkh); - const balance = useGetAccountBalance()(address.pkh); - const addressKind = useAddressKind(address); +export const AccountSmallTile = ({ account, ...flexProps }: { account: Account } & FlexProps) => { + const balance = useGetAccountBalance()(account.address.pkh); - if (!account) { - return null; - } return ( - + {account.label} @@ -45,7 +35,7 @@ export const AccountSmallTile = ({ data-testid="account-small-tile-pkh" size="xs" > - {formatPkh(address.pkh)} + {formatPkh(account.address.pkh)} {balance && ( diff --git a/src/views/home/AccountsList.tsx b/src/views/home/AccountsList.tsx index b4575ed711..ed8b77262c 100644 --- a/src/views/home/AccountsList.tsx +++ b/src/views/home/AccountsList.tsx @@ -48,7 +48,7 @@ export const AccountsList = () => { color={colors.gray[400]} textAlign="center" > - + Create New Multisig diff --git a/src/views/tokens/AccountTokens.tsx b/src/views/tokens/AccountTokens.tsx index e995b0697e..3a00bd00bd 100644 --- a/src/views/tokens/AccountTokens.tsx +++ b/src/views/tokens/AccountTokens.tsx @@ -47,7 +47,7 @@ const Header: React.FC<{ data-testid="header" paddingX="30px" > - + From 8f5e721a23fffc6ea9c632c9b974b07353e829a8 Mon Sep 17 00:00:00 2001 From: Sergey Kintsel Date: Wed, 22 May 2024 12:31:14 +0100 Subject: [PATCH 16/18] Add tests for the social auth --- jest.config.ts | 4 +- package.json | 2 +- src/auth/Auth.test.ts | 96 +++++++++++++++++++++++ src/auth/Auth.ts | 7 +- src/auth/EmailAuth.ts | 2 +- src/auth/FacebookAuth.ts | 2 +- src/auth/GoogleAuth.ts | 2 +- src/auth/RedditAuth.ts | 3 +- src/auth/TwitterAuth.ts | 2 +- src/auth/forIDP.test.ts | 18 +++++ src/auth/forIDP.ts | 3 + src/components/Onboarding/FakeAccount.tsx | 2 +- 12 files changed, 134 insertions(+), 9 deletions(-) create mode 100644 src/auth/Auth.test.ts create mode 100644 src/auth/forIDP.test.ts diff --git a/jest.config.ts b/jest.config.ts index 50060dbd93..ad9b757172 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -3,6 +3,8 @@ * https://jestjs.io/docs/configuration */ +import process from "process"; + import type { Config } from "jest"; const config: Config = { @@ -19,7 +21,7 @@ const config: Config = { clearMocks: true, // Indicates whether the coverage information should be collected while executing the test - collectCoverage: true, + collectCoverage: process.env.DEV !== "true", // An array of glob patterns indicating a set of files for which coverage information should be collected // collectCoverageFrom: undefined, diff --git a/package.json b/package.json index 986b01d6f9..e53fea35e8 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "lint:ci": "eslint src --ext .js,.jsx,.ts,.tsx --max-warnings=0", "lint": "eslint src --ext .js,.jsx,.ts,.tsx --fix", "test": "cross-env TZ=CET jest", - "test:watch": "yarn test --watch", + "test:watch": "cross-env DEV=true yarn test --watch", "test:e2e": "cross-env TS_NODE_PROJECT=tsconfig.e2e.json cucumber-js", "test:e2e:focus": "yarn test:e2e --tags='@focus'", "theme:watch": "chakra-cli tokens src/style/theme.ts --watch", diff --git a/src/auth/Auth.test.ts b/src/auth/Auth.test.ts new file mode 100644 index 0000000000..f1a4a16d9d --- /dev/null +++ b/src/auth/Auth.test.ts @@ -0,0 +1,96 @@ +import { TorusAggregateLoginResponse, TorusLoginResponse } from "@toruslabs/customauth"; + +import { Auth } from "./Auth"; +import { EmailAuth } from "./EmailAuth"; +import { FacebookAuth } from "./FacebookAuth"; +import { GoogleAuth } from "./GoogleAuth"; +import { RedditAuth } from "./RedditAuth"; +import { TwitterAuth } from "./TwitterAuth"; + +const rawSecretKey = "ad02df00ce58f9e3e1ff882661edbfa4e5a31b9ebceaa4e3e1fe810fb2ba38f2"; +const secretKey = "spsk2jm29sHC99HDi64VBpSwEMZRQ7WfHdvQPVMZCkyWyR4spBrtRW"; + +describe("Auth", () => { + const testCases = [ + { + finalKey: rawSecretKey, + oAuthKey: rawSecretKey, + email: "some email", + name: "some name", + resultKey: secretKey, + resultName: "some email", + }, + { + finalKey: rawSecretKey, + oAuthKey: rawSecretKey, + email: undefined, + name: "some name", + resultKey: secretKey, + resultName: "some name", + }, + { + finalKey: rawSecretKey, + oAuthKey: rawSecretKey, + email: undefined, + name: undefined, + resultKey: secretKey, + resultName: (p: Auth) => p.idpName, + }, + { + finalKey: rawSecretKey, + oAuthKey: rawSecretKey + "some extra", + email: undefined, + name: undefined, + resultKey: secretKey, + resultName: (p: Auth) => p.idpName, + }, + { + finalKey: undefined, + oAuthKey: rawSecretKey, + email: "some email", + name: "some name", + resultKey: secretKey, + resultName: "some email", + }, + ]; + + describe.each(testCases)( + "for $finalKey, $oAuthKey, $email, $name", + ({ finalKey, oAuthKey, email, name, resultKey, resultName }) => { + it.each([GoogleAuth, EmailAuth, RedditAuth])( + "handles aggregated login for %p", + async Provider => { + const provider = new Provider(); + jest.spyOn(provider, "login").mockImplementation(() => + Promise.resolve({ + finalKeyData: { privKey: finalKey }, + oAuthKeyData: { privKey: oAuthKey }, + userInfo: [{ email, name }], + } as TorusAggregateLoginResponse) + ); + + await expect(provider.getCredentials()).resolves.toEqual({ + secretKey: resultKey, + name: typeof resultName === "function" ? resultName(provider) : resultName, + }); + } + ); + + it.each([FacebookAuth, TwitterAuth])("handles login for %p", async Provider => { + const provider = new Provider(); + jest.spyOn(provider, "login").mockImplementation(() => + Promise.resolve({ + finalKeyData: { privKey: finalKey }, + oAuthKeyData: { privKey: oAuthKey }, + userInfo: { email, name }, + } as TorusLoginResponse) + ); + + await expect(provider.getCredentials()).resolves.toEqual({ + secretKey: resultKey, + name: typeof resultName === "function" ? resultName(provider) : resultName, + }); + }); + } + ); +}); diff --git a/src/auth/Auth.ts b/src/auth/Auth.ts index 60ec1c84af..6445b8a439 100644 --- a/src/auth/Auth.ts +++ b/src/auth/Auth.ts @@ -6,6 +6,11 @@ import { IDP } from "./types"; const WEB3_AUTH_CLIENT_ID = "BBQoFIabI50S1-0QsGHGTM4qID_FDjja0ZxIxKPyFqc0El--M-EG0c2giaBYVTVVE6RC9WCUzCJyW24aJrR_Lzc"; +/** + * Abstract class that's responsible for the social auth process + * + * the details are defined in the subclasses. + */ export abstract class Auth { abstract idpName: IDP; abstract clientId: string; @@ -25,7 +30,7 @@ export abstract class Auth { return torus; } - protected abstract login(): Promise; + abstract login(): Promise; async getCredentials(): Promise<{ secretKey: string; diff --git a/src/auth/EmailAuth.ts b/src/auth/EmailAuth.ts index c6df7a878a..2553c41a31 100644 --- a/src/auth/EmailAuth.ts +++ b/src/auth/EmailAuth.ts @@ -6,7 +6,7 @@ export class EmailAuth extends Auth { clientId = "LTg6fVsacafGmhv14TZlrWF1EavwQoDZ"; idpName: IDP = "email"; - protected override async login() { + override async login() { const client = await this.getTorusClient(); return client.triggerAggregateLogin({ diff --git a/src/auth/FacebookAuth.ts b/src/auth/FacebookAuth.ts index 242f919ff1..b10752a7c5 100644 --- a/src/auth/FacebookAuth.ts +++ b/src/auth/FacebookAuth.ts @@ -5,7 +5,7 @@ export class FacebookAuth extends Auth { idpName: IDP = "facebook"; clientId = "523634882377310"; - protected override async login() { + override async login() { const client = await this.getTorusClient(); return client.triggerLogin({ diff --git a/src/auth/GoogleAuth.ts b/src/auth/GoogleAuth.ts index ea39dc51e9..6f4ea352b2 100644 --- a/src/auth/GoogleAuth.ts +++ b/src/auth/GoogleAuth.ts @@ -5,7 +5,7 @@ export class GoogleAuth extends Auth { idpName: IDP = "google"; clientId = "1070572364808-d31nlkneam5ee6dr0tu28fjjbsdkfta5.apps.googleusercontent.com"; - protected override async login() { + override async login() { const client = await this.getTorusClient(); return client.triggerAggregateLogin({ diff --git a/src/auth/RedditAuth.ts b/src/auth/RedditAuth.ts index f4b485f487..366a925869 100644 --- a/src/auth/RedditAuth.ts +++ b/src/auth/RedditAuth.ts @@ -5,7 +5,8 @@ import type { IDP } from "./types"; export class RedditAuth extends Auth { idpName: IDP = "reddit"; clientId = "zyQ9tnKfdg3VNyj6MGhZq4dHbBzbmEvl"; - protected override async login() { + + override async login() { const client = await this.getTorusClient(); return client.triggerAggregateLogin({ diff --git a/src/auth/TwitterAuth.ts b/src/auth/TwitterAuth.ts index 4128b27ec1..2b19665c00 100644 --- a/src/auth/TwitterAuth.ts +++ b/src/auth/TwitterAuth.ts @@ -6,7 +6,7 @@ export class TwitterAuth extends Auth { idpName: IDP = "twitter"; clientId = "3aCoxh3pw8g8JeFsdlJNUGwdgtLwdwgE"; - protected override async login() { + override async login() { const client = await this.getTorusClient(); return client.triggerLogin({ diff --git a/src/auth/forIDP.test.ts b/src/auth/forIDP.test.ts new file mode 100644 index 0000000000..1bd33a6813 --- /dev/null +++ b/src/auth/forIDP.test.ts @@ -0,0 +1,18 @@ +import { EmailAuth } from "./EmailAuth"; +import { FacebookAuth } from "./FacebookAuth"; +import { forIDP } from "./forIDP"; +import { GoogleAuth } from "./GoogleAuth"; +import { RedditAuth } from "./RedditAuth"; +import { TwitterAuth } from "./TwitterAuth"; + +describe("forIDP", () => { + it.each([ + { idp: "google" as const, provider: GoogleAuth }, + { idp: "facebook" as const, provider: FacebookAuth }, + { idp: "reddit" as const, provider: RedditAuth }, + { idp: "twitter" as const, provider: TwitterAuth }, + { idp: "email" as const, provider: EmailAuth }, + ])("creates an instance for the $idp IDP", ({ idp, provider }) => { + expect(forIDP(idp)).toBeInstanceOf(provider); + }); +}); diff --git a/src/auth/forIDP.ts b/src/auth/forIDP.ts index 0d2fc19723..88da6e3f10 100644 --- a/src/auth/forIDP.ts +++ b/src/auth/forIDP.ts @@ -5,6 +5,9 @@ import { RedditAuth } from "./RedditAuth"; import { TwitterAuth } from "./TwitterAuth"; import type { IDP } from "./types"; +/** + * Returns the Auth instance for the given IDP + */ export const forIDP = (idp: IDP) => { switch (idp) { case "google": diff --git a/src/components/Onboarding/FakeAccount.tsx b/src/components/Onboarding/FakeAccount.tsx index 25b417f5f0..b4dff5cf20 100644 --- a/src/components/Onboarding/FakeAccount.tsx +++ b/src/components/Onboarding/FakeAccount.tsx @@ -18,7 +18,7 @@ export const FakeAccount = ({ onClose }: { onClose: () => void }) => { register, handleSubmit, formState: { errors }, - } = useForm<{ pkh: string; name: string; idp?: string }>({ mode: "onBlur" }); + } = useForm<{ pkh: string; name: string; idp?: IDP }>({ mode: "onBlur" }); const restoreLedger = useRestoreLedger(); const restoreSocial = useRestoreSocial(); From 053087f351bf559270fb5868d2c10e4f0cd6ace7 Mon Sep 17 00:00:00 2001 From: Sergey Kintsel Date: Wed, 22 May 2024 19:57:45 +0100 Subject: [PATCH 17/18] Upgrade customauth to 16.0.6 --- package.json | 2 +- src/auth/Auth.ts | 2 +- yarn.lock | 274 ++++++++++++++++++++++++++--------------------- 3 files changed, 156 insertions(+), 122 deletions(-) diff --git a/package.json b/package.json index e53fea35e8..7a5818a70a 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "@testing-library/jest-dom": "6.4.5", "@testing-library/react": "14.3.0", "@testing-library/user-event": "14.5.2", - "@toruslabs/customauth": "^15.0.5", + "@toruslabs/customauth": "^16.0.6", "@types/babel__core": "^7", "@types/babel__preset-env": "^7", "@types/identity-obj-proxy": "^3", diff --git a/src/auth/Auth.ts b/src/auth/Auth.ts index 6445b8a439..00a93d7251 100644 --- a/src/auth/Auth.ts +++ b/src/auth/Auth.ts @@ -18,7 +18,7 @@ export abstract class Auth { protected async getTorusClient(): Promise { const torus = new CustomAuth({ web3AuthClientId: WEB3_AUTH_CLIENT_ID, - baseUrl: "https://umamiwallet.com/auth/v2.0.1/", + baseUrl: "https://umamiwallet.com/auth/v2.2.0/", redirectPathName: "redirect.html", redirectToOpener: true, uxMode: "popup", diff --git a/yarn.lock b/yarn.lock index 375ff30889..9e7278d7fe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1968,7 +1968,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.21.5, @babel/runtime@npm:^7.4.2, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.4.2, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": version: 7.22.10 resolution: "@babel/runtime@npm:7.22.10" dependencies: @@ -2004,7 +2004,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.24.5": +"@babel/runtime@npm:^7.22.10, @babel/runtime@npm:^7.24.5": version: 7.24.5 resolution: "@babel/runtime@npm:7.24.5" dependencies: @@ -2167,9 +2167,9 @@ __metadata: linkType: hard "@chaitanyapotti/register-service-worker@npm:^1.7.3": - version: 1.7.3 - resolution: "@chaitanyapotti/register-service-worker@npm:1.7.3" - checksum: 10/995b875e434a11dcb24ccde8d7914e2f6f95047e83278f5747cfd0d83813a81c712b6794d60e7371ff92734393b5a399e31e68fa5fa5a8a2ab91aa9bdffc8e4d + version: 1.7.4 + resolution: "@chaitanyapotti/register-service-worker@npm:1.7.4" + checksum: 10/77df892ffcac378c57aaccd1c7ffb98619a15c04296c8a9bf9813756073f84351d674483fe5a56b91fa3ca3280234f9987f757cb7c1c57faec9c974997034611 languageName: node linkType: hard @@ -4748,16 +4748,23 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:1.0.0, @noble/curves@npm:~1.0.0": - version: 1.0.0 - resolution: "@noble/curves@npm:1.0.0" +"@noble/curves@npm:1.3.0, @noble/curves@npm:~1.3.0": + version: 1.3.0 + resolution: "@noble/curves@npm:1.3.0" dependencies: - "@noble/hashes": "npm:1.3.0" - checksum: 10/6db884e03b3f6c773317bcf4611bf1d9adb8084eab0bf6158407cc998c9c5dcb0560741bdd0aaca9c4393c9e8a3dcd7592b4148a6cfd561d0a00addb77a6129f + "@noble/hashes": "npm:1.3.3" + checksum: 10/f3cbdd1af00179e30146eac5539e6df290228fb857a7a8ba36d1a772cbe59288a2ca83d06f175d3446ef00db3a80d7fd8b8347f7de9c2d4d5bf3865d8bb78252 languageName: node linkType: hard -"@noble/hashes@npm:1.3.0, @noble/hashes@npm:^1.2.0, @noble/hashes@npm:~1.3.0": +"@noble/hashes@npm:1.3.3, @noble/hashes@npm:~1.3.2": + version: 1.3.3 + resolution: "@noble/hashes@npm:1.3.3" + checksum: 10/1025ddde4d24630e95c0818e63d2d54ee131b980fe113312d17ed7468bc18f54486ac86c907685759f8a7e13c2f9b9e83ec7b67d1cc20836f36b5e4a65bb102d + languageName: node + linkType: hard + +"@noble/hashes@npm:^1.2.0": version: 1.3.0 resolution: "@noble/hashes@npm:1.3.0" checksum: 10/4680a71941c06ac897cc9eab9d229717d5af1147cea5e8cd4942190c817426ad3173ded750d897f58d764b869f9347d4fc3f6b3c16574541ac81906efa9ddc36 @@ -5007,31 +5014,31 @@ __metadata: languageName: node linkType: hard -"@scure/base@npm:~1.1.0": - version: 1.1.1 - resolution: "@scure/base@npm:1.1.1" - checksum: 10/9aaa525ac25215cbe1bde00733a2fd25e99f03793aa1fd2961c567bb62b60c8a3a485a7cb5d748c41604fca79d149de19b05e64449b770c0a04b9ae38d0b5b2b +"@scure/base@npm:~1.1.4": + version: 1.1.6 + resolution: "@scure/base@npm:1.1.6" + checksum: 10/814fd1cce24f1e152751fabca2853d26aaa96ff8a9349c43d9aebc3b3d8ca88dd902966e1c289590a37f35d4c4436c6aedb1b386924b2909072045af4c3e9fe4 languageName: node linkType: hard -"@scure/bip32@npm:1.3.0": - version: 1.3.0 - resolution: "@scure/bip32@npm:1.3.0" +"@scure/bip32@npm:1.3.3": + version: 1.3.3 + resolution: "@scure/bip32@npm:1.3.3" dependencies: - "@noble/curves": "npm:~1.0.0" - "@noble/hashes": "npm:~1.3.0" - "@scure/base": "npm:~1.1.0" - checksum: 10/1fabcc7f2215910b35980bfc455c03fc4ae7f848efed066fe3867960a8dfceb6141c932496434fc2cfbf385d270ff9efdfce2571992e4584103f82e45ac2103f + "@noble/curves": "npm:~1.3.0" + "@noble/hashes": "npm:~1.3.2" + "@scure/base": "npm:~1.1.4" + checksum: 10/4b8b75567866ff7d6b3ba154538add02d2951e9433e8dd7f0014331ac500cda5a88fe3d39b408fcc36e86b633682013f172b967af022c2e4e4ab07336801d688 languageName: node linkType: hard -"@scure/bip39@npm:1.2.0": - version: 1.2.0 - resolution: "@scure/bip39@npm:1.2.0" +"@scure/bip39@npm:1.2.2": + version: 1.2.2 + resolution: "@scure/bip39@npm:1.2.2" dependencies: - "@noble/hashes": "npm:~1.3.0" - "@scure/base": "npm:~1.1.0" - checksum: 10/2a260eefea0b2658c5d3b2cb982479ef650552c3007e57f667b445943c79717eb923c1a104a664b4873bc210aeb59859bf890c3e7b47fb51ed5b94dc96f75105 + "@noble/hashes": "npm:~1.3.2" + "@scure/base": "npm:~1.1.4" + checksum: 10/f71aceda10a7937bf3779fd2b4c4156c95ec9813269470ddca464cb8ab610d2451b173037f4b1e6dac45414e406e7adc7b5814c51279f4474d5d38140bbee542 languageName: node linkType: hard @@ -5645,43 +5652,44 @@ __metadata: languageName: node linkType: hard -"@toruslabs/broadcast-channel@npm:^7.0.0": - version: 7.0.0 - resolution: "@toruslabs/broadcast-channel@npm:7.0.0" +"@toruslabs/broadcast-channel@npm:^8.0.0": + version: 8.0.0 + resolution: "@toruslabs/broadcast-channel@npm:8.0.0" dependencies: - "@babel/runtime": "npm:^7.21.5" - "@toruslabs/eccrypto": "npm:^3.0.0" - "@toruslabs/metadata-helpers": "npm:^4.0.0" + "@babel/runtime": "npm:^7.22.10" + "@toruslabs/eccrypto": "npm:^4.0.0" + "@toruslabs/metadata-helpers": "npm:^5.0.0" bowser: "npm:^2.11.0" loglevel: "npm:^1.8.1" oblivious-set: "npm:1.1.1" - socket.io-client: "npm:^4.6.1" + socket.io-client: "npm:^4.7.2" unload: "npm:^2.4.1" - checksum: 10/984778b9ff158c1e2f3673dffe3e64f47ec6ff99e75f387336021fb2a6a6dfd7333f45bac4824fda916931259d94d6155be305a6cc0feb6887bff2591385780e + checksum: 10/146f792025114615d349b5ca36fb6eab7d6963b1ff7ee8a0582a31f3a4285cad79d25c2fa56aa9688199273fe4f43fb01fe4f1a19c628ab3745ed405faa97037 languageName: node linkType: hard -"@toruslabs/constants@npm:^11.0.0": - version: 11.0.0 - resolution: "@toruslabs/constants@npm:11.0.0" +"@toruslabs/constants@npm:^13.0.1, @toruslabs/constants@npm:^13.4.0": + version: 13.4.0 + resolution: "@toruslabs/constants@npm:13.4.0" peerDependencies: "@babel/runtime": 7.x - checksum: 10/60ff98b61fdae18fdb164d8f8ccc87e464fa9d8e2e26e9d2014f0aea766266603f95ee5e1ba187ae85f2510755e30ffcec912212f3c4e6cdd420d3413d942b4d + checksum: 10/eb4d1446474e66cd8d820eed6ea4beb21fa1563d0545fa77e21803153ac6673260d54050e90ab98c90c82572f885fa19cd88e08dfee37df3fb5345809609959c languageName: node linkType: hard -"@toruslabs/customauth@npm:^15.0.5": - version: 15.0.5 - resolution: "@toruslabs/customauth@npm:15.0.5" +"@toruslabs/customauth@npm:^16.0.6": + version: 16.0.6 + resolution: "@toruslabs/customauth@npm:16.0.6" dependencies: "@chaitanyapotti/register-service-worker": "npm:^1.7.3" - "@toruslabs/broadcast-channel": "npm:^7.0.0" - "@toruslabs/constants": "npm:^11.0.0" - "@toruslabs/eccrypto": "npm:^3.0.0" - "@toruslabs/fetch-node-details": "npm:^11.0.1" - "@toruslabs/http-helpers": "npm:^4.0.0" - "@toruslabs/metadata-helpers": "npm:^4.0.0" - "@toruslabs/torus.js": "npm:^10.0.5" + "@toruslabs/broadcast-channel": "npm:^8.0.0" + "@toruslabs/constants": "npm:^13.0.1" + "@toruslabs/eccrypto": "npm:^4.0.0" + "@toruslabs/fetch-node-details": "npm:^13.0.1" + "@toruslabs/http-helpers": "npm:^5.0.0" + "@toruslabs/metadata-helpers": "npm:^5.0.0" + "@toruslabs/torus.js": "npm:^11.0.5" + base64url: "npm:^3.0.1" bowser: "npm:^2.11.0" events: "npm:^3.3.0" jwt-decode: "npm:^3.1.2" @@ -5693,47 +5701,47 @@ __metadata: peerDependenciesMeta: "@sentry/types": optional: true - checksum: 10/172632508c1b942cd9b705193ad30515a79a7a6734eb70b1126860bbbb9d6f99418ecfee0d77eeadc59f819703e812de70830783b98b049fc6ec288b077281a0 + checksum: 10/8520ed2e66074d5686a641e168481a9c3d63401db6aea86dba8e61bfe8446a86ba4aaf5d75990580f3a6b430b7469117c6c2ded26a332e32fd4bdda73ff50f1e languageName: node linkType: hard -"@toruslabs/eccrypto@npm:^3.0.0": - version: 3.0.0 - resolution: "@toruslabs/eccrypto@npm:3.0.0" +"@toruslabs/eccrypto@npm:^4.0.0": + version: 4.0.0 + resolution: "@toruslabs/eccrypto@npm:4.0.0" dependencies: elliptic: "npm:^6.5.4" - checksum: 10/14e56f63ed0b1b4d8a97bd13243699babb2981ba63eb6594417e2feaadba0847f33cfd2b56bb4f1ec3b90351e8020b80552a617420b24e2681fe6065ac1f7ad3 + checksum: 10/9b8bcc2ccb8c21bd2c4170303a21e4e4e3563b4a44a0a43cc11d0587b6a01029ab0cdccc7b88b4c861ec07f841ee1cdcba7b35e7e07b5abff307f753c31dfdff languageName: node linkType: hard -"@toruslabs/fetch-node-details@npm:^11.0.1": - version: 11.0.1 - resolution: "@toruslabs/fetch-node-details@npm:11.0.1" +"@toruslabs/fetch-node-details@npm:^13.0.1": + version: 13.4.0 + resolution: "@toruslabs/fetch-node-details@npm:13.4.0" dependencies: - "@toruslabs/constants": "npm:^11.0.0" - "@toruslabs/fnd-base": "npm:^11.0.1" - "@toruslabs/http-helpers": "npm:^4.0.0" - loglevel: "npm:^1.8.1" + "@toruslabs/constants": "npm:^13.4.0" + "@toruslabs/fnd-base": "npm:^13.4.0" + "@toruslabs/http-helpers": "npm:^6.1.1" + loglevel: "npm:^1.9.1" peerDependencies: "@babel/runtime": 7.x - checksum: 10/9ef0614db509538a007a294aeb59b36de9f2272c4f2c170413341e3deba155ad1ffa9573139554160bf730227f60be062fdacff8a8f923954debe8cd109f4d62 + checksum: 10/515638ac2fb9298472db7b5945ab67fa8be6f9512e1b2a05f5f68e8a42dcaca71c64ab830b706da55dffe65cf542ef22a400070cb63736285f2613225c61e10e languageName: node linkType: hard -"@toruslabs/fnd-base@npm:^11.0.1": - version: 11.0.1 - resolution: "@toruslabs/fnd-base@npm:11.0.1" +"@toruslabs/fnd-base@npm:^13.4.0": + version: 13.4.0 + resolution: "@toruslabs/fnd-base@npm:13.4.0" dependencies: - "@toruslabs/constants": "npm:^11.0.0" + "@toruslabs/constants": "npm:^13.4.0" peerDependencies: "@babel/runtime": 7.x - checksum: 10/773f59d53efc0ecc38f18677087c82648e3434c317331c6f25f1f4e06a804abf262a06b7ae56ad4854e3b5cb81aeea28109189e4bab78387e89c488bcb46e643 + checksum: 10/e728642d476384e8bc5ab295bb4aaa995d35e4174aa04c2a92a1470c9cb4098de86e4e545801f41c823f42c67a7d60a825c0f83e0e6e88eb8b35d369bd7e8c7e languageName: node linkType: hard -"@toruslabs/http-helpers@npm:^4.0.0": - version: 4.0.0 - resolution: "@toruslabs/http-helpers@npm:4.0.0" +"@toruslabs/http-helpers@npm:^5.0.0": + version: 5.0.0 + resolution: "@toruslabs/http-helpers@npm:5.0.0" dependencies: lodash.merge: "npm:^4.6.2" loglevel: "npm:^1.8.1" @@ -5743,40 +5751,56 @@ __metadata: peerDependenciesMeta: "@sentry/types": optional: true - checksum: 10/60a718a9ed760061fcf77e3bb7d0667a725278885b8e2d06fd270ba67391bcae5974cc25f59496848cee76e250fa1984197e89b8cd9ce3b0096eda12c4f7b0ac + checksum: 10/ae87bb8c8e73f3ccf7c62fd86e99129031726cddf51efad4420cea9ad053f2c98e06dcb0acddb7e0a67ff759cd16d2b9b672e6909dd779bcb39c8e9caa548505 languageName: node linkType: hard -"@toruslabs/metadata-helpers@npm:^4.0.0": - version: 4.0.0 - resolution: "@toruslabs/metadata-helpers@npm:4.0.0" +"@toruslabs/http-helpers@npm:^6.1.0, @toruslabs/http-helpers@npm:^6.1.1": + version: 6.1.1 + resolution: "@toruslabs/http-helpers@npm:6.1.1" dependencies: - "@toruslabs/eccrypto": "npm:^3.0.0" - "@toruslabs/http-helpers": "npm:^4.0.0" - elliptic: "npm:^6.5.4" - ethereum-cryptography: "npm:^2.0.0" - json-stable-stringify: "npm:^1.0.2" + lodash.merge: "npm:^4.6.2" + loglevel: "npm:^1.9.1" + peerDependencies: + "@babel/runtime": ^7.x + "@sentry/types": ^7.x + peerDependenciesMeta: + "@sentry/types": + optional: true + checksum: 10/1d26bf7d3012d061b88792cb75ea604f0ffd4f220082d8c29bd59c028803f8b04d4366e47da270b711dde381a69c2d73defbfc944d9454d513f5ecf9d48d29da + languageName: node + linkType: hard + +"@toruslabs/metadata-helpers@npm:^5.0.0": + version: 5.1.0 + resolution: "@toruslabs/metadata-helpers@npm:5.1.0" + dependencies: + "@toruslabs/eccrypto": "npm:^4.0.0" + "@toruslabs/http-helpers": "npm:^6.1.0" + elliptic: "npm:^6.5.5" + ethereum-cryptography: "npm:^2.1.3" + json-stable-stringify: "npm:^1.1.1" peerDependencies: "@babel/runtime": 7.x - checksum: 10/99f8fd2ca91a570ee481dd0c1b495272c5db08120d3715de6dae3d14eae80a4e429c7861c163097a26e499e3731d9ca17f8cc2c0633d0b0ca731af6484779909 + checksum: 10/1037864c0170240cf26af607ecb5443ddec169ce86101aec197bc71cc407602df8449cc2f44ccb4e7a4c7b989b01d0aabbc75a0c923a4004430ae5422896010b languageName: node linkType: hard -"@toruslabs/torus.js@npm:^10.0.5": - version: 10.0.5 - resolution: "@toruslabs/torus.js@npm:10.0.5" +"@toruslabs/torus.js@npm:^11.0.5": + version: 11.0.6 + resolution: "@toruslabs/torus.js@npm:11.0.6" dependencies: - "@toruslabs/constants": "npm:^11.0.0" - "@toruslabs/eccrypto": "npm:^3.0.0" - "@toruslabs/http-helpers": "npm:^4.0.0" + "@toruslabs/constants": "npm:^13.0.1" + "@toruslabs/eccrypto": "npm:^4.0.0" + "@toruslabs/http-helpers": "npm:^5.0.0" bn.js: "npm:^5.2.1" elliptic: "npm:^6.5.4" - ethereum-cryptography: "npm:^2.0.0" + ethereum-cryptography: "npm:^2.1.2" json-stable-stringify: "npm:^1.0.2" loglevel: "npm:^1.8.1" peerDependencies: "@babel/runtime": 7.x - checksum: 10/c046671a10658c41bfa9b106d472c1c2d8c6669d6b880916c0cb61d26f5cd7336389021332e06464e3097668bf1c7bb30bd38442c2a6024a7c51ffd662df36a9 + checksum: 10/0c5afcf65eadd729782e4fd16b013cc22cdb229259c600d89c333fcd75bfc614ee60b8c874207e834cc53b23e6d538a26fe4fa45c57b7945099ec1af039b6724 languageName: node linkType: hard @@ -7589,6 +7613,13 @@ __metadata: languageName: node linkType: hard +"base64url@npm:^3.0.1": + version: 3.0.1 + resolution: "base64url@npm:3.0.1" + checksum: 10/a77b2a3a526b3343e25be424de3ae0aa937d78f6af7c813ef9020ef98001c0f4e2323afcd7d8b2d2978996bf8c42445c3e9f60c218c622593e5fdfd54a3d6e18 + languageName: node + linkType: hard + "basic-auth@npm:^2.0.1": version: 2.0.1 resolution: "basic-auth@npm:2.0.1" @@ -9320,23 +9351,23 @@ __metadata: languageName: node linkType: hard -"engine.io-client@npm:~6.5.1": - version: 6.5.1 - resolution: "engine.io-client@npm:6.5.1" +"engine.io-client@npm:~6.5.2": + version: 6.5.3 + resolution: "engine.io-client@npm:6.5.3" dependencies: "@socket.io/component-emitter": "npm:~3.1.0" debug: "npm:~4.3.1" - engine.io-parser: "npm:~5.1.0" + engine.io-parser: "npm:~5.2.1" ws: "npm:~8.11.0" xmlhttprequest-ssl: "npm:~2.0.0" - checksum: 10/827c6c203cd0cb113db99311ef34b74d424a6d6f2f61384d46cce9a8143dd27675f0fa046f7206aaac7f7611c25e7ba239fc2d531bffafaeac94628c70e94141 + checksum: 10/0d7c3e6de23f37706c163bc8a0e90e70e613c7768be0705bda3675124d5e24d849810fddda005f8dcc721da35aee713976a03a0465d71f0856adfc1af7a80e5d languageName: node linkType: hard -"engine.io-parser@npm:~5.1.0": - version: 5.1.0 - resolution: "engine.io-parser@npm:5.1.0" - checksum: 10/9997a91d0d609f95741fe68775947a0b5e2a4032ba7d15f4c14bfa4d70cca9d3a9e17575e1c9f6314ef002a966f243eb910cd0b537aeac7dbc27e6c4c26f60ec +"engine.io-parser@npm:~5.2.1": + version: 5.2.2 + resolution: "engine.io-parser@npm:5.2.2" + checksum: 10/135b1278547bde501412ac462e93b3b4f6a2fecc30a2b843bb9408b96301e8068bb2496c32d124a3d2544eb0aec8b8eddcb4ef0d0d0b84b7d642b1ffde1b2dcf languageName: node linkType: hard @@ -10204,15 +10235,15 @@ __metadata: languageName: node linkType: hard -"ethereum-cryptography@npm:^2.0.0": - version: 2.0.0 - resolution: "ethereum-cryptography@npm:2.0.0" +"ethereum-cryptography@npm:^2.1.2, ethereum-cryptography@npm:^2.1.3": + version: 2.1.3 + resolution: "ethereum-cryptography@npm:2.1.3" dependencies: - "@noble/curves": "npm:1.0.0" - "@noble/hashes": "npm:1.3.0" - "@scure/bip32": "npm:1.3.0" - "@scure/bip39": "npm:1.2.0" - checksum: 10/1f87b4d322fce0801d38741955df1dec20861939ea0c0a89dddf182906f21453f7134662e09fe268e35be9a3848f61667349836b5eb5f4efd6b9a02c1e3bcc85 + "@noble/curves": "npm:1.3.0" + "@noble/hashes": "npm:1.3.3" + "@scure/bip32": "npm:1.3.3" + "@scure/bip39": "npm:1.2.2" + checksum: 10/cc5aa9a4368dc1dd7680ba921957c098ced7b3d7dbb1666334013ab2f8d4cd25a785ad84e66fd9f5c5a9b6de337930ea24ff8c722938f36a9c00cec597ca16b5 languageName: node linkType: hard @@ -12735,12 +12766,15 @@ __metadata: languageName: node linkType: hard -"json-stable-stringify@npm:^1.0.2": - version: 1.0.2 - resolution: "json-stable-stringify@npm:1.0.2" +"json-stable-stringify@npm:^1.0.2, json-stable-stringify@npm:^1.1.1": + version: 1.1.1 + resolution: "json-stable-stringify@npm:1.1.1" dependencies: + call-bind: "npm:^1.0.5" + isarray: "npm:^2.0.5" jsonify: "npm:^0.0.1" - checksum: 10/96c8d697520072231c4916b7c0084ea857418cad0d06dc910f89a40df3824386a8eee5ed83ceea25b6052d67223fe821f9b1e51be311383104c5b2305b1dc87e + object-keys: "npm:^1.1.1" + checksum: 10/60853c1f63451319b5c7953465a555aa816cf84e60e3ca36b6c05225d8fdc4615127fb4ecb92f9f5ad880c552ab8cbae9a519f78b995e7788d6d89e57afafdeb languageName: node linkType: hard @@ -12968,10 +13002,10 @@ __metadata: languageName: node linkType: hard -"loglevel@npm:^1.8.1": - version: 1.8.1 - resolution: "loglevel@npm:1.8.1" - checksum: 10/36a786082a7e4f1d962de330122291da3a102b88dbde81a45eb92a045c38b0903783958ba39dce641440c0413da303410e7f2565f897bccad828853bd5974c86 +"loglevel@npm:^1.8.1, loglevel@npm:^1.9.1": + version: 1.9.1 + resolution: "loglevel@npm:1.9.1" + checksum: 10/863cbbcddf850a937482c604e2d11586574a5110b746bb49c7cc04739e01f6035f6db841d25377106dd330bca7142d74995f15a97c5f3ea0af86d9472d4a99f4 languageName: node linkType: hard @@ -15930,15 +15964,15 @@ __metadata: languageName: node linkType: hard -"socket.io-client@npm:^4.6.1": - version: 4.7.1 - resolution: "socket.io-client@npm:4.7.1" +"socket.io-client@npm:^4.7.2": + version: 4.7.5 + resolution: "socket.io-client@npm:4.7.5" dependencies: "@socket.io/component-emitter": "npm:~3.1.0" debug: "npm:~4.3.2" - engine.io-client: "npm:~6.5.1" + engine.io-client: "npm:~6.5.2" socket.io-parser: "npm:~4.2.4" - checksum: 10/cc44661677c090d936853a37cde548c0fcf2638fbcc1713f065013b19df0fb30e07eee08d91d7e07f8fe7668b914dbc5d1341a94a133c0170aecc52a27a06e67 + checksum: 10/a9e118081dc1669a63af3abd9defce94f85c8ed8d9146cd7a77665b5f1f78baf0b9f4155cf0fce7770856f97493416551abcba686f02778045f4768ceaafed5c languageName: node linkType: hard @@ -17017,7 +17051,7 @@ __metadata: "@testing-library/jest-dom": "npm:6.4.5" "@testing-library/react": "npm:14.3.0" "@testing-library/user-event": "npm:14.5.2" - "@toruslabs/customauth": "npm:^15.0.5" + "@toruslabs/customauth": "npm:^16.0.6" "@types/babel__core": "npm:^7" "@types/babel__preset-env": "npm:^7" "@types/identity-obj-proxy": "npm:^3" From e2390e61023dada6b0e202d00eccd7fb9dfeafd8 Mon Sep 17 00:00:00 2001 From: Sergey Kintsel Date: Wed, 22 May 2024 20:26:19 +0100 Subject: [PATCH 18/18] Update sign button text --- src/components/SendFlow/SignButton.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SendFlow/SignButton.tsx b/src/components/SendFlow/SignButton.tsx index 8334d97dc3..0c6d400d77 100644 --- a/src/components/SendFlow/SignButton.tsx +++ b/src/components/SendFlow/SignButton.tsx @@ -125,7 +125,7 @@ export const SignButton: React.FC<{ onClick={onSocialSign} size="lg" > - {text || "Sign with Google"} + {text || "Sign with social"} ); case "ledger":