@coinbase/create-cdp-app 0.0.54 → 0.0.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,56 @@
1
+ import React, { useState } from "react";
2
+ import { View, TouchableOpacity, Text, Alert } from "react-native";
3
+ import { useSignInWithOAuth } from "@coinbase/cdp-hooks";
4
+ import { useSignInFormStyles } from "../hooks/useSignInFormStyles";
5
+
6
+ export const OAuthForm: React.FC = () => {
7
+ const { signInWithOAuth } = useSignInWithOAuth();
8
+ const styles = useSignInFormStyles();
9
+ const [isLoading, setIsLoading] = useState(false);
10
+ const [loadingProvider, setLoadingProvider] = useState<string | null>(null);
11
+
12
+ const handleOAuthSignIn = async (provider: "google" | "apple") => {
13
+ setIsLoading(true);
14
+ setLoadingProvider(provider);
15
+ try {
16
+ await signInWithOAuth(provider);
17
+ } catch (error) {
18
+ Alert.alert("Error", error instanceof Error ? error.message : "Failed to sign in.");
19
+ } finally {
20
+ setIsLoading(false);
21
+ setLoadingProvider(null);
22
+ }
23
+ };
24
+
25
+ return (
26
+ <>
27
+ <View style={styles.header}>
28
+ <View style={styles.logoCircle}>
29
+ <Text style={styles.logoText}>C</Text>
30
+ </View>
31
+ <Text style={styles.title}>Sign in</Text>
32
+ </View>
33
+ <View style={styles.form}>
34
+ <TouchableOpacity
35
+ style={[styles.continueButton, isLoading && styles.buttonDisabled]}
36
+ onPress={() => handleOAuthSignIn("google")}
37
+ disabled={isLoading}
38
+ >
39
+ <Text style={styles.continueButtonText}>
40
+ {loadingProvider === "google" ? "Signing in..." : "Continue with Google"}
41
+ </Text>
42
+ </TouchableOpacity>
43
+
44
+ <TouchableOpacity
45
+ style={[styles.continueButton, isLoading && styles.buttonDisabled]}
46
+ onPress={() => handleOAuthSignIn("apple")}
47
+ disabled={isLoading}
48
+ >
49
+ <Text style={styles.continueButtonText}>
50
+ {loadingProvider === "apple" ? "Signing in..." : "Continue with Apple"}
51
+ </Text>
52
+ </TouchableOpacity>
53
+ </View>
54
+ </>
55
+ );
56
+ };
@@ -0,0 +1,155 @@
1
+ import React, { useState, useRef, useEffect } from "react";
2
+ import {
3
+ View,
4
+ TouchableOpacity,
5
+ Text,
6
+ TextInput,
7
+ KeyboardAvoidingView,
8
+ Platform,
9
+ ScrollView,
10
+ Keyboard,
11
+ Alert,
12
+ } from "react-native";
13
+ import { useVerifyEmailOTP, useVerifySmsOTP } from "@coinbase/cdp-hooks";
14
+ import { useTheme } from "../theme/ThemeContext";
15
+ import { useSignInFormStyles } from "../hooks/useSignInFormStyles";
16
+ import { AuthMethod } from "../types";
17
+
18
+ export interface OtpFormProps {
19
+ authMethod: AuthMethod;
20
+ flowId: string;
21
+ onSuccess: () => void;
22
+ onBack: () => void;
23
+ }
24
+
25
+ export const OtpForm: React.FC<OtpFormProps> = ({ authMethod, flowId, onSuccess, onBack }) => {
26
+ const { colors } = useTheme();
27
+ const { verifyEmailOTP } = useVerifyEmailOTP();
28
+ const { verifySmsOTP } = useVerifySmsOTP();
29
+ const styles = useSignInFormStyles();
30
+ const [otp, setOtp] = useState("");
31
+ const [isLoading, setIsLoading] = useState(false);
32
+ const scrollViewRef = useRef<ScrollView>(null);
33
+
34
+ // Auto-scroll to ensure OTP field and button are visible
35
+ useEffect(() => {
36
+ if (scrollViewRef.current) {
37
+ setTimeout(() => {
38
+ scrollViewRef.current?.scrollTo({
39
+ y: 200,
40
+ animated: true,
41
+ });
42
+ }, 300);
43
+ }
44
+ }, []);
45
+
46
+ // Handle keyboard events for better positioning
47
+ useEffect(() => {
48
+ const keyboardDidShowListener = Keyboard.addListener("keyboardDidShow", () => {
49
+ if (scrollViewRef.current) {
50
+ setTimeout(() => {
51
+ scrollViewRef.current?.scrollTo({
52
+ y: 250,
53
+ animated: true,
54
+ });
55
+ }, 100);
56
+ }
57
+ });
58
+
59
+ const keyboardDidHideListener = Keyboard.addListener("keyboardDidHide", () => {
60
+ if (scrollViewRef.current) {
61
+ setTimeout(() => {
62
+ scrollViewRef.current?.scrollTo({
63
+ y: 150,
64
+ animated: true,
65
+ });
66
+ }, 100);
67
+ }
68
+ });
69
+
70
+ return () => {
71
+ keyboardDidShowListener.remove();
72
+ keyboardDidHideListener.remove();
73
+ };
74
+ }, []);
75
+
76
+ const handleVerifyOTP = async () => {
77
+ if (!otp || !flowId) {
78
+ Alert.alert("Error", "Please enter the OTP.");
79
+ return;
80
+ }
81
+
82
+ setIsLoading(true);
83
+ try {
84
+ if (authMethod === "email") {
85
+ await verifyEmailOTP({ flowId, otp });
86
+ } else {
87
+ await verifySmsOTP({ flowId, otp });
88
+ }
89
+ setOtp("");
90
+ onSuccess();
91
+ } catch (error) {
92
+ Alert.alert("Error", error instanceof Error ? error.message : "Failed to verify OTP.");
93
+ } finally {
94
+ setIsLoading(false);
95
+ }
96
+ };
97
+
98
+ return (
99
+ <KeyboardAvoidingView
100
+ behavior={Platform.OS === "ios" ? "padding" : "position"}
101
+ style={styles.keyboardContainer}
102
+ keyboardVerticalOffset={Platform.OS === "ios" ? 60 : -150}
103
+ >
104
+ <ScrollView
105
+ ref={scrollViewRef}
106
+ contentContainerStyle={styles.scrollContainerWithOtp}
107
+ keyboardShouldPersistTaps="handled"
108
+ showsVerticalScrollIndicator={false}
109
+ >
110
+ <View style={styles.container}>
111
+ <View style={styles.card}>
112
+ <View style={styles.header}>
113
+ <TouchableOpacity style={styles.backButton} onPress={onBack} disabled={isLoading}>
114
+ <Text style={styles.backButtonText}>←</Text>
115
+ </TouchableOpacity>
116
+ <View style={styles.logoCircle}>
117
+ <Text style={styles.logoText}>C</Text>
118
+ </View>
119
+ <Text style={styles.title}>
120
+ Check your {authMethod === "email" ? "email" : "phone"}
121
+ </Text>
122
+ </View>
123
+ <View style={styles.form}>
124
+ <Text style={styles.inputLabel}>Verification code</Text>
125
+ <TextInput
126
+ style={styles.input}
127
+ value={otp}
128
+ onChangeText={setOtp}
129
+ placeholder="Enter 6-digit code"
130
+ keyboardType="number-pad"
131
+ maxLength={6}
132
+ editable={!isLoading}
133
+ placeholderTextColor={colors.textSecondary}
134
+ returnKeyType="done"
135
+ onSubmitEditing={handleVerifyOTP}
136
+ blurOnSubmit={true}
137
+ autoFocus={true}
138
+ />
139
+
140
+ <TouchableOpacity
141
+ style={[styles.continueButton, isLoading && styles.buttonDisabled]}
142
+ onPress={handleVerifyOTP}
143
+ disabled={isLoading}
144
+ >
145
+ <Text style={styles.continueButtonText}>
146
+ {isLoading ? "Verifying..." : "Verify Code"}
147
+ </Text>
148
+ </TouchableOpacity>
149
+ </View>
150
+ </View>
151
+ </View>
152
+ </ScrollView>
153
+ </KeyboardAvoidingView>
154
+ );
155
+ };