@hawcx/react-native-sdk 1.0.1
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 +10 -0
- package/HawcxReactNative.podspec +25 -0
- package/LICENSE +21 -0
- package/README.md +109 -0
- package/docs/RELEASE.md +119 -0
- package/example/README.md +59 -0
- package/example/android/app/build.gradle +126 -0
- package/example/android/app/debug.keystore +0 -0
- package/example/android/app/proguard-rules.pro +10 -0
- package/example/android/app/src/debug/AndroidManifest.xml +9 -0
- package/example/android/app/src/main/AndroidManifest.xml +27 -0
- package/example/android/app/src/main/java/com/hawcx/example/MainActivity.kt +22 -0
- package/example/android/app/src/main/java/com/hawcx/example/MainApplication.kt +45 -0
- package/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +36 -0
- 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 +3 -0
- package/example/android/app/src/main/res/values/styles.xml +9 -0
- package/example/android/build.gradle +23 -0
- package/example/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/example/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/example/android/gradle.properties +41 -0
- package/example/android/gradlew +249 -0
- package/example/android/gradlew.bat +92 -0
- package/example/android/local.properties +2 -0
- package/example/android/settings.gradle +4 -0
- package/example/app.json +4 -0
- package/example/babel.config.js +3 -0
- package/example/e2e/README.md +17 -0
- package/example/e2e/hawcx-login.yaml +14 -0
- package/example/index.js +5 -0
- package/example/ios/.xcode.env +11 -0
- package/example/ios/HawcxExampleApp/AppDelegate.h +6 -0
- package/example/ios/HawcxExampleApp/AppDelegate.mm +31 -0
- package/example/ios/HawcxExampleApp/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
- package/example/ios/HawcxExampleApp/Images.xcassets/Contents.json +6 -0
- package/example/ios/HawcxExampleApp/Info.plist +55 -0
- package/example/ios/HawcxExampleApp/LaunchScreen.storyboard +47 -0
- package/example/ios/HawcxExampleApp/PrivacyInfo.xcprivacy +37 -0
- package/example/ios/HawcxExampleApp/main.m +10 -0
- package/example/ios/HawcxExampleApp.xcodeproj/project.pbxproj +704 -0
- package/example/ios/HawcxExampleApp.xcodeproj/project.xcworkspace/xcuserdata/agambhullar.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/example/ios/HawcxExampleApp.xcodeproj/xcshareddata/xcschemes/HawcxExampleApp.xcscheme +90 -0
- package/example/ios/HawcxExampleApp.xcodeproj/xcuserdata/agambhullar.xcuserdatad/xcschemes/xcschememanagement.plist +16 -0
- package/example/ios/HawcxExampleApp.xcworkspace/contents.xcworkspacedata +10 -0
- package/example/ios/HawcxExampleAppTests/HawcxExampleAppTests.m +66 -0
- package/example/ios/HawcxExampleAppTests/Info.plist +24 -0
- package/example/ios/Podfile +55 -0
- package/example/ios/Podfile.lock +1290 -0
- package/example/metro.config.js +16 -0
- package/example/package-lock.json +13220 -0
- package/example/package.json +30 -0
- package/example/src/App.tsx +552 -0
- package/example/src/hawcx.config.ts +41 -0
- package/example/tsconfig.json +8 -0
- package/ios/Frameworks/.keep +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/Info.plist +44 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/HawcxFramework +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Headers/HawcxFramework.h +16 -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 +9794 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.private.swiftinterface +302 -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 +302 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/module.modulemap +6 -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/Headers/HawcxFramework.h +16 -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 +9794 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +302 -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 +302 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.abi.json +9794 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +302 -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 +302 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/module.modulemap +6 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/_CodeSignature/CodeResources +234 -0
- package/ios/HawcxReactNative.m +51 -0
- package/ios/HawcxReactNative.swift +311 -0
- package/lib/commonjs/index.js +404 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/module/index.js +375 -0
- package/lib/module/index.js.map +1 -0
- package/lib/typescript/__tests__/index.test.d.ts +2 -0
- package/lib/typescript/__tests__/index.test.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +151 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/package.json +72 -0
- package/react_mobile_sdk_plan.md +240 -0
- package/src/__tests__/index.test.ts +163 -0
- package/src/index.ts +518 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hawcx-react-native-sdk-example",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"start": "react-native start",
|
|
7
|
+
"ios": "react-native run-ios",
|
|
8
|
+
"android": "react-native run-android",
|
|
9
|
+
"lint": "eslint . --ext .js,.ts,.tsx"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@hawcx/react-native-sdk": "file:..",
|
|
13
|
+
"react": "18.2.0",
|
|
14
|
+
"react-native": "0.73.9",
|
|
15
|
+
"react-native-safe-area-context": "4.10.5",
|
|
16
|
+
"react-native-svg": "15.1.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@react-native/eslint-config": "^0.74.0",
|
|
20
|
+
"@react-native/metro-config": "^0.82.1",
|
|
21
|
+
"@types/react": "^18.3.3",
|
|
22
|
+
"@types/react-native": "^0.73.0",
|
|
23
|
+
"babel-jest": "^29.7.0",
|
|
24
|
+
"eslint": "^8.57.0",
|
|
25
|
+
"jest": "^29.7.0",
|
|
26
|
+
"metro-react-native-babel-preset": "^0.76.8",
|
|
27
|
+
"react-test-renderer": "18.2.0",
|
|
28
|
+
"typescript": "^5.4.3"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,552 @@
|
|
|
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
|
+
handlePushNotification as forwardPushPayload,
|
|
23
|
+
approvePushRequest,
|
|
24
|
+
declinePushRequest,
|
|
25
|
+
type HawcxInitializeConfig,
|
|
26
|
+
type PushEvent,
|
|
27
|
+
type AuthEvent,
|
|
28
|
+
type SessionEvent,
|
|
29
|
+
} from '@hawcx/react-native-sdk';
|
|
30
|
+
import { DEFAULT_HAWCX_CONFIG } from './hawcx.config';
|
|
31
|
+
|
|
32
|
+
const COLORS = {
|
|
33
|
+
bg: '#0f172a',
|
|
34
|
+
card: '#1e293b',
|
|
35
|
+
accent: '#38bdf8',
|
|
36
|
+
text: '#f8fafc',
|
|
37
|
+
muted: '#94a3b8',
|
|
38
|
+
error: '#f87171',
|
|
39
|
+
success: '#4ade80',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const App = () => {
|
|
43
|
+
const activeConfig: HawcxInitializeConfig | null = DEFAULT_HAWCX_CONFIG;
|
|
44
|
+
const [initStatus, setInitStatus] = useState<'idle' | 'initializing' | 'ready' | 'error'>(
|
|
45
|
+
DEFAULT_HAWCX_CONFIG ? 'initializing' : 'error',
|
|
46
|
+
);
|
|
47
|
+
const [initError, setInitError] = useState<string | null>(
|
|
48
|
+
DEFAULT_HAWCX_CONFIG ? null : 'Set credentials in example/src/hawcx.config.ts to continue.',
|
|
49
|
+
);
|
|
50
|
+
const [email, setEmail] = useState('user@example.com');
|
|
51
|
+
const [otp, setOtp] = useState('');
|
|
52
|
+
const [pin, setPin] = useState('');
|
|
53
|
+
const [token, setToken] = useState('');
|
|
54
|
+
const [pushTokenInput, setPushTokenInput] = useState('');
|
|
55
|
+
const [pushPayloadInput, setPushPayloadInput] = useState('{"request_id":"","ip_address":"","deviceInfo":"","timestamp":""}');
|
|
56
|
+
const [pushRequestId, setPushRequestId] = useState('');
|
|
57
|
+
const [pushEvents, setPushEvents] = useState<PushEvent[]>([]);
|
|
58
|
+
const [pushStatus, setPushStatus] = useState<string | null>(null);
|
|
59
|
+
const [pushError, setPushError] = useState<string | null>(null);
|
|
60
|
+
const [logs, setLogs] = useState<string[]>([]);
|
|
61
|
+
const [loggingEnabled, setLoggingEnabled] = useState(false);
|
|
62
|
+
|
|
63
|
+
const { state: authState, authenticate, submitOtp } = useHawcxAuth();
|
|
64
|
+
const web = useHawcxWebLogin();
|
|
65
|
+
|
|
66
|
+
const appendLog = useCallback(
|
|
67
|
+
(message: string) => {
|
|
68
|
+
if (!loggingEnabled) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
72
|
+
setLogs((prev) => [`[${timestamp}] ${message}`, ...prev].slice(0, 100));
|
|
73
|
+
},
|
|
74
|
+
[loggingEnabled],
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (!activeConfig) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
setInitStatus('initializing');
|
|
82
|
+
setInitError(null);
|
|
83
|
+
initialize(activeConfig)
|
|
84
|
+
.then(() => {
|
|
85
|
+
setInitStatus('ready');
|
|
86
|
+
appendLog('SDK initialized successfully');
|
|
87
|
+
})
|
|
88
|
+
.catch((err) => {
|
|
89
|
+
console.warn('Init failed', err);
|
|
90
|
+
setInitStatus('error');
|
|
91
|
+
setInitError(err?.message ?? 'Failed to initialize the Hawcx SDK');
|
|
92
|
+
appendLog(`SDK initialization failed: ${err?.message ?? 'unknown error'}`);
|
|
93
|
+
});
|
|
94
|
+
}, [activeConfig, appendLog]);
|
|
95
|
+
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
const authSubscription = addAuthListener((event: AuthEvent) => {
|
|
98
|
+
appendLog(`auth event: ${event.type}`);
|
|
99
|
+
if (event.type === 'auth_error') {
|
|
100
|
+
appendLog(`auth error payload: ${event.payload.code} ${event.payload.message}`);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
const sessionSubscription = addSessionListener((event: SessionEvent) => {
|
|
104
|
+
appendLog(`session event: ${event.type}`);
|
|
105
|
+
if (event.type === 'session_error') {
|
|
106
|
+
appendLog(`session error payload: ${event.payload.code} ${event.payload.message}`);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
const pushSubscription = addPushListener((event) => {
|
|
110
|
+
setPushEvents((prev) => [event, ...prev].slice(0, 4));
|
|
111
|
+
setPushStatus(`Received push event: ${event.type}`);
|
|
112
|
+
appendLog(`push event: ${event.type}`);
|
|
113
|
+
});
|
|
114
|
+
return () => {
|
|
115
|
+
authSubscription.remove();
|
|
116
|
+
sessionSubscription.remove();
|
|
117
|
+
pushSubscription.remove();
|
|
118
|
+
};
|
|
119
|
+
}, [appendLog]);
|
|
120
|
+
|
|
121
|
+
const isReady = initStatus === 'ready';
|
|
122
|
+
const maskedKey = useMemo(() => {
|
|
123
|
+
if (!activeConfig?.projectApiKey) {
|
|
124
|
+
return '';
|
|
125
|
+
}
|
|
126
|
+
const suffix = activeConfig.projectApiKey.slice(-4);
|
|
127
|
+
return `Active key ••••${suffix}`;
|
|
128
|
+
}, [activeConfig?.projectApiKey]);
|
|
129
|
+
const oauthStatus = activeConfig?.oauthConfig ? 'Configured' : 'Not provided';
|
|
130
|
+
|
|
131
|
+
const requireReady = () => {
|
|
132
|
+
if (!isReady) {
|
|
133
|
+
setInitError('Initialize the SDK with your credentials in hawcx.config.ts before calling this action.');
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
return true;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const startAuth = () => {
|
|
140
|
+
if (!requireReady()) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
appendLog(`trigger authenticate for ${email}`);
|
|
144
|
+
authenticate(email);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const submitOtpCode = () => {
|
|
148
|
+
if (!requireReady()) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
appendLog('submit OTP');
|
|
152
|
+
submitOtp(otp);
|
|
153
|
+
setOtp('');
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const registerPushToken = async () => {
|
|
157
|
+
if (!requireReady()) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const trimmed = pushTokenInput.trim();
|
|
161
|
+
if (!trimmed) {
|
|
162
|
+
setPushError('Enter a token first (FCM token for Android or byte list for iOS).');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
setPushError(null);
|
|
166
|
+
try {
|
|
167
|
+
if (Platform.OS === 'ios') {
|
|
168
|
+
const bytes = trimmed
|
|
169
|
+
.split(',')
|
|
170
|
+
.map((segment) => parseInt(segment.trim(), 10))
|
|
171
|
+
.filter((value) => !Number.isNaN(value));
|
|
172
|
+
if (!bytes.length) {
|
|
173
|
+
throw new Error('Provide a comma-separated list of APNs byte values for iOS.');
|
|
174
|
+
}
|
|
175
|
+
await setPushDeviceToken(bytes);
|
|
176
|
+
} else {
|
|
177
|
+
await setPushDeviceToken(trimmed);
|
|
178
|
+
}
|
|
179
|
+
appendLog('push token registered with native SDK');
|
|
180
|
+
setPushStatus('Push token submitted to the Hawcx SDK.');
|
|
181
|
+
} catch (error: unknown) {
|
|
182
|
+
setPushError((error as Error)?.message ?? 'Failed to register token');
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const forwardPush = async () => {
|
|
187
|
+
if (!requireReady()) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
setPushError(null);
|
|
191
|
+
try {
|
|
192
|
+
const parsed = JSON.parse(pushPayloadInput);
|
|
193
|
+
await forwardPushPayload(parsed);
|
|
194
|
+
appendLog('forwarded push payload to native SDK');
|
|
195
|
+
setPushStatus('Forwarded payload to the Hawcx SDK.');
|
|
196
|
+
} catch (error: unknown) {
|
|
197
|
+
setPushError((error as Error)?.message ?? 'Invalid JSON payload');
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const onApprovePush = async () => {
|
|
202
|
+
try {
|
|
203
|
+
await approvePushRequest(pushRequestId.trim());
|
|
204
|
+
appendLog(`approved push request ${pushRequestId.trim()}`);
|
|
205
|
+
setPushStatus('Approved push request');
|
|
206
|
+
} catch (error: unknown) {
|
|
207
|
+
setPushError((error as Error)?.message ?? 'Failed to approve push request');
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const onDeclinePush = async () => {
|
|
212
|
+
try {
|
|
213
|
+
await declinePushRequest(pushRequestId.trim());
|
|
214
|
+
appendLog(`declined push request ${pushRequestId.trim()}`);
|
|
215
|
+
setPushStatus('Declined push request');
|
|
216
|
+
} catch (error: unknown) {
|
|
217
|
+
setPushError((error as Error)?.message ?? 'Failed to decline push request');
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const markUserAuthenticated = async () => {
|
|
222
|
+
try {
|
|
223
|
+
await notifyUserAuthenticated();
|
|
224
|
+
appendLog('notified native SDK that user authenticated');
|
|
225
|
+
setPushStatus('Notified native SDK to register push token.');
|
|
226
|
+
} catch (error: unknown) {
|
|
227
|
+
setPushError((error as Error)?.message ?? 'Failed to notify Hawcx SDK');
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const ScrollContainer = RNScrollView ?? View;
|
|
232
|
+
|
|
233
|
+
return (
|
|
234
|
+
<SafeAreaView style={styles.container}>
|
|
235
|
+
<ScrollContainer
|
|
236
|
+
contentContainerStyle={ScrollContainer === View ? undefined : styles.scrollContent}
|
|
237
|
+
style={ScrollContainer === View ? styles.viewFallback : undefined}>
|
|
238
|
+
<Text style={styles.title}>Hawcx React Native SDK</Text>
|
|
239
|
+
|
|
240
|
+
<View style={styles.card}>
|
|
241
|
+
<Text style={styles.cardTitle}>SDK Status</Text>
|
|
242
|
+
<Text style={styles.status}>State: {initStatus === 'ready' ? 'Ready' : initStatus}</Text>
|
|
243
|
+
{!!maskedKey && <Text style={styles.status}>{maskedKey}</Text>}
|
|
244
|
+
<Text style={styles.status}>OAuth: {oauthStatus}</Text>
|
|
245
|
+
{!!initError && <Text style={[styles.status, styles.errorText]}>{initError}</Text>}
|
|
246
|
+
</View>
|
|
247
|
+
|
|
248
|
+
<View style={styles.card}>
|
|
249
|
+
<Text style={styles.cardTitle}>V5 Authentication</Text>
|
|
250
|
+
<TextInput
|
|
251
|
+
style={styles.input}
|
|
252
|
+
placeholder="Email"
|
|
253
|
+
placeholderTextColor={COLORS.muted}
|
|
254
|
+
value={email}
|
|
255
|
+
onChangeText={setEmail}
|
|
256
|
+
autoCapitalize="none"
|
|
257
|
+
/>
|
|
258
|
+
<TouchableOpacity
|
|
259
|
+
style={[styles.button, !isReady && styles.buttonDisabled]}
|
|
260
|
+
onPress={startAuth}
|
|
261
|
+
disabled={!isReady}>
|
|
262
|
+
<Text style={styles.buttonText}>Authenticate</Text>
|
|
263
|
+
</TouchableOpacity>
|
|
264
|
+
{authState.status === 'otp' && (
|
|
265
|
+
<View style={styles.otpRow}>
|
|
266
|
+
<TextInput
|
|
267
|
+
style={[styles.input, styles.otpInput]}
|
|
268
|
+
placeholder="OTP"
|
|
269
|
+
placeholderTextColor={COLORS.muted}
|
|
270
|
+
value={otp}
|
|
271
|
+
onChangeText={setOtp}
|
|
272
|
+
keyboardType="number-pad"
|
|
273
|
+
/>
|
|
274
|
+
<TouchableOpacity
|
|
275
|
+
style={[styles.button, styles.otpButton, !isReady && styles.buttonDisabled]}
|
|
276
|
+
onPress={submitOtpCode}
|
|
277
|
+
disabled={!isReady}>
|
|
278
|
+
<Text style={styles.buttonText}>Submit</Text>
|
|
279
|
+
</TouchableOpacity>
|
|
280
|
+
</View>
|
|
281
|
+
)}
|
|
282
|
+
<Text style={styles.status}>State: {authState.status}</Text>
|
|
283
|
+
{authState.status === 'error' && (
|
|
284
|
+
<Text style={[styles.status, styles.errorText]}>
|
|
285
|
+
{authState.error.code}: {authState.error.message}
|
|
286
|
+
</Text>
|
|
287
|
+
)}
|
|
288
|
+
{authState.status === 'success' && (
|
|
289
|
+
<Text style={[styles.status, styles.successText]}>Tokens received from Hawcx</Text>
|
|
290
|
+
)}
|
|
291
|
+
</View>
|
|
292
|
+
|
|
293
|
+
<View style={styles.card}>
|
|
294
|
+
<Text style={styles.cardTitle}>Web Login</Text>
|
|
295
|
+
<TextInput
|
|
296
|
+
style={styles.input}
|
|
297
|
+
placeholder="Web PIN"
|
|
298
|
+
placeholderTextColor={COLORS.muted}
|
|
299
|
+
value={pin}
|
|
300
|
+
onChangeText={setPin}
|
|
301
|
+
/>
|
|
302
|
+
<TouchableOpacity
|
|
303
|
+
style={[styles.button, !isReady && styles.buttonDisabled]}
|
|
304
|
+
disabled={!isReady}
|
|
305
|
+
onPress={() => {
|
|
306
|
+
if (requireReady()) {
|
|
307
|
+
web.webLogin(pin);
|
|
308
|
+
}
|
|
309
|
+
}}>
|
|
310
|
+
<Text style={styles.buttonText}>Validate PIN</Text>
|
|
311
|
+
</TouchableOpacity>
|
|
312
|
+
<TextInput
|
|
313
|
+
style={styles.input}
|
|
314
|
+
placeholder="Web Token"
|
|
315
|
+
placeholderTextColor={COLORS.muted}
|
|
316
|
+
value={token}
|
|
317
|
+
onChangeText={setToken}
|
|
318
|
+
/>
|
|
319
|
+
<TouchableOpacity
|
|
320
|
+
style={[styles.button, !isReady && styles.buttonDisabled]}
|
|
321
|
+
disabled={!isReady}
|
|
322
|
+
onPress={() => {
|
|
323
|
+
if (requireReady()) {
|
|
324
|
+
web.webApprove(token);
|
|
325
|
+
}
|
|
326
|
+
}}>
|
|
327
|
+
<Text style={styles.buttonText}>Approve Token</Text>
|
|
328
|
+
</TouchableOpacity>
|
|
329
|
+
<Text style={styles.status}>Web State: {web.state.status}</Text>
|
|
330
|
+
{web.state.status === 'error' && (
|
|
331
|
+
<Text style={[styles.status, styles.errorText]}>
|
|
332
|
+
{web.state.error.code}: {web.state.error.message}
|
|
333
|
+
</Text>
|
|
334
|
+
)}
|
|
335
|
+
</View>
|
|
336
|
+
|
|
337
|
+
<View style={styles.card}>
|
|
338
|
+
<Text style={styles.cardTitle}>Push Approvals (Manual Harness)</Text>
|
|
339
|
+
<Text style={styles.status}>
|
|
340
|
+
Token format: Android expects the FCM string. iOS expects a comma-separated list of APNs byte values.
|
|
341
|
+
</Text>
|
|
342
|
+
<TextInput
|
|
343
|
+
style={[styles.input, styles.payloadInput]}
|
|
344
|
+
placeholder={Platform.OS === 'ios' ? 'e.g. 42, 13, 255' : 'FCM token'}
|
|
345
|
+
placeholderTextColor={COLORS.muted}
|
|
346
|
+
value={pushTokenInput}
|
|
347
|
+
onChangeText={setPushTokenInput}
|
|
348
|
+
multiline
|
|
349
|
+
/>
|
|
350
|
+
<View style={styles.row}>
|
|
351
|
+
<TouchableOpacity style={[styles.button, styles.rowButton]} onPress={registerPushToken} disabled={!isReady}>
|
|
352
|
+
<Text style={styles.buttonText}>Register Token</Text>
|
|
353
|
+
</TouchableOpacity>
|
|
354
|
+
<TouchableOpacity style={[styles.button, styles.rowButton]} onPress={markUserAuthenticated} disabled={!isReady}>
|
|
355
|
+
<Text style={styles.buttonText}>Notify Authenticated</Text>
|
|
356
|
+
</TouchableOpacity>
|
|
357
|
+
</View>
|
|
358
|
+
|
|
359
|
+
<TextInput
|
|
360
|
+
style={[styles.input, styles.payloadInput]}
|
|
361
|
+
placeholder='Raw push payload JSON (e.g. {"request_id": "..."} )'
|
|
362
|
+
placeholderTextColor={COLORS.muted}
|
|
363
|
+
value={pushPayloadInput}
|
|
364
|
+
onChangeText={setPushPayloadInput}
|
|
365
|
+
multiline
|
|
366
|
+
/>
|
|
367
|
+
<TouchableOpacity style={[styles.button, !isReady && styles.buttonDisabled]} onPress={forwardPush} disabled={!isReady}>
|
|
368
|
+
<Text style={styles.buttonText}>Send Payload to SDK</Text>
|
|
369
|
+
</TouchableOpacity>
|
|
370
|
+
|
|
371
|
+
<TextInput
|
|
372
|
+
style={styles.input}
|
|
373
|
+
placeholder="Request ID for approve/decline"
|
|
374
|
+
placeholderTextColor={COLORS.muted}
|
|
375
|
+
value={pushRequestId}
|
|
376
|
+
onChangeText={setPushRequestId}
|
|
377
|
+
/>
|
|
378
|
+
<View style={styles.row}>
|
|
379
|
+
<TouchableOpacity
|
|
380
|
+
style={[styles.button, styles.successButton, (!isReady || !pushRequestId.trim()) && styles.buttonDisabled]}
|
|
381
|
+
onPress={onApprovePush}
|
|
382
|
+
disabled={!isReady || !pushRequestId.trim()}>
|
|
383
|
+
<Text style={styles.buttonText}>Approve</Text>
|
|
384
|
+
</TouchableOpacity>
|
|
385
|
+
<TouchableOpacity
|
|
386
|
+
style={[styles.button, styles.errorButton, (!isReady || !pushRequestId.trim()) && styles.buttonDisabled]}
|
|
387
|
+
onPress={onDeclinePush}
|
|
388
|
+
disabled={!isReady || !pushRequestId.trim()}>
|
|
389
|
+
<Text style={styles.buttonText}>Decline</Text>
|
|
390
|
+
</TouchableOpacity>
|
|
391
|
+
</View>
|
|
392
|
+
|
|
393
|
+
{!!pushStatus && <Text style={[styles.status, styles.successText]}>{pushStatus}</Text>}
|
|
394
|
+
{!!pushError && <Text style={[styles.status, styles.errorText]}>{pushError}</Text>}
|
|
395
|
+
|
|
396
|
+
<View>
|
|
397
|
+
<Text style={styles.status}>Recent Push Events</Text>
|
|
398
|
+
{pushEvents.length === 0 && <Text style={styles.status}>Waiting for events…</Text>}
|
|
399
|
+
{pushEvents.map((event, index) => (
|
|
400
|
+
<View key={`${event.type}-${index}`} style={styles.pushEvent}>
|
|
401
|
+
<Text style={styles.monoText}>{event.type}</Text>
|
|
402
|
+
{'payload' in event && event.payload && (
|
|
403
|
+
<Text style={[styles.monoText, styles.payloadText]}>{JSON.stringify(event.payload, null, 2)}</Text>
|
|
404
|
+
)}
|
|
405
|
+
</View>
|
|
406
|
+
))}
|
|
407
|
+
</View>
|
|
408
|
+
</View>
|
|
409
|
+
|
|
410
|
+
<View style={styles.card}>
|
|
411
|
+
<View style={styles.loggerHeader}>
|
|
412
|
+
<Text style={styles.cardTitle}>Logging</Text>
|
|
413
|
+
<View style={styles.loggerToggle}>
|
|
414
|
+
<Text style={styles.status}>{loggingEnabled ? 'On' : 'Off'}</Text>
|
|
415
|
+
<Switch value={loggingEnabled} onValueChange={setLoggingEnabled} />
|
|
416
|
+
</View>
|
|
417
|
+
</View>
|
|
418
|
+
<Text style={styles.status}>
|
|
419
|
+
Enable logging to see SDK events, errors, and push actions below. Logs are kept in-memory and
|
|
420
|
+
capped to the most recent 10 entries.
|
|
421
|
+
</Text>
|
|
422
|
+
{logs.length === 0 ? (
|
|
423
|
+
<Text style={styles.status}>No logs yet.</Text>
|
|
424
|
+
) : (
|
|
425
|
+
logs.slice(0, 10).map((log) => (
|
|
426
|
+
<Text key={`${log}`} style={[styles.status, styles.logLine]}>
|
|
427
|
+
{log}
|
|
428
|
+
</Text>
|
|
429
|
+
))
|
|
430
|
+
)}
|
|
431
|
+
</View>
|
|
432
|
+
</ScrollContainer>
|
|
433
|
+
</SafeAreaView>
|
|
434
|
+
);
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
const styles = StyleSheet.create({
|
|
438
|
+
container: {
|
|
439
|
+
flex: 1,
|
|
440
|
+
backgroundColor: COLORS.bg,
|
|
441
|
+
},
|
|
442
|
+
scrollContent: {
|
|
443
|
+
padding: 16,
|
|
444
|
+
gap: 16,
|
|
445
|
+
paddingBottom: 32,
|
|
446
|
+
},
|
|
447
|
+
viewFallback: {
|
|
448
|
+
padding: 16,
|
|
449
|
+
gap: 16,
|
|
450
|
+
},
|
|
451
|
+
title: {
|
|
452
|
+
color: COLORS.text,
|
|
453
|
+
fontSize: 22,
|
|
454
|
+
fontWeight: '600',
|
|
455
|
+
},
|
|
456
|
+
card: {
|
|
457
|
+
backgroundColor: COLORS.card,
|
|
458
|
+
padding: 16,
|
|
459
|
+
borderRadius: 12,
|
|
460
|
+
gap: 12,
|
|
461
|
+
},
|
|
462
|
+
cardTitle: {
|
|
463
|
+
color: COLORS.text,
|
|
464
|
+
fontSize: 18,
|
|
465
|
+
fontWeight: '500',
|
|
466
|
+
},
|
|
467
|
+
input: {
|
|
468
|
+
backgroundColor: '#0b1220',
|
|
469
|
+
borderRadius: 8,
|
|
470
|
+
paddingHorizontal: 12,
|
|
471
|
+
paddingVertical: 10,
|
|
472
|
+
color: COLORS.text,
|
|
473
|
+
},
|
|
474
|
+
button: {
|
|
475
|
+
backgroundColor: COLORS.accent,
|
|
476
|
+
paddingVertical: 12,
|
|
477
|
+
borderRadius: 8,
|
|
478
|
+
alignItems: 'center',
|
|
479
|
+
},
|
|
480
|
+
buttonDisabled: {
|
|
481
|
+
opacity: 0.5,
|
|
482
|
+
},
|
|
483
|
+
buttonText: {
|
|
484
|
+
color: '#0f172a',
|
|
485
|
+
fontWeight: '600',
|
|
486
|
+
},
|
|
487
|
+
row: {
|
|
488
|
+
flexDirection: 'row',
|
|
489
|
+
gap: 12,
|
|
490
|
+
},
|
|
491
|
+
rowButton: {
|
|
492
|
+
flex: 1,
|
|
493
|
+
},
|
|
494
|
+
payloadInput: {
|
|
495
|
+
minHeight: 70,
|
|
496
|
+
textAlignVertical: 'top',
|
|
497
|
+
},
|
|
498
|
+
status: {
|
|
499
|
+
color: COLORS.muted,
|
|
500
|
+
},
|
|
501
|
+
errorText: {
|
|
502
|
+
color: COLORS.error,
|
|
503
|
+
},
|
|
504
|
+
successText: {
|
|
505
|
+
color: COLORS.success,
|
|
506
|
+
},
|
|
507
|
+
successButton: {
|
|
508
|
+
backgroundColor: COLORS.success,
|
|
509
|
+
},
|
|
510
|
+
errorButton: {
|
|
511
|
+
backgroundColor: COLORS.error,
|
|
512
|
+
},
|
|
513
|
+
otpRow: {
|
|
514
|
+
flexDirection: 'row',
|
|
515
|
+
gap: 8,
|
|
516
|
+
},
|
|
517
|
+
otpInput: {
|
|
518
|
+
flex: 1,
|
|
519
|
+
},
|
|
520
|
+
otpButton: {
|
|
521
|
+
flex: 0.6,
|
|
522
|
+
},
|
|
523
|
+
pushEvent: {
|
|
524
|
+
backgroundColor: '#0b1527',
|
|
525
|
+
padding: 10,
|
|
526
|
+
borderRadius: 8,
|
|
527
|
+
marginTop: 8,
|
|
528
|
+
},
|
|
529
|
+
monoText: {
|
|
530
|
+
fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace',
|
|
531
|
+
fontSize: 12,
|
|
532
|
+
color: COLORS.text,
|
|
533
|
+
},
|
|
534
|
+
payloadText: {
|
|
535
|
+
marginTop: 4,
|
|
536
|
+
},
|
|
537
|
+
loggerHeader: {
|
|
538
|
+
flexDirection: 'row',
|
|
539
|
+
justifyContent: 'space-between',
|
|
540
|
+
alignItems: 'center',
|
|
541
|
+
},
|
|
542
|
+
loggerToggle: {
|
|
543
|
+
flexDirection: 'row',
|
|
544
|
+
alignItems: 'center',
|
|
545
|
+
gap: 8,
|
|
546
|
+
},
|
|
547
|
+
logLine: {
|
|
548
|
+
fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace',
|
|
549
|
+
},
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
export default App;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { HawcxInitializeConfig } from '@hawcx/react-native-sdk';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Set these constants if you prefer to hard-code dev credentials instead of using
|
|
5
|
+
* the in-app configuration inputs.
|
|
6
|
+
*/
|
|
7
|
+
export const HAWCX_PROJECT_API_KEY = 'YE3a8gqIPPiiRw3dotlMo3mksiamUy1V';
|
|
8
|
+
export const HAWCX_OAUTH_CLIENT_ID = 'parenthive-demo.hawcx.com';
|
|
9
|
+
export const HAWCX_OAUTH_TOKEN_ENDPOINT = 'https://parenthive-api.hawcx.com/oauth2/token';
|
|
10
|
+
export const HAWCX_OAUTH_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
11
|
+
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBFPGnOqFYdmJr6nYpJyYuArjf6LKg
|
|
12
|
+
pr0XRbu6X9515vaX3oMW0MZCikmZ9Rph+GgvELzf2EgHX6PcdHPEnuYB5n8BrdUf
|
|
13
|
+
Yu2TDW4QfK9r2QgNbi34h87SLzPaHXPomwEAmxk2SQrH0gSYiJRna7pPeDj7aStX
|
|
14
|
+
v/xVOP7RclIR29l+SwU=
|
|
15
|
+
-----END PUBLIC KEY-----`;
|
|
16
|
+
|
|
17
|
+
const buildDefaultConfig = (): HawcxInitializeConfig | null => {
|
|
18
|
+
const trimmedKey = HAWCX_PROJECT_API_KEY.trim();
|
|
19
|
+
if (!trimmedKey) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const baseConfig: HawcxInitializeConfig = {
|
|
23
|
+
projectApiKey: trimmedKey,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const trimmedClientId = HAWCX_OAUTH_CLIENT_ID.trim();
|
|
27
|
+
const trimmedEndpoint = HAWCX_OAUTH_TOKEN_ENDPOINT.trim();
|
|
28
|
+
const trimmedPem = HAWCX_OAUTH_PUBLIC_KEY_PEM.trim();
|
|
29
|
+
|
|
30
|
+
if (trimmedClientId && trimmedEndpoint && trimmedPem) {
|
|
31
|
+
baseConfig.oauthConfig = {
|
|
32
|
+
clientId: trimmedClientId,
|
|
33
|
+
tokenEndpoint: trimmedEndpoint,
|
|
34
|
+
publicKeyPem: trimmedPem,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return baseConfig;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const DEFAULT_HAWCX_CONFIG = buildDefaultConfig();
|
|
File without changes
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>AvailableLibraries</key>
|
|
6
|
+
<array>
|
|
7
|
+
<dict>
|
|
8
|
+
<key>BinaryPath</key>
|
|
9
|
+
<string>HawcxFramework.framework/HawcxFramework</string>
|
|
10
|
+
<key>LibraryIdentifier</key>
|
|
11
|
+
<string>ios-arm64_x86_64-simulator</string>
|
|
12
|
+
<key>LibraryPath</key>
|
|
13
|
+
<string>HawcxFramework.framework</string>
|
|
14
|
+
<key>SupportedArchitectures</key>
|
|
15
|
+
<array>
|
|
16
|
+
<string>arm64</string>
|
|
17
|
+
<string>x86_64</string>
|
|
18
|
+
</array>
|
|
19
|
+
<key>SupportedPlatform</key>
|
|
20
|
+
<string>ios</string>
|
|
21
|
+
<key>SupportedPlatformVariant</key>
|
|
22
|
+
<string>simulator</string>
|
|
23
|
+
</dict>
|
|
24
|
+
<dict>
|
|
25
|
+
<key>BinaryPath</key>
|
|
26
|
+
<string>HawcxFramework.framework/HawcxFramework</string>
|
|
27
|
+
<key>LibraryIdentifier</key>
|
|
28
|
+
<string>ios-arm64</string>
|
|
29
|
+
<key>LibraryPath</key>
|
|
30
|
+
<string>HawcxFramework.framework</string>
|
|
31
|
+
<key>SupportedArchitectures</key>
|
|
32
|
+
<array>
|
|
33
|
+
<string>arm64</string>
|
|
34
|
+
</array>
|
|
35
|
+
<key>SupportedPlatform</key>
|
|
36
|
+
<string>ios</string>
|
|
37
|
+
</dict>
|
|
38
|
+
</array>
|
|
39
|
+
<key>CFBundlePackageType</key>
|
|
40
|
+
<string>XFWK</string>
|
|
41
|
+
<key>XCFrameworkFormatVersion</key>
|
|
42
|
+
<string>1.0</string>
|
|
43
|
+
</dict>
|
|
44
|
+
</plist>
|
package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/HawcxFramework
ADDED
|
Binary file
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//
|
|
2
|
+
// HawcxFramework.h
|
|
3
|
+
// HawcxFramework
|
|
4
|
+
//
|
|
5
|
+
// Created by dev on 10/15/24.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import <Foundation/Foundation.h>
|
|
9
|
+
|
|
10
|
+
//! Project version number for HawcxFramework.
|
|
11
|
+
FOUNDATION_EXPORT double HawcxFrameworkVersionNumber;
|
|
12
|
+
|
|
13
|
+
//! Project version string for HawcxFramework.
|
|
14
|
+
FOUNDATION_EXPORT const unsigned char HawcxFrameworkVersionString[];
|
|
15
|
+
|
|
16
|
+
// In this header, you should import all the public headers of your framework using statements like #import <HawcxFramework/PublicHeader.h>
|