Skip to content

Commit dae2fe6

Browse files
committed
Loading, error exposing on ewk. Exposed email customization
1 parent 044e2d2 commit dae2fe6

File tree

6 files changed

+151
-35
lines changed

6 files changed

+151
-35
lines changed

packages/sdk-react/src/components/auth/Apple.tsx

+26-6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
popupHeight,
1010
popupWidth,
1111
} from "./constants";
12+
import { CircularProgress } from "@mui/material";
1213

1314
function isMobileBrowser() {
1415
return /Android|iPhone|iPad|iPod|Opera Mini|IEMobile|WPDesktop/i.test(
@@ -35,6 +36,8 @@ const AppleAuthButton: React.FC<AppleAuthButtonProps> = ({
3536
onSuccess,
3637
layout,
3738
}) => {
39+
const [loading, setLoading] = useState(false);
40+
3841
const [appleSDKLoaded, setAppleSDKLoaded] = useState(false);
3942
const redirectURI = process.env.NEXT_PUBLIC_OAUTH_REDIRECT_URI!;
4043

@@ -71,6 +74,7 @@ const AppleAuthButton: React.FC<AppleAuthButtonProps> = ({
7174
}, [onSuccess]);
7275

7376
const handleLogin = () => {
77+
setLoading(true);
7478
const nonce = bytesToHex(sha256(iframePublicKey));
7579
const appleAuthUrl = new URL(APPLE_AUTH_URL);
7680
appleAuthUrl.searchParams.set("client_id", clientId);
@@ -104,6 +108,7 @@ const AppleAuthButton: React.FC<AppleAuthButtonProps> = ({
104108
const interval = setInterval(() => {
105109
try {
106110
if (authWindow.closed) {
111+
setLoading(false);
107112
clearInterval(interval);
108113
return;
109114
}
@@ -113,6 +118,7 @@ const AppleAuthButton: React.FC<AppleAuthButtonProps> = ({
113118
const idToken = hashParams.get("id_token");
114119
if (idToken) {
115120
authWindow.close();
121+
setLoading(false);
116122
clearInterval(interval);
117123
onSuccess({ idToken });
118124
}
@@ -123,6 +129,7 @@ const AppleAuthButton: React.FC<AppleAuthButtonProps> = ({
123129
// Due to browser security policies (Same-Origin Policy), accessing properties like location.href on a window that is on a different domain will throw an exception.
124130
// Once the popup redirects to the same origin as the parent window, these errors will no longer occur, and the script can safely access the popup's location to extract parameters.
125131
if (authWindow?.closed) {
132+
setLoading(false);
126133
clearInterval(interval);
127134
}
128135
}
@@ -137,13 +144,26 @@ const AppleAuthButton: React.FC<AppleAuthButtonProps> = ({
137144
return (
138145
<div
139146
className={layout === "inline" ? styles.iconButton : styles.socialButton}
140-
onClick={handleLogin}
147+
onClick={loading ? undefined : handleLogin}
141148
>
142-
<img
143-
src={appleIcon}
144-
className={layout === "inline" ? styles.iconLarge : styles.iconSmall}
145-
/>
146-
{layout === "stacked" && <span>Continue with Apple</span>}
149+
{loading ? (
150+
<CircularProgress
151+
size={24}
152+
thickness={4}
153+
className={styles.buttonProgress || ""}
154+
/>
155+
) : (
156+
<>
157+
<img
158+
src={appleIcon}
159+
className={
160+
layout === "inline" ? styles.iconLarge : styles.iconSmall
161+
}
162+
alt="Apple"
163+
/>
164+
{layout === "stacked" && <span>Continue with Apple</span>}
165+
</>
166+
)}
147167
</div>
148168
);
149169
};

packages/sdk-react/src/components/auth/Auth.tsx

+54-15
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import ChevronLeftIcon from "@mui/icons-material/ChevronLeft";
1515
import OtpVerification from "./OtpVerification";
1616
import { useTurnkey } from "../../hooks/use-turnkey";
1717
import { FilterType, OtpType, authErrors } from "./constants";
18-
import type { WalletAccount } from "@turnkey/sdk-browser";
18+
import type { TurnkeyApiTypes, WalletAccount } from "@turnkey/sdk-browser";
1919
import { server } from "@turnkey/sdk-server";
2020
import parsePhoneNumberFromString from "libphonenumber-js";
2121
import { useRouter } from "next/navigation";
@@ -76,6 +76,8 @@ interface AuthProps {
7676
facebookClientId?: string; // will default to NEXT_PUBLIC_FACEBOOK_CLIENT_ID
7777
};
7878
configOrder: string[];
79+
emailCustomization?: TurnkeyApiTypes["v1EmailCustomizationParams"];
80+
sendFromEmailAddress?: string;
7981
customSmsMessage?: string;
8082
customAccounts?: WalletAccount[];
8183
}
@@ -85,6 +87,8 @@ const Auth: React.FC<AuthProps> = ({
8587
onError,
8688
authConfig,
8789
configOrder,
90+
emailCustomization,
91+
sendFromEmailAddress,
8892
customSmsMessage,
8993
customAccounts,
9094
}) => {
@@ -94,14 +98,14 @@ const Auth: React.FC<AuthProps> = ({
9498
const [phone, setPhone] = useState<string>("");
9599
const [otpId, setOtpId] = useState<string | null>(null);
96100
const [step, setStep] = useState<string>("auth");
101+
const [loading, setLoading] = useState<string | undefined>();
97102
const [oauthLoading, setOauthLoading] = useState<string>("");
98103
const [suborgId, setSuborgId] = useState<string>("");
99104
const [passkeySignupScreen, setPasskeySignupScreen] = useState(false);
100105
const [passkeyCreationScreen, setPasskeyCreationScreen] = useState(false);
101106
const [passkeySignupError, setPasskeySignupError] = useState("");
102-
const [loading, setLoading] = useState(true);
107+
const [componentReady, setComponentReady] = useState(false);
103108
const [passkeyCreated, setPasskeyCreated] = useState(false);
104-
const [walletLoading, setWalletLoading] = useState(false);
105109

106110
const handleResendCode = async () => {
107111
if (step === OtpType.Email) {
@@ -113,11 +117,11 @@ const Auth: React.FC<AuthProps> = ({
113117

114118
useEffect(() => {
115119
if (authIframeClient) {
116-
setLoading(false);
120+
setComponentReady(true);
117121
}
118122
}, [authIframeClient]);
119123

120-
if (loading) {
124+
if (!componentReady) {
121125
return (
122126
<div className={styles.defaultLoader}>
123127
<CircularProgress
@@ -198,13 +202,15 @@ const Auth: React.FC<AuthProps> = ({
198202
authIframeClient?.iframePublicKey!,
199203
);
200204
router.push("/dashboard");
201-
} catch {
205+
} catch (error) {
202206
setPasskeySignupError(authErrors.passkey.timeoutOrNotAllowed);
207+
console.error("Error during passkey signup: ", error);
203208
}
204209
};
205210

206211
const handleLoginWithPasskey = async () => {
207212
try {
213+
setLoading("passkey");
208214
await passkeyClient?.loginWithPasskey(
209215
SessionType.READ_WRITE,
210216
authIframeClient!,
@@ -213,6 +219,9 @@ const Auth: React.FC<AuthProps> = ({
213219
router.push("/dashboard");
214220
} catch (error) {
215221
onError(authErrors.passkey.loginFailed);
222+
console.error("Error during passkey login: ", error);
223+
} finally {
224+
setLoading(undefined);
216225
}
217226
};
218227

@@ -221,6 +230,7 @@ const Auth: React.FC<AuthProps> = ({
221230
value: string,
222231
otpType: string,
223232
) => {
233+
setLoading(otpType);
224234
const createSuborgData: Record<string, any> = {};
225235
if (type === FilterType.Email) createSuborgData.email = value;
226236
else if (type === FilterType.PhoneNumber)
@@ -244,6 +254,8 @@ const Auth: React.FC<AuthProps> = ({
244254
suborgID: suborgId!,
245255
otpType,
246256
contact: value,
257+
...(emailCustomization && { emailCustomization }),
258+
...(sendFromEmailAddress && { sendFromEmailAddress }),
247259
...(customSmsMessage && { customSmsMessage }),
248260
userIdentifier: authIframeClient?.iframePublicKey!,
249261
});
@@ -254,6 +266,7 @@ const Auth: React.FC<AuthProps> = ({
254266
} else {
255267
onError(authErrors.otp.sendFailed);
256268
}
269+
setLoading(undefined);
257270
};
258271

259272
const handleOAuthLogin = async (credential: string, providerName: string) => {
@@ -290,7 +303,7 @@ const Auth: React.FC<AuthProps> = ({
290303
};
291304

292305
const handleLoginWithWallet = async () => {
293-
setWalletLoading(true);
306+
setLoading("wallet");
294307
try {
295308
if (!walletClient) {
296309
throw new Error("Wallet client not initialized");
@@ -341,8 +354,9 @@ const Auth: React.FC<AuthProps> = ({
341354
}
342355
} catch (error: any) {
343356
onError(error.message || authErrors.wallet.loginFailed);
357+
console.error("Error during wallet login: ", error);
344358
} finally {
345-
setWalletLoading(false);
359+
setLoading(undefined);
346360
}
347361
};
348362

@@ -470,9 +484,17 @@ const Auth: React.FC<AuthProps> = ({
470484
<button
471485
className={styles.authButton}
472486
type="submit"
473-
disabled={!isValidEmail(email)}
487+
disabled={!isValidEmail(email) || loading === OtpType.Email}
474488
>
475-
Continue
489+
{loading === OtpType.Email ? (
490+
<CircularProgress
491+
size={24}
492+
thickness={4}
493+
className={styles.buttonProgress || ""}
494+
/>
495+
) : (
496+
"Continue"
497+
)}
476498
</button>
477499
</form>
478500
) : null;
@@ -491,9 +513,17 @@ const Auth: React.FC<AuthProps> = ({
491513
<button
492514
className={styles.authButton}
493515
type="submit"
494-
disabled={!isValidPhone(phone)}
516+
disabled={!isValidPhone(phone) || loading === OtpType.Sms}
495517
>
496-
Continue
518+
{loading === OtpType.Sms ? (
519+
<CircularProgress
520+
size={24}
521+
thickness={4}
522+
className={styles.buttonProgress || ""}
523+
/>
524+
) : (
525+
"Continue"
526+
)}
497527
</button>
498528
</form>
499529
) : null;
@@ -505,8 +535,17 @@ const Auth: React.FC<AuthProps> = ({
505535
className={styles.authButton}
506536
type="button"
507537
onClick={handleLoginWithPasskey}
538+
disabled={loading === "passkey"}
508539
>
509-
Log in with passkey
540+
{loading === "passkey" ? (
541+
<CircularProgress
542+
size={24}
543+
thickness={4}
544+
className={styles.buttonProgress || ""}
545+
/>
546+
) : (
547+
"Log in with passkey"
548+
)}
510549
</button>
511550
<div
512551
className={styles.noPasskeyLink}
@@ -524,9 +563,9 @@ const Auth: React.FC<AuthProps> = ({
524563
className={styles.authButton}
525564
type="button"
526565
onClick={handleLoginWithWallet}
527-
disabled={walletLoading}
566+
disabled={loading === "wallet"}
528567
>
529-
{walletLoading ? (
568+
{loading === "wallet" ? (
530569
<CircularProgress
531570
size={24}
532571
thickness={4}

packages/sdk-react/src/components/auth/Facebook.tsx

+24-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { sha256 } from "@noble/hashes/sha256";
77
import { bytesToHex } from "@noble/hashes/utils";
88
import facebookIcon from "assets/facebook.svg";
99
import { FACEBOOK_AUTH_URL, popupHeight, popupWidth } from "./constants";
10+
import { CircularProgress } from "@mui/material";
1011

1112
interface FacebookAuthButtonProps {
1213
iframePublicKey: string;
@@ -21,10 +22,13 @@ const FacebookAuthButton: React.FC<FacebookAuthButtonProps> = ({
2122
clientId,
2223
layout,
2324
}) => {
25+
const [loading, setLoading] = useState(false);
26+
2427
const [tokenExchanged, setTokenExchanged] = useState<boolean>(false);
2528
const redirectURI = process.env.NEXT_PUBLIC_OAUTH_REDIRECT_URI!;
2629

2730
const initiateFacebookLogin = async () => {
31+
setLoading(true);
2832
const { verifier, codeChallenge } = await generateChallengePair();
2933
sessionStorage.setItem("facebook_verifier", verifier);
3034

@@ -57,6 +61,7 @@ const FacebookAuthButton: React.FC<FacebookAuthButtonProps> = ({
5761
const interval = setInterval(async () => {
5862
try {
5963
if (popup.closed) {
64+
setLoading(false);
6065
clearInterval(interval);
6166
return;
6267
}
@@ -65,6 +70,7 @@ const FacebookAuthButton: React.FC<FacebookAuthButtonProps> = ({
6570
const authCode = popupUrl.searchParams.get("code");
6671

6772
if (authCode) {
73+
setLoading(false);
6874
popup.close();
6975
clearInterval(interval);
7076
handleTokenExchange(authCode);
@@ -107,13 +113,25 @@ const FacebookAuthButton: React.FC<FacebookAuthButtonProps> = ({
107113
return (
108114
<div
109115
className={layout === "inline" ? styles.iconButton : styles.socialButton}
110-
onClick={initiateFacebookLogin}
116+
onClick={loading ? undefined : initiateFacebookLogin}
111117
>
112-
<img
113-
src={facebookIcon}
114-
className={layout === "inline" ? styles.iconLarge : styles.iconSmall}
115-
/>
116-
{layout === "stacked" && <span>Continue with Facebook</span>}
118+
{loading ? (
119+
<CircularProgress
120+
size={24}
121+
thickness={4}
122+
className={styles.buttonProgress || ""}
123+
/>
124+
) : (
125+
<>
126+
<img
127+
src={facebookIcon}
128+
className={
129+
layout === "inline" ? styles.iconLarge : styles.iconSmall
130+
}
131+
/>
132+
{layout === "stacked" && <span>Continue with Facebook</span>}
133+
</>
134+
)}
117135
</div>
118136
);
119137
};

0 commit comments

Comments
 (0)