@testernest/react-native 0.1.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.
Files changed (139) hide show
  1. package/README.md +108 -0
  2. package/android/build/.transforms/8ce4b91dc8d15c015a4e81859b96eec6/results.bin +1 -0
  3. package/android/build/.transforms/8ce4b91dc8d15c015a4e81859b96eec6/transformed/classes/classes.dex +0 -0
  4. package/android/build/generated/source/buildConfig/debug/com/testernest/reactnative/BuildConfig.java +10 -0
  5. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/aapt/AndroidManifest.xml +7 -0
  6. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/aapt/output-metadata.json +18 -0
  7. package/android/build/intermediates/aar_metadata/debug/aar-metadata.properties +5 -0
  8. package/android/build/intermediates/annotation_processor_list/debug/annotationProcessors.json +1 -0
  9. package/android/build/intermediates/compile_library_classes_jar/debug/classes.jar +0 -0
  10. package/android/build/intermediates/compile_r_class_jar/debug/R.jar +0 -0
  11. package/android/build/intermediates/compile_symbol_list/debug/R.txt +1986 -0
  12. package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -0
  13. package/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml +2 -0
  14. package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +2 -0
  15. package/android/build/intermediates/incremental/mergeDebugShaders/merger.xml +2 -0
  16. package/android/build/intermediates/incremental/packageDebugAssets/merger.xml +2 -0
  17. package/android/build/intermediates/javac/debug/classes/com/testernest/reactnative/BuildConfig.class +0 -0
  18. package/android/build/intermediates/library_java_res/debug/res.jar +0 -0
  19. package/android/build/intermediates/local_only_symbol_list/debug/R-def.txt +2 -0
  20. package/android/build/intermediates/manifest_merge_blame_file/debug/manifest-merger-blame-debug-report.txt +8 -0
  21. package/android/build/intermediates/merged_manifest/debug/AndroidManifest.xml +7 -0
  22. package/android/build/intermediates/navigation_json/debug/navigation.json +1 -0
  23. package/android/build/intermediates/packaged_manifests/debug/output-metadata.json +18 -0
  24. package/android/build/intermediates/runtime_library_classes_jar/debug/classes.jar +0 -0
  25. package/android/build/intermediates/symbol_list_with_package_name/debug/package-aware-r.txt +1479 -0
  26. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
  27. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream +0 -0
  28. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream.len +0 -0
  29. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.len +0 -0
  30. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
  31. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab_i +0 -0
  32. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab_i.len +0 -0
  33. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
  34. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream +0 -0
  35. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len +0 -0
  36. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.len +0 -0
  37. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.values.at +0 -0
  38. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i +0 -0
  39. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i.len +0 -0
  40. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
  41. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream +0 -0
  42. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len +0 -0
  43. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len +0 -0
  44. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at +0 -0
  45. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i +0 -0
  46. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i.len +0 -0
  47. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab +0 -0
  48. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.keystream +0 -0
  49. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.keystream.len +0 -0
  50. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.len +0 -0
  51. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.values.at +0 -0
  52. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab_i +0 -0
  53. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab_i.len +0 -0
  54. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
  55. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
  56. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
  57. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len +0 -0
  58. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at +0 -0
  59. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
  60. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i.len +0 -0
  61. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/java-sources-proto-map.tab +0 -0
  62. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/java-sources-proto-map.tab.keystream +0 -0
  63. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/java-sources-proto-map.tab.keystream.len +0 -0
  64. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/java-sources-proto-map.tab.len +0 -0
  65. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/java-sources-proto-map.tab.values.at +0 -0
  66. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/java-sources-proto-map.tab_i +0 -0
  67. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/java-sources-proto-map.tab_i.len +0 -0
  68. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
  69. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream +0 -0
  70. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream.len +0 -0
  71. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.len +0 -0
  72. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
  73. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i +0 -0
  74. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i.len +0 -0
  75. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
  76. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream +0 -0
  77. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream.len +0 -0
  78. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.len +0 -0
  79. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
  80. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab_i +0 -0
  81. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab_i.len +0 -0
  82. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab +0 -0
  83. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.keystream +0 -0
  84. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.keystream.len +0 -0
  85. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.len +0 -0
  86. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at +0 -0
  87. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab_i +0 -0
  88. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab_i.len +0 -0
  89. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab +0 -0
  90. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream +0 -0
  91. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream.len +0 -0
  92. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.len +0 -0
  93. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.values.at +0 -0
  94. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i +0 -0
  95. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i.len +0 -0
  96. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/counters.tab +2 -0
  97. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab +0 -0
  98. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream +0 -0
  99. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream.len +0 -0
  100. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.len +0 -0
  101. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.values.at +0 -0
  102. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab_i +0 -0
  103. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab_i.len +0 -0
  104. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab +0 -0
  105. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream +0 -0
  106. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream.len +0 -0
  107. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.len +0 -0
  108. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.values.at +0 -0
  109. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i +0 -0
  110. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i.len +0 -0
  111. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
  112. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
  113. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
  114. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.len +0 -0
  115. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
  116. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
  117. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i.len +0 -0
  118. package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
  119. package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
  120. package/android/build/outputs/logs/manifest-merger-debug-report.txt +25 -0
  121. package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  122. package/android/build/tmp/kotlin-classes/debug/META-INF/react-native-testernest_debug.kotlin_module +0 -0
  123. package/android/build/tmp/kotlin-classes/debug/com/testernest/reactnative/TesternestModule$Companion.class +0 -0
  124. package/android/build/tmp/kotlin-classes/debug/com/testernest/reactnative/TesternestModule$logClaimResultAsync$poll$1.class +0 -0
  125. package/android/build/tmp/kotlin-classes/debug/com/testernest/reactnative/TesternestModule$redactTokens$1.class +0 -0
  126. package/android/build/tmp/kotlin-classes/debug/com/testernest/reactnative/TesternestModule$redactTokens$2.class +0 -0
  127. package/android/build/tmp/kotlin-classes/debug/com/testernest/reactnative/TesternestModule.class +0 -0
  128. package/android/build/tmp/kotlin-classes/debug/com/testernest/reactnative/TesternestPackage.class +0 -0
  129. package/android/build.gradle +34 -0
  130. package/android/consumer-rules.pro +1 -0
  131. package/android/gradle.properties +3 -0
  132. package/android/src/main/AndroidManifest.xml +2 -0
  133. package/android/src/main/java/com/testernest/reactnative/TesternestModule.kt +221 -0
  134. package/android/src/main/java/com/testernest/reactnative/TesternestPackage.kt +16 -0
  135. package/package.json +30 -0
  136. package/react-native.config.js +9 -0
  137. package/src/index.ts +2 -0
  138. package/src/native.ts +69 -0
  139. package/src/testernestConnectPrompt.tsx +173 -0
