From 3e9564d7b919b2535885da73399b22ddee33f102 Mon Sep 17 00:00:00 2001 From: Verticool Date: Mon, 3 Feb 2025 11:04:15 +0600 Subject: [PATCH] auth flow fix, validation for auth in steps /uploadContnt --- src/pages/root/steps/welcome-step/index.tsx | 23 ++-- src/pages/view-content/index.tsx | 89 ++++--------- src/shared/hooks/use-steps/index.ts | 33 +++-- src/shared/services/auth/index.ts | 139 ++++++++++++-------- src/shared/stores/root/index.ts | 2 +- 5 files changed, 147 insertions(+), 139 deletions(-) diff --git a/src/pages/root/steps/welcome-step/index.tsx b/src/pages/root/steps/welcome-step/index.tsx index 72172e3..80243d2 100644 --- a/src/pages/root/steps/welcome-step/index.tsx +++ b/src/pages/root/steps/welcome-step/index.tsx @@ -11,22 +11,27 @@ type WelcomeStepProps = { export const WelcomeStep = ({ nextStep }: WelcomeStepProps) => { const [tonConnectUI] = useTonConnectUI(); const [isLoaded, setLoaded] = useState(false); - + console.log("💩💩💩 enter WelcomeStep"); const auth = useAuth(); console.log("💩💩💩 after useAuth"); - - const handleNextClick = async () => { - if (tonConnectUI.connected) { - await auth.mutateAsync(); - nextStep(); - } else { + +const handleNextClick = async () => { + if (tonConnectUI.connected) { + await auth.mutateAsync(); + nextStep(); + } else { + try { await tonConnectUI.openModal(); await auth.mutateAsync(); + nextStep(); + } catch (error) { + console.error('Failed to connect or authenticate:', error); } - }; + } +}; // useEffect(() => { // const first = setTimeout(async () => { @@ -104,4 +109,4 @@ export const WelcomeStep = ({ nextStep }: WelcomeStepProps) => { /> ); -}; +}; \ No newline at end of file diff --git a/src/pages/view-content/index.tsx b/src/pages/view-content/index.tsx index 5a07966..877d5a6 100644 --- a/src/pages/view-content/index.tsx +++ b/src/pages/view-content/index.tsx @@ -2,6 +2,7 @@ import ReactPlayer from "react-player/lazy"; import { useTonConnectUI } from "@tonconnect/ui-react"; import { useWebApp } from "@vkruglikov/react-telegram-web-app"; +import { Button } from "~/shared/ui/button"; import { usePurchaseContent, useViewContent } from "~/shared/services/content"; import { fromNanoTON } from "~/shared/utils"; import {useCallback, useEffect, useMemo} from "react"; @@ -71,54 +72,18 @@ export const ViewContentPage = () => { }, []); - useEffect(() => { - const mainButton = WebApp.MainButton; - const secondaryButton = WebApp.SecondaryButton; - - try { - // Set main button color - mainButton.color = '#e40615'; - mainButton.textColor = '#FFFFFF'; - - // Set secondary button color - secondaryButton.color = '#363636'; - secondaryButton.textColor = '#FFFFFF'; - if (!haveLicense) { - mainButton.text = `Купить за ${fromNanoTON(content?.data?.encrypted?.license?.resale?.price)} ТОН`; - mainButton.show(); - mainButton.onClick(handleBuyContent); - } else { - mainButton.hide(); - } - - secondaryButton.text = 'Загрузить свой контент'; - secondaryButton.show(); - secondaryButton.onClick(() => { - WebApp.openTelegramLink('https://t.me/MY_UploaderRobot'); - }); - - return () => { - mainButton.hide(); - mainButton.offClick(handleBuyContent); - secondaryButton.hide(); - secondaryButton.offClick(); - }; - } catch (error) { - console.error('Error setting up Telegram WebApp buttons:', error); - } - }, [content, haveLicense, WebApp, handleBuyContent]); - + return ( -
- {content?.data?.content_type.startsWith("audio") && content?.data?.display_options?.metadata?.image && ( -
- {"content_image"} -
- )} +
+ {content?.data?.content_type.startsWith("audio") && content?.data?.display_options?.metadata?.image && ( +
+ {"content_image"} +
+ )} {content?.data?.content_type.startsWith("audio") ? ( @@ -146,21 +111,23 @@ export const ViewContentPage = () => {

- {/* {!haveLicense &&
); }; diff --git a/src/shared/hooks/use-steps/index.ts b/src/shared/hooks/use-steps/index.ts index 7f632e4..311f069 100644 --- a/src/shared/hooks/use-steps/index.ts +++ b/src/shared/hooks/use-steps/index.ts @@ -1,4 +1,8 @@ -import { ReactNode, useMemo, useState } from "react"; +import { useTonConnectUI } from "@tonconnect/ui-react"; +import { ReactNode, useEffect, useMemo, useState } from "react"; + +const CHECK_INTERVAL = 20000; + export const useSteps = ( sections: ({ @@ -9,15 +13,24 @@ export const useSteps = ( prevStep(): void; }) => ReactNode[], ) => { + + const [tonConnectUI] = useTonConnectUI(); + const [step, setStep] = useState(0); - - const nextStep = () => { - return setStep((s) => s + 1); - }; - - const prevStep = () => { - return setStep((s) => s - 1); - }; + + // If connection is lost, reset the step + useEffect(() => { + const interval = setInterval(() => { + if (!tonConnectUI.connected && step !== 0) { + setStep(0); + } + }, CHECK_INTERVAL); + + return () => clearInterval(interval); + }, []); + + const nextStep = () => setStep((s) => s + 1); + const prevStep = () => setStep((s) => s - 1); const ActiveSection = useMemo(() => { return sections({ nextStep, prevStep })[step]; @@ -28,4 +41,4 @@ export const useSteps = ( setStep, step, }; -}; +}; \ No newline at end of file diff --git a/src/shared/services/auth/index.ts b/src/shared/services/auth/index.ts index 0f6d859..fd0a308 100644 --- a/src/shared/services/auth/index.ts +++ b/src/shared/services/auth/index.ts @@ -13,80 +13,103 @@ export const useAuth = () => { const [tonConnectUI] = useTonConnectUI(); const interval = useRef | undefined>(); + const waitForWalletProof = async () => { + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => reject(new Error("Timeout waiting for proof")), 30000); + const checkProof = setInterval(() => { + const currentWallet = tonConnectUI.wallet; + if ( + currentWallet?.connectItems?.tonProof && + !("error" in currentWallet.connectItems.tonProof) + ) { + clearInterval(checkProof); + clearTimeout(timeout); + resolve(currentWallet.connectItems.tonProof.proof); + } + }, 500); + }); + }; + + const makeAuthRequest = async (params: { + twa_data: string; + ton_proof?: { + account: any; + ton_proof: any; + }; + }) => { + const res = await request.post<{ + connected_wallet: null | { + version: string; + address: string; + ton_balance: string; + }; + auth_v1_token: string; + }>("/auth.twa", params); + + if (res?.data?.auth_v1_token) { + localStorage.setItem(sessionStorageKey, res.data.auth_v1_token); + } else { + throw new Error("Failed to get auth token"); + } + return res; + }; + return useMutation(["auth"], async () => { clearInterval(interval.current); + console.log("DEBUG: Starting auth flow"); - if (!wallet) { + // Case 1: Not connected - need to connect and get proof + if (!tonConnectUI.connected) { + console.log("DEBUG: No wallet connection, starting flow"); localStorage.removeItem(sessionStorageKey); const refreshPayload = async () => { tonConnectUI.setConnectRequestParameters({ state: "loading" }); - - const value = await request - .post<{ - auth_v1_token: string; - }>("/auth.twa", { - twa_data: WebApp.initData, - }) - .catch((error: any) => { - console.error("Error in authentication request: ", error); - throw new Error("Failed to authenticate."); - }); - if (!value) { - tonConnectUI.setConnectRequestParameters(null); - } else { + const value = await request.post<{ auth_v1_token: string }>("/auth.twa", { + twa_data: WebApp.initData, + }); + + if (value?.data?.auth_v1_token) { tonConnectUI.setConnectRequestParameters({ state: "ready", - value: { - tonProof: value?.data?.auth_v1_token, - }, + value: { tonProof: value.data.auth_v1_token }, }); + } else { + tonConnectUI.setConnectRequestParameters(null); } }; - void refreshPayload(); - setInterval(refreshPayload, payloadTTLMS); + await refreshPayload(); + interval.current = setInterval(refreshPayload, payloadTTLMS); - return; + const tonProof = await waitForWalletProof(); + console.log("DEBUG: Got initial proof", tonProof); + + return makeAuthRequest({ + twa_data: WebApp.initData, + ton_proof: { + account: tonConnectUI.wallet!.account, + ton_proof: tonProof, + }, + }); } - if ( - wallet.connectItems?.tonProof && - !("error" in wallet.connectItems.tonProof) - ) { - const tonProof = wallet.connectItems.tonProof.proof; - - console.log("DEBUG TON-PROOF", tonProof); - - request - .post<{ - connected_wallet: null | { - version: string; - address: string; - ton_balance: string; - }; - auth_v1_token: string; - }>("/auth.twa", { - twa_data: WebApp.initData, - ton_proof: { - account: wallet.account, - ton_proof: tonProof, - }, - }) - .then((res) => { - if (res?.data?.auth_v1_token) { - localStorage.setItem(sessionStorageKey, res?.data?.auth_v1_token); - } else { - alert("Please try another wallet"); - } - }) - .catch((error: any) => { - console.error("Error in authentication request: ", error); - throw new Error("Failed to authenticate."); - }); - } else { - void tonConnectUI.disconnect(); - localStorage.removeItem(sessionStorageKey) + // Case 2: Connected with proof - use it + if (wallet?.connectItems?.tonProof && !("error" in wallet.connectItems.tonProof)) { + console.log("DEBUG: Using existing proof"); + return makeAuthRequest({ + twa_data: WebApp.initData, + ton_proof: { + account: wallet.account, + ton_proof: wallet.connectItems.tonProof.proof, + }, + }); } + + // Case 3: Connected without proof - already authenticated + console.log("DEBUG: Connected without proof, proceeding without it"); + return makeAuthRequest({ + twa_data: WebApp.initData, + }); }); }; \ No newline at end of file diff --git a/src/shared/stores/root/index.ts b/src/shared/stores/root/index.ts index 038e113..376d717 100644 --- a/src/shared/stores/root/index.ts +++ b/src/shared/stores/root/index.ts @@ -77,7 +77,7 @@ export const useRootStore = create((set) => ({ authors: [], setAuthors: (authors) => set({ authors }), - royalty: [{ address: "", value: 100 }], + royalty: [], setRoyalty: (royalty) => set({ royalty }), price: 0.15,