@hawcx/react-native-sdk 1.0.8 → 1.1.0
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.
- package/CHANGELOG.md +3 -0
- package/HawcxReactNative.podspec +2 -2
- package/README.md +327 -109
- package/android/build.gradle +2 -2
- package/android/src/main/java/com/hawcx/reactnative/HawcxEventDispatcher.kt +4 -0
- package/android/src/main/java/com/hawcx/reactnative/HawcxReactNativeModule.kt +324 -1
- package/android/src/main/java/com/hawcx/reactnative/v6/HawcxV6Bridge.kt +402 -0
- package/ios/Frameworks/HawcxFramework.xcframework/Info.plist +5 -5
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/HawcxFramework +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Info.plist +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.abi.json +22145 -2
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.private.swiftinterface +628 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.swiftinterface +628 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/HawcxFramework +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Info.plist +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.abi.json +22145 -2
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +628 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.swiftinterface +628 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.abi.json +22145 -2
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +628 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +628 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/_CodeSignature/CodeResources +21 -21
- package/ios/HawcxReactNative.m +56 -0
- package/ios/HawcxReactNative.swift +380 -1
- package/ios/HawcxV6BridgeSupport.swift +468 -0
- package/lib/commonjs/index.js +326 -3
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/v6Normalization.js +325 -0
- package/lib/commonjs/v6Normalization.js.map +1 -0
- package/lib/commonjs/v6State.js +186 -0
- package/lib/commonjs/v6State.js.map +1 -0
- package/lib/commonjs/v6Types.js +2 -0
- package/lib/commonjs/v6Types.js.map +1 -0
- package/lib/commonjs/v6WebLogin.js +101 -0
- package/lib/commonjs/v6WebLogin.js.map +1 -0
- package/lib/module/index.js +287 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/v6Normalization.js +318 -0
- package/lib/module/v6Normalization.js.map +1 -0
- package/lib/module/v6State.js +173 -0
- package/lib/module/v6State.js.map +1 -0
- package/lib/module/v6Types.js +2 -0
- package/lib/module/v6Types.js.map +1 -0
- package/lib/module/v6WebLogin.js +92 -0
- package/lib/module/v6WebLogin.js.map +1 -0
- package/lib/typescript/index.d.ts +83 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/v6Normalization.d.ts +3 -0
- package/lib/typescript/v6Normalization.d.ts.map +1 -0
- package/lib/typescript/v6State.d.ts +13 -0
- package/lib/typescript/v6State.d.ts.map +1 -0
- package/lib/typescript/v6Types.d.ts +157 -0
- package/lib/typescript/v6Types.d.ts.map +1 -0
- package/lib/typescript/v6WebLogin.d.ts +32 -0
- package/lib/typescript/v6WebLogin.d.ts.map +1 -0
- package/package.json +21 -9
- package/src/index.ts +477 -0
- package/src/v6Normalization.ts +356 -0
- package/src/v6State.ts +238 -0
- package/src/v6Types.ts +194 -0
- package/src/v6WebLogin.ts +154 -0
- package/android/.settings/org.eclipse.buildship.core.prefs +0 -2
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +0 -6
- package/android/gradlew +0 -185
- package/android/gradlew.bat +0 -89
- package/android/libs/hawcx-5.1.4.aar +0 -0
- package/docs/RELEASE.md +0 -129
- package/example/README.md +0 -59
- package/example/android/app/build.gradle +0 -126
- package/example/android/app/debug.keystore +0 -0
- package/example/android/app/proguard-rules.pro +0 -10
- package/example/android/app/src/debug/AndroidManifest.xml +0 -9
- package/example/android/app/src/main/AndroidManifest.xml +0 -27
- package/example/android/app/src/main/java/com/hawcx/example/MainActivity.kt +0 -22
- package/example/android/app/src/main/java/com/hawcx/example/MainApplication.kt +0 -45
- package/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +0 -36
- package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/values/strings.xml +0 -3
- package/example/android/app/src/main/res/values/styles.xml +0 -9
- package/example/android/build.gradle +0 -35
- package/example/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/example/android/gradle/wrapper/gradle-wrapper.properties +0 -7
- package/example/android/gradle.properties +0 -41
- package/example/android/gradlew +0 -249
- package/example/android/gradlew.bat +0 -92
- package/example/android/local.properties +0 -2
- package/example/android/settings.gradle +0 -38
- package/example/app.json +0 -4
- package/example/babel.config.js +0 -3
- package/example/e2e/README.md +0 -17
- package/example/e2e/hawcx-login.yaml +0 -14
- package/example/index.js +0 -5
- package/example/ios/.xcode.env +0 -11
- package/example/ios/HawcxExampleApp/AppDelegate.h +0 -6
- package/example/ios/HawcxExampleApp/AppDelegate.mm +0 -31
- package/example/ios/HawcxExampleApp/Images.xcassets/AppIcon.appiconset/Contents.json +0 -53
- package/example/ios/HawcxExampleApp/Images.xcassets/Contents.json +0 -6
- package/example/ios/HawcxExampleApp/Info.plist +0 -55
- package/example/ios/HawcxExampleApp/LaunchScreen.storyboard +0 -47
- package/example/ios/HawcxExampleApp/PrivacyInfo.xcprivacy +0 -37
- package/example/ios/HawcxExampleApp/main.m +0 -10
- package/example/ios/HawcxExampleApp.xcodeproj/project.pbxproj +0 -704
- package/example/ios/HawcxExampleApp.xcodeproj/project.xcworkspace/xcuserdata/agambhullar.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/example/ios/HawcxExampleApp.xcodeproj/xcshareddata/xcschemes/HawcxExampleApp.xcscheme +0 -90
- package/example/ios/HawcxExampleApp.xcodeproj/xcuserdata/agambhullar.xcuserdatad/xcschemes/xcschememanagement.plist +0 -16
- package/example/ios/HawcxExampleApp.xcworkspace/contents.xcworkspacedata +0 -10
- package/example/ios/HawcxExampleAppTests/HawcxExampleAppTests.m +0 -66
- package/example/ios/HawcxExampleAppTests/Info.plist +0 -24
- package/example/ios/Podfile +0 -79
- package/example/ios/Podfile.lock +0 -1290
- package/example/metro.config.js +0 -16
- package/example/package-lock.json +0 -13220
- package/example/package.json +0 -30
- package/example/src/App.tsx +0 -755
- package/example/src/hawcx.config.ts +0 -25
- package/example/tsconfig.json +0 -8
- package/ios/Frameworks/.keep +0 -0
- package/lib/typescript/__tests__/index.test.d.ts +0 -2
- package/lib/typescript/__tests__/index.test.d.ts.map +0 -1
- package/react_mobile_sdk_plan.md +0 -242
- package/src/__tests__/index.test.ts +0 -206
package/example/src/App.tsx
DELETED
|
@@ -1,755 +0,0 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
Platform,
|
|
4
|
-
SafeAreaView,
|
|
5
|
-
ScrollView as RNScrollView,
|
|
6
|
-
StyleSheet,
|
|
7
|
-
Switch,
|
|
8
|
-
Text,
|
|
9
|
-
TextInput,
|
|
10
|
-
TouchableOpacity,
|
|
11
|
-
View,
|
|
12
|
-
} from 'react-native';
|
|
13
|
-
import {
|
|
14
|
-
initialize,
|
|
15
|
-
useHawcxAuth,
|
|
16
|
-
useHawcxWebLogin,
|
|
17
|
-
addAuthListener,
|
|
18
|
-
addSessionListener,
|
|
19
|
-
addPushListener,
|
|
20
|
-
setPushDeviceToken,
|
|
21
|
-
notifyUserAuthenticated,
|
|
22
|
-
storeBackendOAuthTokens,
|
|
23
|
-
handlePushNotification as forwardPushPayload,
|
|
24
|
-
approvePushRequest,
|
|
25
|
-
declinePushRequest,
|
|
26
|
-
type HawcxInitializeConfig,
|
|
27
|
-
type PushEvent,
|
|
28
|
-
type AuthEvent,
|
|
29
|
-
type SessionEvent,
|
|
30
|
-
type AuthorizationCodePayload,
|
|
31
|
-
type AdditionalVerificationPayload,
|
|
32
|
-
} from '@hawcx/react-native-sdk';
|
|
33
|
-
import { DEFAULT_HAWCX_CONFIG } from './hawcx.config';
|
|
34
|
-
|
|
35
|
-
const COLORS = {
|
|
36
|
-
bg: '#0f172a',
|
|
37
|
-
card: '#1e293b',
|
|
38
|
-
accent: '#38bdf8',
|
|
39
|
-
text: '#f8fafc',
|
|
40
|
-
muted: '#94a3b8',
|
|
41
|
-
error: '#f87171',
|
|
42
|
-
success: '#4ade80',
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const DEFAULT_BACKEND_URL = 'http://localhost:3000/api/login';
|
|
46
|
-
const BACKEND_FLOW_ENABLED = false;
|
|
47
|
-
|
|
48
|
-
type BackendExchangeResponse = {
|
|
49
|
-
success: boolean;
|
|
50
|
-
message?: string;
|
|
51
|
-
error?: string;
|
|
52
|
-
access_token?: string;
|
|
53
|
-
refresh_token?: string;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const App = () => {
|
|
57
|
-
const [activeConfig, setActiveConfig] = useState<HawcxInitializeConfig | null>(
|
|
58
|
-
DEFAULT_HAWCX_CONFIG,
|
|
59
|
-
);
|
|
60
|
-
const [initStatus, setInitStatus] = useState<'idle' | 'initializing' | 'ready' | 'error'>('idle');
|
|
61
|
-
const [initError, setInitError] = useState<string | null>(null);
|
|
62
|
-
const [email, setEmail] = useState('user@example.com');
|
|
63
|
-
const [otp, setOtp] = useState('');
|
|
64
|
-
const [pin, setPin] = useState('');
|
|
65
|
-
const [token, setToken] = useState('');
|
|
66
|
-
const [pushTokenInput, setPushTokenInput] = useState('');
|
|
67
|
-
const [pushPayloadInput, setPushPayloadInput] = useState('{"request_id":"","ip_address":"","deviceInfo":"","timestamp":""}');
|
|
68
|
-
const [pushRequestId, setPushRequestId] = useState('');
|
|
69
|
-
const [pushEvents, setPushEvents] = useState<PushEvent[]>([]);
|
|
70
|
-
const [pushStatus, setPushStatus] = useState<string | null>(null);
|
|
71
|
-
const [pushError, setPushError] = useState<string | null>(null);
|
|
72
|
-
const [logs, setLogs] = useState<string[]>([]);
|
|
73
|
-
const [loggingEnabled, setLoggingEnabled] = useState(false);
|
|
74
|
-
const [backendUrl, setBackendUrl] = useState(DEFAULT_BACKEND_URL);
|
|
75
|
-
const [lastAuthCode, setLastAuthCode] = useState<AuthorizationCodePayload | null>(null);
|
|
76
|
-
const [additionalVerification, setAdditionalVerification] =
|
|
77
|
-
useState<AdditionalVerificationPayload | null>(null);
|
|
78
|
-
const [backendStatus, setBackendStatus] = useState<string | null>(null);
|
|
79
|
-
const [backendError, setBackendError] = useState<string | null>(null);
|
|
80
|
-
|
|
81
|
-
const { state: authState, authenticate, submitOtp } = useHawcxAuth();
|
|
82
|
-
const web = useHawcxWebLogin();
|
|
83
|
-
|
|
84
|
-
const appendLog = useCallback(
|
|
85
|
-
(message: string) => {
|
|
86
|
-
if (!loggingEnabled) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
const timestamp = new Date().toLocaleTimeString();
|
|
90
|
-
setLogs((prev) => [`[${timestamp}] ${message}`, ...prev].slice(0, 100));
|
|
91
|
-
},
|
|
92
|
-
[loggingEnabled],
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
const exchangeWithBackend = useCallback(
|
|
96
|
-
async (payload: AuthorizationCodePayload) => {
|
|
97
|
-
setLastAuthCode(payload);
|
|
98
|
-
setAdditionalVerification(null);
|
|
99
|
-
setBackendError(null);
|
|
100
|
-
if (!BACKEND_FLOW_ENABLED) {
|
|
101
|
-
setBackendStatus(
|
|
102
|
-
`Demo mode: treated authorization code ${payload.code.slice(0, 6)}… as a successful login.`,
|
|
103
|
-
);
|
|
104
|
-
appendLog(`demo login completed with authorization code ${payload.code}`);
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const trimmedUrl = backendUrl.trim();
|
|
109
|
-
if (!trimmedUrl) {
|
|
110
|
-
setBackendError('Enter a backend URL to send the authorization code.');
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
const trimmedEmail = email.trim();
|
|
114
|
-
if (!trimmedEmail) {
|
|
115
|
-
setBackendError('Enter an email before exchanging the authorization code.');
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
setBackendStatus('Forwarding authorization code to backend…');
|
|
120
|
-
appendLog('forwarding authorization code to backend');
|
|
121
|
-
|
|
122
|
-
try {
|
|
123
|
-
const response = await fetch(trimmedUrl, {
|
|
124
|
-
method: 'POST',
|
|
125
|
-
headers: { 'Content-Type': 'application/json' },
|
|
126
|
-
body: JSON.stringify({
|
|
127
|
-
code: payload.code,
|
|
128
|
-
email: trimmedEmail,
|
|
129
|
-
expires_in: payload.expiresIn,
|
|
130
|
-
}),
|
|
131
|
-
});
|
|
132
|
-
const text = await response.text();
|
|
133
|
-
let parsed: BackendExchangeResponse = { success: response.ok };
|
|
134
|
-
if (text) {
|
|
135
|
-
try {
|
|
136
|
-
parsed = JSON.parse(text);
|
|
137
|
-
} catch {
|
|
138
|
-
// Non-JSON response – fallback to HTTP status.
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
if (!response.ok || parsed.success === false) {
|
|
142
|
-
throw new Error(parsed.error ?? parsed.message ?? `Backend responded with ${response.status}`);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (parsed.access_token) {
|
|
146
|
-
await storeBackendOAuthTokens(
|
|
147
|
-
trimmedEmail,
|
|
148
|
-
parsed.access_token,
|
|
149
|
-
parsed.refresh_token ?? undefined,
|
|
150
|
-
);
|
|
151
|
-
await notifyUserAuthenticated();
|
|
152
|
-
setBackendStatus('Backend tokens stored with Hawcx SDK. Login complete.');
|
|
153
|
-
appendLog('stored backend-issued tokens via Hawcx SDK');
|
|
154
|
-
} else {
|
|
155
|
-
setBackendStatus(
|
|
156
|
-
parsed.message ?? 'Backend accepted the code but did not return access tokens.',
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
} catch (error) {
|
|
160
|
-
const message =
|
|
161
|
-
(error as Error)?.message ?? 'Failed to reach backend. Check ngrok/local server.';
|
|
162
|
-
setBackendError(message);
|
|
163
|
-
setBackendStatus(null);
|
|
164
|
-
appendLog(`backend exchange failed: ${message}`);
|
|
165
|
-
}
|
|
166
|
-
},
|
|
167
|
-
[appendLog, backendUrl, email],
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
useEffect(() => {
|
|
171
|
-
if (!activeConfig) {
|
|
172
|
-
setInitStatus('error');
|
|
173
|
-
setInitError(
|
|
174
|
-
'Add your project API key in example/src/hawcx.config.ts or via the in-app form to initialize the SDK.',
|
|
175
|
-
);
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
setInitStatus('initializing');
|
|
179
|
-
setInitError(null);
|
|
180
|
-
initialize(activeConfig)
|
|
181
|
-
.then(() => {
|
|
182
|
-
setInitStatus('ready');
|
|
183
|
-
appendLog('SDK initialized successfully');
|
|
184
|
-
})
|
|
185
|
-
.catch((err) => {
|
|
186
|
-
console.warn('Init failed', err);
|
|
187
|
-
setInitStatus('error');
|
|
188
|
-
setInitError(err?.message ?? 'Failed to initialize the Hawcx SDK');
|
|
189
|
-
appendLog(`SDK initialization failed: ${err?.message ?? 'unknown error'}`);
|
|
190
|
-
});
|
|
191
|
-
}, [activeConfig, appendLog]);
|
|
192
|
-
|
|
193
|
-
useEffect(() => {
|
|
194
|
-
const authSubscription = addAuthListener((event: AuthEvent) => {
|
|
195
|
-
appendLog(`auth event: ${event.type}`);
|
|
196
|
-
if (event.type === 'auth_error') {
|
|
197
|
-
appendLog(`auth error payload: ${event.payload.code} ${event.payload.message}`);
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
const sessionSubscription = addSessionListener((event: SessionEvent) => {
|
|
201
|
-
appendLog(`session event: ${event.type}`);
|
|
202
|
-
if (event.type === 'session_error') {
|
|
203
|
-
appendLog(`session error payload: ${event.payload.code} ${event.payload.message}`);
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
const pushSubscription = addPushListener((event) => {
|
|
207
|
-
setPushEvents((prev) => [event, ...prev].slice(0, 4));
|
|
208
|
-
setPushStatus(`Received push event: ${event.type}`);
|
|
209
|
-
appendLog(`push event: ${event.type}`);
|
|
210
|
-
});
|
|
211
|
-
return () => {
|
|
212
|
-
authSubscription.remove();
|
|
213
|
-
sessionSubscription.remove();
|
|
214
|
-
pushSubscription.remove();
|
|
215
|
-
};
|
|
216
|
-
}, [appendLog]);
|
|
217
|
-
|
|
218
|
-
useEffect(() => {
|
|
219
|
-
switch (authState.status) {
|
|
220
|
-
case 'authorization_code':
|
|
221
|
-
appendLog(
|
|
222
|
-
`authorization_code event received (backend=${BACKEND_FLOW_ENABLED ? 'on' : 'off'})`,
|
|
223
|
-
);
|
|
224
|
-
void exchangeWithBackend(authState.payload);
|
|
225
|
-
break;
|
|
226
|
-
case 'additional_verification_required':
|
|
227
|
-
appendLog(
|
|
228
|
-
`additional_verification_required: ${authState.payload.sessionId} ${
|
|
229
|
-
authState.payload.detail ?? ''
|
|
230
|
-
}`.trim(),
|
|
231
|
-
);
|
|
232
|
-
setAdditionalVerification(authState.payload);
|
|
233
|
-
break;
|
|
234
|
-
case 'success':
|
|
235
|
-
appendLog('auth_success event received from native SDK');
|
|
236
|
-
setBackendStatus('Hawcx SDK stored tokens automatically.');
|
|
237
|
-
setBackendError(null);
|
|
238
|
-
break;
|
|
239
|
-
case 'error':
|
|
240
|
-
appendLog(`auth_error: ${authState.error.code} ${authState.error.message}`);
|
|
241
|
-
if (!backendError) {
|
|
242
|
-
setBackendError(authState.error.message);
|
|
243
|
-
}
|
|
244
|
-
break;
|
|
245
|
-
case 'pending':
|
|
246
|
-
setBackendError(null);
|
|
247
|
-
setBackendStatus(null);
|
|
248
|
-
setAdditionalVerification(null);
|
|
249
|
-
setLastAuthCode(null);
|
|
250
|
-
break;
|
|
251
|
-
default:
|
|
252
|
-
break;
|
|
253
|
-
}
|
|
254
|
-
}, [appendLog, authState, backendError, exchangeWithBackend]);
|
|
255
|
-
|
|
256
|
-
const summarizeCode = useCallback((code: string) => {
|
|
257
|
-
if (code.length <= 10) {
|
|
258
|
-
return code;
|
|
259
|
-
}
|
|
260
|
-
return `${code.slice(0, 6)}…${code.slice(-4)}`;
|
|
261
|
-
}, []);
|
|
262
|
-
const isReady = initStatus === 'ready';
|
|
263
|
-
const maskedKey = useMemo(() => {
|
|
264
|
-
if (!activeConfig?.projectApiKey) {
|
|
265
|
-
return '';
|
|
266
|
-
}
|
|
267
|
-
const suffix = activeConfig.projectApiKey.slice(-4);
|
|
268
|
-
return `Active key ••••${suffix}`;
|
|
269
|
-
}, [activeConfig?.projectApiKey]);
|
|
270
|
-
|
|
271
|
-
const requireReady = () => {
|
|
272
|
-
if (!isReady) {
|
|
273
|
-
setInitError('Initialize the SDK with your credentials in hawcx.config.ts before calling this action.');
|
|
274
|
-
return false;
|
|
275
|
-
}
|
|
276
|
-
return true;
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
const startAuth = () => {
|
|
280
|
-
if (!requireReady()) {
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
appendLog(`trigger authenticate for ${email}`);
|
|
284
|
-
authenticate(email);
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
const submitOtpCode = () => {
|
|
288
|
-
if (!requireReady()) {
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
appendLog('submit OTP');
|
|
292
|
-
submitOtp(otp);
|
|
293
|
-
setOtp('');
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
const registerPushToken = async () => {
|
|
297
|
-
if (!requireReady()) {
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
const trimmed = pushTokenInput.trim();
|
|
301
|
-
if (!trimmed) {
|
|
302
|
-
setPushError('Enter a token first (FCM token for Android or byte list for iOS).');
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
setPushError(null);
|
|
306
|
-
try {
|
|
307
|
-
if (Platform.OS === 'ios') {
|
|
308
|
-
const bytes = trimmed
|
|
309
|
-
.split(',')
|
|
310
|
-
.map((segment) => parseInt(segment.trim(), 10))
|
|
311
|
-
.filter((value) => !Number.isNaN(value));
|
|
312
|
-
if (!bytes.length) {
|
|
313
|
-
throw new Error('Provide a comma-separated list of APNs byte values for iOS.');
|
|
314
|
-
}
|
|
315
|
-
await setPushDeviceToken(bytes);
|
|
316
|
-
} else {
|
|
317
|
-
await setPushDeviceToken(trimmed);
|
|
318
|
-
}
|
|
319
|
-
appendLog('push token registered with native SDK');
|
|
320
|
-
setPushStatus('Push token submitted to the Hawcx SDK.');
|
|
321
|
-
} catch (error: unknown) {
|
|
322
|
-
setPushError((error as Error)?.message ?? 'Failed to register token');
|
|
323
|
-
}
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
const forwardPush = async () => {
|
|
327
|
-
if (!requireReady()) {
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
setPushError(null);
|
|
331
|
-
try {
|
|
332
|
-
const parsed = JSON.parse(pushPayloadInput);
|
|
333
|
-
await forwardPushPayload(parsed);
|
|
334
|
-
appendLog('forwarded push payload to native SDK');
|
|
335
|
-
setPushStatus('Forwarded payload to the Hawcx SDK.');
|
|
336
|
-
} catch (error: unknown) {
|
|
337
|
-
setPushError((error as Error)?.message ?? 'Invalid JSON payload');
|
|
338
|
-
}
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
const onApprovePush = async () => {
|
|
342
|
-
try {
|
|
343
|
-
await approvePushRequest(pushRequestId.trim());
|
|
344
|
-
appendLog(`approved push request ${pushRequestId.trim()}`);
|
|
345
|
-
setPushStatus('Approved push request');
|
|
346
|
-
} catch (error: unknown) {
|
|
347
|
-
setPushError((error as Error)?.message ?? 'Failed to approve push request');
|
|
348
|
-
}
|
|
349
|
-
};
|
|
350
|
-
|
|
351
|
-
const onDeclinePush = async () => {
|
|
352
|
-
try {
|
|
353
|
-
await declinePushRequest(pushRequestId.trim());
|
|
354
|
-
appendLog(`declined push request ${pushRequestId.trim()}`);
|
|
355
|
-
setPushStatus('Declined push request');
|
|
356
|
-
} catch (error: unknown) {
|
|
357
|
-
setPushError((error as Error)?.message ?? 'Failed to decline push request');
|
|
358
|
-
}
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
const markUserAuthenticated = async () => {
|
|
362
|
-
try {
|
|
363
|
-
await notifyUserAuthenticated();
|
|
364
|
-
appendLog('notified native SDK that user authenticated');
|
|
365
|
-
setPushStatus('Notified native SDK to register push token.');
|
|
366
|
-
} catch (error: unknown) {
|
|
367
|
-
setPushError((error as Error)?.message ?? 'Failed to notify Hawcx SDK');
|
|
368
|
-
}
|
|
369
|
-
};
|
|
370
|
-
|
|
371
|
-
const ScrollContainer = RNScrollView ?? View;
|
|
372
|
-
|
|
373
|
-
return (
|
|
374
|
-
<SafeAreaView style={styles.container}>
|
|
375
|
-
<ScrollContainer
|
|
376
|
-
contentContainerStyle={ScrollContainer === View ? undefined : styles.scrollContent}
|
|
377
|
-
style={ScrollContainer === View ? styles.viewFallback : undefined}>
|
|
378
|
-
<Text style={styles.title}>Hawcx React Native SDK</Text>
|
|
379
|
-
|
|
380
|
-
<View style={styles.card}>
|
|
381
|
-
<Text style={styles.cardTitle}>SDK Status</Text>
|
|
382
|
-
<Text style={styles.status}>State: {initStatus === 'ready' ? 'Ready' : initStatus}</Text>
|
|
383
|
-
{!!maskedKey && <Text style={styles.status}>{maskedKey}</Text>}
|
|
384
|
-
{!!initError && <Text style={[styles.status, styles.errorText]}>{initError}</Text>}
|
|
385
|
-
</View>
|
|
386
|
-
|
|
387
|
-
<View style={styles.card}>
|
|
388
|
-
<Text style={styles.cardTitle}>V5 Authentication</Text>
|
|
389
|
-
<TextInput
|
|
390
|
-
style={styles.input}
|
|
391
|
-
placeholder="Email"
|
|
392
|
-
placeholderTextColor={COLORS.muted}
|
|
393
|
-
value={email}
|
|
394
|
-
onChangeText={setEmail}
|
|
395
|
-
autoCapitalize="none"
|
|
396
|
-
/>
|
|
397
|
-
<TouchableOpacity
|
|
398
|
-
style={[styles.button, !isReady && styles.buttonDisabled]}
|
|
399
|
-
onPress={startAuth}
|
|
400
|
-
disabled={!isReady}>
|
|
401
|
-
<Text style={styles.buttonText}>Authenticate</Text>
|
|
402
|
-
</TouchableOpacity>
|
|
403
|
-
{authState.status === 'otp' && (
|
|
404
|
-
<View style={styles.otpRow}>
|
|
405
|
-
<TextInput
|
|
406
|
-
style={[styles.input, styles.otpInput]}
|
|
407
|
-
placeholder="OTP"
|
|
408
|
-
placeholderTextColor={COLORS.muted}
|
|
409
|
-
value={otp}
|
|
410
|
-
onChangeText={setOtp}
|
|
411
|
-
keyboardType="number-pad"
|
|
412
|
-
/>
|
|
413
|
-
<TouchableOpacity
|
|
414
|
-
style={[styles.button, styles.otpButton, !isReady && styles.buttonDisabled]}
|
|
415
|
-
onPress={submitOtpCode}
|
|
416
|
-
disabled={!isReady}>
|
|
417
|
-
<Text style={styles.buttonText}>Submit</Text>
|
|
418
|
-
</TouchableOpacity>
|
|
419
|
-
</View>
|
|
420
|
-
)}
|
|
421
|
-
<Text style={styles.status}>State: {authState.status}</Text>
|
|
422
|
-
{authState.status === 'error' && (
|
|
423
|
-
<Text style={[styles.status, styles.errorText]}>
|
|
424
|
-
{authState.error.code}: {authState.error.message}
|
|
425
|
-
</Text>
|
|
426
|
-
)}
|
|
427
|
-
{authState.status === 'success' && (
|
|
428
|
-
<Text style={[styles.status, styles.successText]}>Tokens received from Hawcx</Text>
|
|
429
|
-
)}
|
|
430
|
-
</View>
|
|
431
|
-
|
|
432
|
-
<View style={styles.card}>
|
|
433
|
-
<Text style={styles.cardTitle}>Authorization Code & Backend Exchange</Text>
|
|
434
|
-
{!BACKEND_FLOW_ENABLED ? (
|
|
435
|
-
<Text style={styles.status}>
|
|
436
|
-
Demo mode: codes are treated as success locally. Set `BACKEND_FLOW_ENABLED = true` in
|
|
437
|
-
`App.tsx` to forward codes to your backend.
|
|
438
|
-
</Text>
|
|
439
|
-
) : (
|
|
440
|
-
<>
|
|
441
|
-
<TextInput
|
|
442
|
-
style={styles.input}
|
|
443
|
-
placeholder="https://example-ngrok-url.ngrok-free.dev/api/login"
|
|
444
|
-
placeholderTextColor={COLORS.muted}
|
|
445
|
-
autoCapitalize="none"
|
|
446
|
-
value={backendUrl}
|
|
447
|
-
onChangeText={setBackendUrl}
|
|
448
|
-
/>
|
|
449
|
-
<TouchableOpacity
|
|
450
|
-
style={[styles.button, styles.secondaryButton]}
|
|
451
|
-
onPress={() => setBackendUrl(DEFAULT_BACKEND_URL)}>
|
|
452
|
-
<Text style={styles.buttonText}>Use localhost:3000</Text>
|
|
453
|
-
</TouchableOpacity>
|
|
454
|
-
</>
|
|
455
|
-
)}
|
|
456
|
-
{lastAuthCode && (
|
|
457
|
-
<View style={styles.codeBox}>
|
|
458
|
-
<Text style={styles.status}>Latest code: </Text>
|
|
459
|
-
<Text style={styles.monoText}>{summarizeCode(lastAuthCode.code)}</Text>
|
|
460
|
-
<Text style={styles.status}>
|
|
461
|
-
Expires in approximately {lastAuthCode.expiresIn ?? 60} seconds
|
|
462
|
-
</Text>
|
|
463
|
-
</View>
|
|
464
|
-
)}
|
|
465
|
-
{BACKEND_FLOW_ENABLED && lastAuthCode && (
|
|
466
|
-
<TouchableOpacity
|
|
467
|
-
style={[styles.button, styles.rowButton]}
|
|
468
|
-
onPress={() => exchangeWithBackend(lastAuthCode)}>
|
|
469
|
-
<Text style={styles.buttonText}>Retry Backend Exchange</Text>
|
|
470
|
-
</TouchableOpacity>
|
|
471
|
-
)}
|
|
472
|
-
{backendStatus && <Text style={[styles.status, styles.successText]}>{backendStatus}</Text>}
|
|
473
|
-
{backendError && <Text style={[styles.status, styles.errorText]}>{backendError}</Text>}
|
|
474
|
-
{additionalVerification && (
|
|
475
|
-
<Text style={[styles.status, styles.errorText]}>
|
|
476
|
-
Additional verification required ({additionalVerification.sessionId}).{' '}
|
|
477
|
-
{additionalVerification.detail ?? 'Complete verification inside Hawcx Admin.'}
|
|
478
|
-
</Text>
|
|
479
|
-
)}
|
|
480
|
-
</View>
|
|
481
|
-
|
|
482
|
-
<View style={styles.card}>
|
|
483
|
-
<Text style={styles.cardTitle}>Web Login</Text>
|
|
484
|
-
<TextInput
|
|
485
|
-
style={styles.input}
|
|
486
|
-
placeholder="Web PIN"
|
|
487
|
-
placeholderTextColor={COLORS.muted}
|
|
488
|
-
value={pin}
|
|
489
|
-
onChangeText={setPin}
|
|
490
|
-
/>
|
|
491
|
-
<TouchableOpacity
|
|
492
|
-
style={[styles.button, !isReady && styles.buttonDisabled]}
|
|
493
|
-
disabled={!isReady}
|
|
494
|
-
onPress={() => {
|
|
495
|
-
if (requireReady()) {
|
|
496
|
-
web.webLogin(pin);
|
|
497
|
-
}
|
|
498
|
-
}}>
|
|
499
|
-
<Text style={styles.buttonText}>Validate PIN</Text>
|
|
500
|
-
</TouchableOpacity>
|
|
501
|
-
<TextInput
|
|
502
|
-
style={styles.input}
|
|
503
|
-
placeholder="Web Token"
|
|
504
|
-
placeholderTextColor={COLORS.muted}
|
|
505
|
-
value={token}
|
|
506
|
-
onChangeText={setToken}
|
|
507
|
-
/>
|
|
508
|
-
<TouchableOpacity
|
|
509
|
-
style={[styles.button, !isReady && styles.buttonDisabled]}
|
|
510
|
-
disabled={!isReady}
|
|
511
|
-
onPress={() => {
|
|
512
|
-
if (requireReady()) {
|
|
513
|
-
web.webApprove(token);
|
|
514
|
-
}
|
|
515
|
-
}}>
|
|
516
|
-
<Text style={styles.buttonText}>Approve Token</Text>
|
|
517
|
-
</TouchableOpacity>
|
|
518
|
-
<Text style={styles.status}>Web State: {web.state.status}</Text>
|
|
519
|
-
{web.state.status === 'error' && (
|
|
520
|
-
<Text style={[styles.status, styles.errorText]}>
|
|
521
|
-
{web.state.error.code}: {web.state.error.message}
|
|
522
|
-
</Text>
|
|
523
|
-
)}
|
|
524
|
-
</View>
|
|
525
|
-
|
|
526
|
-
<View style={styles.card}>
|
|
527
|
-
<Text style={styles.cardTitle}>Push Approvals (Manual Harness)</Text>
|
|
528
|
-
<Text style={styles.status}>
|
|
529
|
-
Token format: Android expects the FCM string. iOS expects a comma-separated list of APNs byte values.
|
|
530
|
-
</Text>
|
|
531
|
-
<TextInput
|
|
532
|
-
style={[styles.input, styles.payloadInput]}
|
|
533
|
-
placeholder={Platform.OS === 'ios' ? 'e.g. 42, 13, 255' : 'FCM token'}
|
|
534
|
-
placeholderTextColor={COLORS.muted}
|
|
535
|
-
value={pushTokenInput}
|
|
536
|
-
onChangeText={setPushTokenInput}
|
|
537
|
-
multiline
|
|
538
|
-
/>
|
|
539
|
-
<View style={styles.row}>
|
|
540
|
-
<TouchableOpacity style={[styles.button, styles.rowButton]} onPress={registerPushToken} disabled={!isReady}>
|
|
541
|
-
<Text style={styles.buttonText}>Register Token</Text>
|
|
542
|
-
</TouchableOpacity>
|
|
543
|
-
<TouchableOpacity style={[styles.button, styles.rowButton]} onPress={markUserAuthenticated} disabled={!isReady}>
|
|
544
|
-
<Text style={styles.buttonText}>Notify Authenticated</Text>
|
|
545
|
-
</TouchableOpacity>
|
|
546
|
-
</View>
|
|
547
|
-
|
|
548
|
-
<TextInput
|
|
549
|
-
style={[styles.input, styles.payloadInput]}
|
|
550
|
-
placeholder='Raw push payload JSON (e.g. {"request_id": "..."} )'
|
|
551
|
-
placeholderTextColor={COLORS.muted}
|
|
552
|
-
value={pushPayloadInput}
|
|
553
|
-
onChangeText={setPushPayloadInput}
|
|
554
|
-
multiline
|
|
555
|
-
/>
|
|
556
|
-
<TouchableOpacity style={[styles.button, !isReady && styles.buttonDisabled]} onPress={forwardPush} disabled={!isReady}>
|
|
557
|
-
<Text style={styles.buttonText}>Send Payload to SDK</Text>
|
|
558
|
-
</TouchableOpacity>
|
|
559
|
-
|
|
560
|
-
<TextInput
|
|
561
|
-
style={styles.input}
|
|
562
|
-
placeholder="Request ID for approve/decline"
|
|
563
|
-
placeholderTextColor={COLORS.muted}
|
|
564
|
-
value={pushRequestId}
|
|
565
|
-
onChangeText={setPushRequestId}
|
|
566
|
-
/>
|
|
567
|
-
<View style={styles.row}>
|
|
568
|
-
<TouchableOpacity
|
|
569
|
-
style={[styles.button, styles.successButton, (!isReady || !pushRequestId.trim()) && styles.buttonDisabled]}
|
|
570
|
-
onPress={onApprovePush}
|
|
571
|
-
disabled={!isReady || !pushRequestId.trim()}>
|
|
572
|
-
<Text style={styles.buttonText}>Approve</Text>
|
|
573
|
-
</TouchableOpacity>
|
|
574
|
-
<TouchableOpacity
|
|
575
|
-
style={[styles.button, styles.errorButton, (!isReady || !pushRequestId.trim()) && styles.buttonDisabled]}
|
|
576
|
-
onPress={onDeclinePush}
|
|
577
|
-
disabled={!isReady || !pushRequestId.trim()}>
|
|
578
|
-
<Text style={styles.buttonText}>Decline</Text>
|
|
579
|
-
</TouchableOpacity>
|
|
580
|
-
</View>
|
|
581
|
-
|
|
582
|
-
{!!pushStatus && <Text style={[styles.status, styles.successText]}>{pushStatus}</Text>}
|
|
583
|
-
{!!pushError && <Text style={[styles.status, styles.errorText]}>{pushError}</Text>}
|
|
584
|
-
|
|
585
|
-
<View>
|
|
586
|
-
<Text style={styles.status}>Recent Push Events</Text>
|
|
587
|
-
{pushEvents.length === 0 && <Text style={styles.status}>Waiting for events…</Text>}
|
|
588
|
-
{pushEvents.map((event, index) => (
|
|
589
|
-
<View key={`${event.type}-${index}`} style={styles.pushEvent}>
|
|
590
|
-
<Text style={styles.monoText}>{event.type}</Text>
|
|
591
|
-
{'payload' in event && event.payload && (
|
|
592
|
-
<Text style={[styles.monoText, styles.payloadText]}>{JSON.stringify(event.payload, null, 2)}</Text>
|
|
593
|
-
)}
|
|
594
|
-
</View>
|
|
595
|
-
))}
|
|
596
|
-
</View>
|
|
597
|
-
</View>
|
|
598
|
-
|
|
599
|
-
<View style={styles.card}>
|
|
600
|
-
<View style={styles.loggerHeader}>
|
|
601
|
-
<Text style={styles.cardTitle}>Logging</Text>
|
|
602
|
-
<View style={styles.loggerToggle}>
|
|
603
|
-
<Text style={styles.status}>{loggingEnabled ? 'On' : 'Off'}</Text>
|
|
604
|
-
<Switch value={loggingEnabled} onValueChange={setLoggingEnabled} />
|
|
605
|
-
</View>
|
|
606
|
-
</View>
|
|
607
|
-
<Text style={styles.status}>
|
|
608
|
-
Enable logging to see SDK events, errors, and push actions below. Logs are kept in-memory and
|
|
609
|
-
capped to the most recent 10 entries.
|
|
610
|
-
</Text>
|
|
611
|
-
{logs.length === 0 ? (
|
|
612
|
-
<Text style={styles.status}>No logs yet.</Text>
|
|
613
|
-
) : (
|
|
614
|
-
logs.slice(0, 10).map((log) => (
|
|
615
|
-
<Text key={`${log}`} style={[styles.status, styles.logLine]}>
|
|
616
|
-
{log}
|
|
617
|
-
</Text>
|
|
618
|
-
))
|
|
619
|
-
)}
|
|
620
|
-
</View>
|
|
621
|
-
</ScrollContainer>
|
|
622
|
-
</SafeAreaView>
|
|
623
|
-
);
|
|
624
|
-
};
|
|
625
|
-
|
|
626
|
-
const styles = StyleSheet.create({
|
|
627
|
-
container: {
|
|
628
|
-
flex: 1,
|
|
629
|
-
backgroundColor: COLORS.bg,
|
|
630
|
-
},
|
|
631
|
-
scrollContent: {
|
|
632
|
-
padding: 16,
|
|
633
|
-
gap: 16,
|
|
634
|
-
paddingBottom: 32,
|
|
635
|
-
},
|
|
636
|
-
viewFallback: {
|
|
637
|
-
padding: 16,
|
|
638
|
-
gap: 16,
|
|
639
|
-
},
|
|
640
|
-
title: {
|
|
641
|
-
color: COLORS.text,
|
|
642
|
-
fontSize: 22,
|
|
643
|
-
fontWeight: '600',
|
|
644
|
-
},
|
|
645
|
-
card: {
|
|
646
|
-
backgroundColor: COLORS.card,
|
|
647
|
-
padding: 16,
|
|
648
|
-
borderRadius: 12,
|
|
649
|
-
gap: 12,
|
|
650
|
-
},
|
|
651
|
-
cardTitle: {
|
|
652
|
-
color: COLORS.text,
|
|
653
|
-
fontSize: 18,
|
|
654
|
-
fontWeight: '500',
|
|
655
|
-
},
|
|
656
|
-
input: {
|
|
657
|
-
backgroundColor: '#0b1220',
|
|
658
|
-
borderRadius: 8,
|
|
659
|
-
paddingHorizontal: 12,
|
|
660
|
-
paddingVertical: 10,
|
|
661
|
-
color: COLORS.text,
|
|
662
|
-
},
|
|
663
|
-
button: {
|
|
664
|
-
backgroundColor: COLORS.accent,
|
|
665
|
-
paddingVertical: 12,
|
|
666
|
-
borderRadius: 8,
|
|
667
|
-
alignItems: 'center',
|
|
668
|
-
},
|
|
669
|
-
buttonDisabled: {
|
|
670
|
-
opacity: 0.5,
|
|
671
|
-
},
|
|
672
|
-
buttonText: {
|
|
673
|
-
color: '#0f172a',
|
|
674
|
-
fontWeight: '600',
|
|
675
|
-
},
|
|
676
|
-
row: {
|
|
677
|
-
flexDirection: 'row',
|
|
678
|
-
gap: 12,
|
|
679
|
-
},
|
|
680
|
-
switchRow: {
|
|
681
|
-
flexDirection: 'row',
|
|
682
|
-
alignItems: 'center',
|
|
683
|
-
justifyContent: 'space-between',
|
|
684
|
-
},
|
|
685
|
-
rowButton: {
|
|
686
|
-
flex: 1,
|
|
687
|
-
},
|
|
688
|
-
payloadInput: {
|
|
689
|
-
minHeight: 70,
|
|
690
|
-
textAlignVertical: 'top',
|
|
691
|
-
},
|
|
692
|
-
status: {
|
|
693
|
-
color: COLORS.muted,
|
|
694
|
-
},
|
|
695
|
-
errorText: {
|
|
696
|
-
color: COLORS.error,
|
|
697
|
-
},
|
|
698
|
-
successText: {
|
|
699
|
-
color: COLORS.success,
|
|
700
|
-
},
|
|
701
|
-
successButton: {
|
|
702
|
-
backgroundColor: COLORS.success,
|
|
703
|
-
},
|
|
704
|
-
errorButton: {
|
|
705
|
-
backgroundColor: COLORS.error,
|
|
706
|
-
},
|
|
707
|
-
secondaryButton: {
|
|
708
|
-
backgroundColor: '#475569',
|
|
709
|
-
},
|
|
710
|
-
otpRow: {
|
|
711
|
-
flexDirection: 'row',
|
|
712
|
-
gap: 8,
|
|
713
|
-
},
|
|
714
|
-
otpInput: {
|
|
715
|
-
flex: 1,
|
|
716
|
-
},
|
|
717
|
-
otpButton: {
|
|
718
|
-
flex: 0.6,
|
|
719
|
-
},
|
|
720
|
-
pushEvent: {
|
|
721
|
-
backgroundColor: '#0b1527',
|
|
722
|
-
padding: 10,
|
|
723
|
-
borderRadius: 8,
|
|
724
|
-
marginTop: 8,
|
|
725
|
-
},
|
|
726
|
-
monoText: {
|
|
727
|
-
fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace',
|
|
728
|
-
fontSize: 12,
|
|
729
|
-
color: COLORS.text,
|
|
730
|
-
},
|
|
731
|
-
payloadText: {
|
|
732
|
-
marginTop: 4,
|
|
733
|
-
},
|
|
734
|
-
loggerHeader: {
|
|
735
|
-
flexDirection: 'row',
|
|
736
|
-
justifyContent: 'space-between',
|
|
737
|
-
alignItems: 'center',
|
|
738
|
-
},
|
|
739
|
-
loggerToggle: {
|
|
740
|
-
flexDirection: 'row',
|
|
741
|
-
alignItems: 'center',
|
|
742
|
-
gap: 8,
|
|
743
|
-
},
|
|
744
|
-
logLine: {
|
|
745
|
-
fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace',
|
|
746
|
-
},
|
|
747
|
-
codeBox: {
|
|
748
|
-
backgroundColor: '#0b1527',
|
|
749
|
-
borderRadius: 8,
|
|
750
|
-
padding: 12,
|
|
751
|
-
marginTop: 8,
|
|
752
|
-
},
|
|
753
|
-
});
|
|
754
|
-
|
|
755
|
-
export default App;
|