Skip to content

Commit

Permalink
added quicker loading of user details
Browse files Browse the repository at this point in the history
  • Loading branch information
ihsraham committed Nov 13, 2024
1 parent 6cb0d5c commit 961dbd2
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 60 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 16 additions & 1 deletion single-factor-auth-web/sfa-web-ton-telegram-example/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -299,4 +299,19 @@ body {
padding: 12px 0;
margin-top: 12px;
}
}
}

.breathing-outline {
border: 2px dashed rgba(0, 0, 0, 0.2);
border-radius: 8px;
animation: breathing 1.5s ease-in-out infinite;
}

@keyframes breathing {
0%, 100% {
border-color: rgba(0, 0, 0, 0.2);
}
50% {
border-color: rgba(0, 0, 0, 0.5);
}
}
121 changes: 65 additions & 56 deletions single-factor-auth-web/sfa-web-ton-telegram-example/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { useEffect, useState } from "react";
import { Web3Auth, decodeToken } from "@web3auth/single-factor-auth";
import { Web3Auth } from "@web3auth/single-factor-auth";
import { CHAIN_NAMESPACES, WEB3AUTH_NETWORK } from "@web3auth/base";
import { CommonPrivateKeyProvider } from "@web3auth/base-provider";
import { getHttpEndpoint } from "@orbs-network/ton-access";
import TonRPC from "./tonRpc";
import { useLaunchParams } from "@telegram-apps/sdk-react";
import { useLaunchParams, User } from "@telegram-apps/sdk-react";
import { useTelegramMock } from "./hooks/useMockTelegramInitData";
import { Sun, Moon, Copy, Check } from "lucide-react";
import Loading from "./components/Loading";
import TelegramLogo from "./assets/TelegramLogo.svg";
import web3AuthLogoLight from "./assets/web3AuthLogoLight.svg";
import web3AuthLogoDark from "./assets/web3AuthLogoDark.svg";
Expand All @@ -16,14 +15,18 @@ import "./App.css";
const verifier = "w3a-telegram-demo";
const clientId = "BPi5PB_UiIZ-cPz1GtV5i1I2iOSOHuimiXBI0e-Oe_u6X3oVAbCiAZOTEBtTXw4tsluTITPqA8zMsfxIKMjiqNQ";

// Gravatar fallback URL with a default image
const getGravatarUrl = (identifier: string) => {
const hash = identifier.toLowerCase(); // Use identifier directly for simplicity
return `https://www.gravatar.com/avatar/${hash}?d=mp`;
};

function App() {
const [isLoggingIn, setIsLoggingIn] = useState(false);
const [web3authSfa, setWeb3authSfa] = useState<Web3Auth | null>(null);
const [web3AuthInitialized, setWeb3AuthInitialized] = useState(false);
const [userData, setUserData] = useState<any | null>(null);
const [userData, setUserData] = useState<User | null>(null);
const [tonAccountAddress, setTonAccountAddress] = useState<string | null>(null);
const [signedMessage, setSignedMessage] = useState<string | null>(null);
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [isDarkMode, setIsDarkMode] = useState(false);
const [copiedStates, setCopiedStates] = useState<{ [key: string]: boolean }>({
account: false,
Expand Down Expand Up @@ -85,29 +88,31 @@ function App() {
initializeWeb3Auth();
}, []);

// Set user data directly from initData if available
useEffect(() => {
if (initData && initData.user) {
setUserData(initData.user);
}
}, [initData]);

useEffect(() => {
const connectWeb3Auth = async () => {
if (web3authSfa && web3AuthInitialized && initDataRaw) {
setIsLoggingIn(true);
try {
if (web3authSfa.status === "connected") {
await web3authSfa.logout();
}

const idToken = await getIdTokenFromServer(initDataRaw, initData?.user.photoUrl);
const idToken = await getIdTokenFromServer(initDataRaw, initData?.user?.photoUrl);
if (!idToken) return;

const { payload } = decodeToken(idToken);

await web3authSfa.connect({
verifier,
verifierId: (payload as any).sub,
verifierId: initData?.user?.id.toString(),
idToken: idToken,
});

setUserData(payload);
setIsLoggedIn(true);

// Set TON account address and signed message after connecting
const tonRpc = new TonRPC(web3authSfa.provider);
const tonAddress = await tonRpc.getAccounts();
setTonAccountAddress(tonAddress);
Expand All @@ -117,16 +122,14 @@ function App() {
setSignedMessage(signedMsg);
} catch (error) {
console.error("Error during Web3Auth connection:", error);
} finally {
setIsLoggingIn(false);
}
}
};

if (web3AuthInitialized && initDataRaw) {
connectWeb3Auth();
}
}, [initDataRaw, web3authSfa, web3AuthInitialized, initData?.user.photoUrl]);
}, [initDataRaw, web3authSfa, web3AuthInitialized, initData?.user?.photoUrl]);

const getIdTokenFromServer = async (initDataRaw: string, photoUrl: string | undefined) => {
const isMocked = !!sessionStorage.getItem("____mocked");
Expand All @@ -149,7 +152,6 @@ function App() {
[type]: true,
}));