package/src/native.ts ADDED
@@ -0,0 +1,69 @@
1
+ import { NativeModules, Platform } from 'react-native';
2
+
3
+ export type InitOptions = {
4
+ publicKey: string;
5
+ baseUrl?: string;
6
+ enableLogs?: boolean;
7
+ };
8
+
9
+ export type DebugSnapshot = Record<string, any>;
10
+
11
+ type NativeTesternest = {
12
+ init(options: InitOptions): Promise<void>;
13
+ track(name: string, properties?: Record<string, any> | null): void;
14
+ flush(): Promise<void>;
15
+ setCurrentScreen(screen: string | null): void;
16
+ connectTester(code6: string): Promise<void>;
17
+ disconnectTester(): Promise<void>;
18
+ getDebugSnapshot(): Promise<DebugSnapshot>;
19
+ isConnected(): Promise<boolean>;
20
+ };
21
+
22
+ const LINKING_ERROR =
23
+ `The package '@testernest/react-native' doesn't seem to be linked. Make sure: \n\n` +
24
+ Platform.select({
25
+ ios: "- You have run 'pod install'\n",
26
+ default: '- You have rebuilt the app after installing the package\n',
27
+ }) +
28
+ '- You are not using Expo Go\n';
29
+
30
+ const Testernest = NativeModules.Testernest as NativeTesternest | undefined;
31
+
32
+ function getModule(): NativeTesternest {
33
+ if (!Testernest) {
34
+ throw new Error(LINKING_ERROR);
35
+ }
36
+ return Testernest;
37
+ }
38
+
39
+ export function init(options: InitOptions): Promise<void> {
40
+ return getModule().init(options);
41
+ }
42
+
43
+ export function track(name: string, properties?: Record<string, any>): void {
44
+ getModule().track(name, properties ?? null);
45
+ }
46
+
47
+ export function flush(): Promise<void> {
48
+ return getModule().flush();
49
+ }
50
+
51
+ export function setCurrentScreen(screen: string | null): void {
52
+ getModule().setCurrentScreen(screen);
53
+ }
54
+
55
+ export function connectTester(code6: string): Promise<void> {
56
+ return getModule().connectTester(code6);
57
+ }
58
+
59
+ export function disconnectTester(): Promise<void> {
60
+ return getModule().disconnectTester();
61
+ }
62
+
63
+ export function getDebugSnapshot(): Promise<DebugSnapshot> {
64
+ return getModule().getDebugSnapshot();
65
+ }
66
+
67
+ export function isConnected(): Promise<boolean> {
68
+ return getModule().isConnected();
69
+ }
@@ -0,0 +1,173 @@
1
+ import React, { useEffect, useMemo, useRef, useState } from 'react';
2
+ import { Modal, Text, TextInput, TouchableOpacity, View } from 'react-native';
3
+ import { connectTester, init, isConnected } from './native';
4
+
5
+ export type TesternestConnectPromptProps = {
6
+ publicKey: string;
7
+ baseUrl?: string;
8
+ enableLogs?: boolean;
9
+ title?: string;
10
+ subtitle?: string;
11
+ appName?: string;
12
+ showSkipButton?: boolean;
13
+ autoInit?: boolean;
14
+ forceVisibleToken?: number;
15
+ };
16
+
17
+ const styles = {
18
+ backdrop: { flex: 1, backgroundColor: 'rgba(0,0,0,0.4)', justifyContent: 'center', padding: 24 },
19
+ card: { backgroundColor: '#fff7ec', borderRadius: 16, padding: 20 },
20
+ title: { fontSize: 20, fontWeight: '700', color: '#1f1b16' },
21
+ subtitle: { marginTop: 8, fontSize: 14, color: '#4b3f35' },
22
+ appName: { marginTop: 6, fontSize: 14, fontWeight: '600', color: '#4b3f35' },
23
+ input: {
24
+ marginTop: 16,
25
+ borderWidth: 1,
26
+ borderColor: '#d4c2ae',
27
+ borderRadius: 10,
28
+ padding: 10,
29
+ backgroundColor: '#fff',
30
+ },
31
+ error: { marginTop: 8, color: '#b00020', fontSize: 13 },
32
+ buttonRow: { flexDirection: 'row', marginTop: 16 },
33
+ skipButton: { flex: 1, marginRight: 8, padding: 12, borderRadius: 10, backgroundColor: '#efe0d1' },
34
+ skipText: { textAlign: 'center', color: '#4b3f35', fontWeight: '600' },
35
+ connectButton: { flex: 1, marginLeft: 8, padding: 12, borderRadius: 10, backgroundColor: '#1f6d5f' },
36
+ connectText: { textAlign: 'center', color: '#fff', fontWeight: '700' },
37
+ };
38
+
39
+ export function TesternestConnectPrompt({
40
+ publicKey,
41
+ baseUrl,
42
+ enableLogs = false,
43
+ title = 'Connect Tester',
44
+ subtitle = 'Enter your 6-digit code to connect.',
45
+ appName,
46
+ showSkipButton = true,
47
+ autoInit = true,
48
+ forceVisibleToken,
49
+ }: TesternestConnectPromptProps) {
50
+ const [visible, setVisible] = useState(false);
51
+ const [code, setCode] = useState('');
52
+ const [error, setError] = useState<string | null>(null);
53
+ const [connecting, setConnecting] = useState(false);
54
+ const dismissedRef = useRef(false);
55
+
56
+ const isSixDigit = useMemo(() => /^\d{6}$/.test(code.trim()), [code]);
57
+
58
+ useEffect(() => {
59
+ dismissedRef.current = false;
60
+ }, [publicKey, baseUrl]);
61
+
62
+ useEffect(() => {
63
+ let active = true;
64
+ if (!autoInit || !publicKey) {
65
+ return undefined;
66
+ }
67
+ (async () => {
68
+ try {
69
+ await init({ publicKey, baseUrl, enableLogs });
70
+ const connected = await isConnected();
71
+ if (active && !connected && !dismissedRef.current) {
72
+ setVisible(true);
73
+ }
74
+ } catch (e) {
75
+ if (active) {
76
+ setError(e instanceof Error ? e.message : String(e));
77
+ setVisible(true);
78
+ }
79
+ }
80
+ })();
81
+ return () => {
82
+ active = false;
83
+ };
84
+ }, [publicKey, baseUrl, enableLogs, autoInit]);
85
+
86
+ useEffect(() => {
87
+ if (!forceVisibleToken || forceVisibleToken <= 0) {
88
+ return;
89
+ }
90
+ dismissedRef.current = false;
91
+ setError(null);
92
+ setCode('');
93
+ setVisible(true);
94
+ }, [forceVisibleToken]);
95
+
96
+ const handleSkip = () => {
97
+ dismissedRef.current = true;
98
+ setVisible(false);
99
+ };
100
+
101
+ const pollConnected = () => {
102
+ const startedAt = Date.now();
103
+ const interval = setInterval(async () => {
104
+ try {
105
+ const connected = await isConnected();
106
+ if (connected) {
107
+ clearInterval(interval);
108
+ dismissedRef.current = true;
109
+ setVisible(false);
110
+ setConnecting(false);
111
+ return;
112
+ }
113
+ } catch (e) {
114
+ clearInterval(interval);
115
+ setConnecting(false);
116
+ setError(e instanceof Error ? e.message : String(e));
117
+ return;
118
+ }
119
+ if (Date.now() - startedAt > 8000) {
120
+ clearInterval(interval);
121
+ setConnecting(false);
122
+ setError('Unable to connect. Check the code and try again.');
123
+ }
124
+ }, 400);
125
+ };
126
+
127
+ const handleConnect = async () => {
128
+ if (connecting) return;
129
+ if (!isSixDigit) {
130
+ setError('Enter a 6-digit code.');
131
+ return;
132
+ }
133
+ setError(null);
134
+ setConnecting(true);
135
+ try {
136
+ await connectTester(code.trim());
137
+ pollConnected();
138
+ } catch (e) {
139
+ setConnecting(false);
140
+ setError(e instanceof Error ? e.message : String(e));
141
+ }
142
+ };
143
+
144
+ return (
145
+ <Modal transparent visible={visible} animationType="fade" onRequestClose={handleSkip}>
146
+ <View style={styles.backdrop}>
147
+ <View style={styles.card}>
148
+ <Text style={styles.title}>{title}</Text>
149
+ {subtitle ? <Text style={styles.subtitle}>{subtitle}</Text> : null}
150
+ {appName ? <Text style={styles.appName}>{appName}</Text> : null}
151
+ <TextInput
152
+ style={styles.input}
153
+ value={code}
154
+ onChangeText={setCode}
155
+ placeholder="123456"
156
+ keyboardType="number-pad"
157
+ />
158
+ {error ? <Text style={styles.error}>{error}</Text> : null}
159
+ <View style={styles.buttonRow}>
160
+ {showSkipButton ? (
161
+ <TouchableOpacity style={styles.skipButton} onPress={handleSkip}>
162
+ <Text style={styles.skipText}>Not now</Text>
163
+ </TouchableOpacity>
164
+ ) : null}
165
+ <TouchableOpacity style={styles.connectButton} onPress={handleConnect}>
166
+ <Text style={styles.connectText}>{connecting ? 'Connecting...' : 'Connect'}</Text>
167
+ </TouchableOpacity>
168
+ </View>
169
+ </View>
170
+ </View>
171
+ </Modal>
172
+ );
173
+ }