// Reset the copied state after 2 seconds
setTimeout(() => {
setCopiedStates((prev) => ({
...prev,
Expand All @@ -173,56 +175,63 @@ function App() {
<div className="title">
<h4>Web3Auth Telegram MiniApp</h4>
</div>

<div className="description">
<p>Seamless wallet access on any chain with Telegram. Just one click, and you're in!</p>
</div>
</div>
{isLoggingIn ? (
<Loading />
) : (
<div className="grid">
{isLoggedIn && (

<div className="grid">
<div className={`user-info-box ${userData ? "" : "breathing-outline"}`}>
{userData ? (
<>
<div className="user-info-box">
<img src={userData?.avatar_url} alt="User avatar" className="user-avatar" />
<div className="user-info">
<div className="id-with-logo">
<p>
<strong>ID:</strong> {userData?.telegram_id}
</p>
<img src={TelegramLogo} alt="Telegram Logo" className="telegram-logo" />
</div>
<p>
<strong>Username:</strong> {userData?.username}
</p>
<img src={userData.photoUrl || getGravatarUrl(userData.username || userData.id.toString())} alt="User avatar" className="user-avatar" />
<div className="user-info">
<div className="id-with-logo">
<p>
<strong>Name:</strong> {userData?.name}
<strong>ID:</strong> {userData?.id}
</p>
<img src={TelegramLogo} alt="Telegram Logo" className="telegram-logo" />
</div>
</div>
<div className="info-box" onClick={() => copyToClipboard(tonAccountAddress || "", "account")}>
<div className="info-box-content">
<p>
<strong>TON Account:</strong>
<span className="ellipsed-text">{tonAccountAddress}</span>
</p>
{copiedStates.account ? <Check className="copy-icon success" size={18} /> : <Copy className="copy-icon" size={18} />}
</div>
</div>
<div className="info-box" onClick={() => copyToClipboard(signedMessage || "", "message")}>
<div className="info-box-content">
<p>
<strong>Signed Message:</strong>
<span className="ellipsed-text">{signedMessage}</span>
</p>
{copiedStates.message ? <Check className="copy-icon success" size={18} /> : <Copy className="copy-icon" size={18} />}
</div>
<p>
<strong>Username:</strong> {userData?.username}
</p>
<p>
<strong>Name:</strong> {`${userData?.firstName} ${userData?.lastName || ""}`}
</p>
</div>
</>
) : (
<p>Loading user info...</p>
)}
</div>
)}

<div
className={`info-box ${tonAccountAddress ? "" : "breathing-outline"}`}
onClick={() => tonAccountAddress && copyToClipboard(tonAccountAddress, "account")}
>
<div className="info-box-content">
<p>
<strong>TON Account:</strong>
<span className="ellipsed-text">{tonAccountAddress || "Loading..."}</span>
</p>
{tonAccountAddress &&
(copiedStates.account ? <Check className="copy-icon success" size={18} /> : <Copy className="copy-icon" size={18} />)}
</div>
</div>

<div
className={`info-box ${signedMessage ? "" : "breathing-outline"}`}
onClick={() => signedMessage && copyToClipboard(signedMessage, "message")}
>
<div className="info-box-content">
<p>
<strong>Signed Message:</strong>
<span className="ellipsed-text">{signedMessage || "Loading..."}</span>
</p>
{signedMessage && (copiedStates.message ? <Check className="copy-icon success" size={18} /> : <Copy className="copy-icon" size={18} />)}
</div>
</div>
</div>

<footer className="footer">
<a
Expand Down

0 comments on commit 961dbd2

Please sign in to comment.