react-native-otp-auto-verify 0.1.9 → 0.2.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/README.md +467 -452
- package/android/build.gradle +1 -0
- package/android/src/main/java/com/otpautoverify/OtpAutoVerifyModule.kt +14 -12
- package/lib/module/NativeOtpAutoVerify.ts +29 -5
- package/lib/module/index.js +77 -33
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/NativeOtpAutoVerify.d.ts +3 -3
- package/lib/typescript/src/NativeOtpAutoVerify.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +7 -7
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +183 -175
- package/src/NativeOtpAutoVerify.ts +29 -5
- package/src/index.tsx +89 -40
package/package.json
CHANGED
|
@@ -1,175 +1,183 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "react-native-otp-auto-verify",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "react-native-otp-auto-verify is a React Native library for automatic OTP verification on Android and iOS. It uses the Google SMS Retriever API to detect OTP codes automatically without requiring the READ_SMS permission. The library is fully Play Store compliant, improves user login experience, supports TurboModule, and works with the React Native New Architecture",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"react-native",
|
|
7
|
-
"react-native-library",
|
|
8
|
-
"react-native-otp",
|
|
9
|
-
"react-native-otp-auto-verify",
|
|
10
|
-
"react-native-sms",
|
|
11
|
-
"otp",
|
|
12
|
-
"otp-verification",
|
|
13
|
-
"otp-auto-verify",
|
|
14
|
-
"otp-auto-read",
|
|
15
|
-
"sms",
|
|
16
|
-
"sms-retriever",
|
|
17
|
-
"google-sms-retriever",
|
|
18
|
-
"android-otp",
|
|
19
|
-
"ios-otp",
|
|
20
|
-
"otp-without-permission",
|
|
21
|
-
"play-store-compliant",
|
|
22
|
-
"react-native-new-architecture",
|
|
23
|
-
"turbo-module"
|
|
24
|
-
],
|
|
25
|
-
"homepage": "https://github.com/kailas-rathod/react-native-otp-auto-verify?tab=readme-ov-file",
|
|
26
|
-
"bugs": {
|
|
27
|
-
"url": "https://github.com/kailas-rathod/react-native-otp-auto-verify/issues"
|
|
28
|
-
},
|
|
29
|
-
"repository": {
|
|
30
|
-
"type": "git",
|
|
31
|
-
"url": "git+https://github.com/kailas-rathod/react-native-otp-auto-verify.git"
|
|
32
|
-
},
|
|
33
|
-
"license": "MIT",
|
|
34
|
-
"author": "Kailas Rathod (https://github.com/kailas-rathod)",
|
|
35
|
-
"type": "commonjs",
|
|
36
|
-
"main": "./lib/module/index.js",
|
|
37
|
-
"types": "./lib/typescript/src/index.d.ts",
|
|
38
|
-
"files": [
|
|
39
|
-
"src",
|
|
40
|
-
"lib",
|
|
41
|
-
"android",
|
|
42
|
-
"ios",
|
|
43
|
-
"*.podspec",
|
|
44
|
-
"react-native.config.js",
|
|
45
|
-
"!ios/build",
|
|
46
|
-
"!android/build",
|
|
47
|
-
"!android/gradle",
|
|
48
|
-
"!android/gradlew",
|
|
49
|
-
"!android/gradlew.bat",
|
|
50
|
-
"!android/local.properties",
|
|
51
|
-
"!**/__tests__",
|
|
52
|
-
"!**/__fixtures__",
|
|
53
|
-
"!**/__mocks__",
|
|
54
|
-
"!**/.*"
|
|
55
|
-
],
|
|
56
|
-
"scripts": {
|
|
57
|
-
"prepare": "bob build",
|
|
58
|
-
"typecheck": "tsc",
|
|
59
|
-
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
60
|
-
"test": "jest",
|
|
61
|
-
"check": "yarn typecheck && yarn lint && yarn test",
|
|
62
|
-
"release": "release-it --only-version"
|
|
63
|
-
},
|
|
64
|
-
"devDependencies": {
|
|
65
|
-
"@
|
|
66
|
-
"@
|
|
67
|
-
"@eslint/eslintrc": "^3.3.4",
|
|
68
|
-
"@eslint/js": "
|
|
69
|
-
"@react-native/
|
|
70
|
-
"@react-native-community/eslint-
|
|
71
|
-
"@
|
|
72
|
-
"@
|
|
73
|
-
"@
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"eslint
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
"
|
|
89
|
-
"react
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
"
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
"
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
"
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
},
|
|
144
|
-
"
|
|
145
|
-
"
|
|
146
|
-
"
|
|
147
|
-
"
|
|
148
|
-
"
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
"
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "react-native-otp-auto-verify",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "react-native-otp-auto-verify is a React Native library for automatic OTP verification on Android and iOS. It uses the Google SMS Retriever API to detect OTP codes automatically without requiring the READ_SMS permission. The library is fully Play Store compliant, improves user login experience, supports TurboModule, and works with the React Native New Architecture",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"react-native",
|
|
7
|
+
"react-native-library",
|
|
8
|
+
"react-native-otp",
|
|
9
|
+
"react-native-otp-auto-verify",
|
|
10
|
+
"react-native-sms",
|
|
11
|
+
"otp",
|
|
12
|
+
"otp-verification",
|
|
13
|
+
"otp-auto-verify",
|
|
14
|
+
"otp-auto-read",
|
|
15
|
+
"sms",
|
|
16
|
+
"sms-retriever",
|
|
17
|
+
"google-sms-retriever",
|
|
18
|
+
"android-otp",
|
|
19
|
+
"ios-otp",
|
|
20
|
+
"otp-without-permission",
|
|
21
|
+
"play-store-compliant",
|
|
22
|
+
"react-native-new-architecture",
|
|
23
|
+
"turbo-module"
|
|
24
|
+
],
|
|
25
|
+
"homepage": "https://github.com/kailas-rathod/react-native-otp-auto-verify?tab=readme-ov-file",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/kailas-rathod/react-native-otp-auto-verify/issues"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/kailas-rathod/react-native-otp-auto-verify.git"
|
|
32
|
+
},
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"author": "Kailas Rathod (https://github.com/kailas-rathod)",
|
|
35
|
+
"type": "commonjs",
|
|
36
|
+
"main": "./lib/module/index.js",
|
|
37
|
+
"types": "./lib/typescript/src/index.d.ts",
|
|
38
|
+
"files": [
|
|
39
|
+
"src",
|
|
40
|
+
"lib",
|
|
41
|
+
"android",
|
|
42
|
+
"ios",
|
|
43
|
+
"*.podspec",
|
|
44
|
+
"react-native.config.js",
|
|
45
|
+
"!ios/build",
|
|
46
|
+
"!android/build",
|
|
47
|
+
"!android/gradle",
|
|
48
|
+
"!android/gradlew",
|
|
49
|
+
"!android/gradlew.bat",
|
|
50
|
+
"!android/local.properties",
|
|
51
|
+
"!**/__tests__",
|
|
52
|
+
"!**/__fixtures__",
|
|
53
|
+
"!**/__mocks__",
|
|
54
|
+
"!**/.*"
|
|
55
|
+
],
|
|
56
|
+
"scripts": {
|
|
57
|
+
"prepare": "bob build",
|
|
58
|
+
"typecheck": "tsc",
|
|
59
|
+
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
60
|
+
"test": "jest",
|
|
61
|
+
"check": "yarn typecheck && yarn lint && yarn test",
|
|
62
|
+
"release": "release-it --only-version"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@babel/eslint-parser": "^7.28.0",
|
|
66
|
+
"@commitlint/config-conventional": "^19.8.1",
|
|
67
|
+
"@eslint/eslintrc": "^3.3.4",
|
|
68
|
+
"@eslint/js": "9.35.0",
|
|
69
|
+
"@react-native-community/eslint-config": "^3.2.0",
|
|
70
|
+
"@react-native-community/eslint-plugin": "^1.1.0",
|
|
71
|
+
"@typescript-eslint/eslint-plugin": "^8.15.0",
|
|
72
|
+
"@typescript-eslint/parser": "^8.15.0",
|
|
73
|
+
"@react-native/babel-preset": "0.83.3",
|
|
74
|
+
"@release-it/conventional-changelog": "^10.0.6",
|
|
75
|
+
"@types/jest": "^29.5.14",
|
|
76
|
+
"@types/react": "^19.2.0",
|
|
77
|
+
"commitlint": "^19.8.1",
|
|
78
|
+
"eslint": "9.35.0",
|
|
79
|
+
"eslint-config-prettier": "^9.1.0",
|
|
80
|
+
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
81
|
+
"eslint-plugin-ft-flow": "^2.0.1",
|
|
82
|
+
"eslint-plugin-jest": "^28.11.0",
|
|
83
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
84
|
+
"eslint-plugin-react": "^7.37.0",
|
|
85
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
86
|
+
"eslint-plugin-react-native": "^5.0.0",
|
|
87
|
+
"jest": "^30.3.0",
|
|
88
|
+
"prettier": "^3.4.2",
|
|
89
|
+
"react": "19.2.5",
|
|
90
|
+
"react-native": "0.83.3",
|
|
91
|
+
"react-native-builder-bob": "0.39.1",
|
|
92
|
+
"release-it": "^19.0.4",
|
|
93
|
+
"typescript": "^5.9.3"
|
|
94
|
+
},
|
|
95
|
+
"peerDependencies": {
|
|
96
|
+
"react": "*",
|
|
97
|
+
"react-native": "*"
|
|
98
|
+
},
|
|
99
|
+
"publishConfig": {
|
|
100
|
+
"registry": "https://registry.npmjs.org/"
|
|
101
|
+
},
|
|
102
|
+
"eslintConfig": {
|
|
103
|
+
"root": true,
|
|
104
|
+
"extends": [
|
|
105
|
+
"@react-native-community",
|
|
106
|
+
"prettier"
|
|
107
|
+
],
|
|
108
|
+
"rules": {
|
|
109
|
+
"prettier/prettier": [
|
|
110
|
+
"error",
|
|
111
|
+
{
|
|
112
|
+
"quoteProps": "consistent",
|
|
113
|
+
"singleQuote": true,
|
|
114
|
+
"tabWidth": 2,
|
|
115
|
+
"trailingComma": "es5",
|
|
116
|
+
"useTabs": false
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
"eslintIgnore": [
|
|
122
|
+
"node_modules/",
|
|
123
|
+
"lib/"
|
|
124
|
+
],
|
|
125
|
+
"packageManager": "yarn@4.11.0",
|
|
126
|
+
"react-native-builder-bob": {
|
|
127
|
+
"source": "src",
|
|
128
|
+
"output": "lib",
|
|
129
|
+
"targets": [
|
|
130
|
+
[
|
|
131
|
+
"module",
|
|
132
|
+
{
|
|
133
|
+
"esm": true
|
|
134
|
+
}
|
|
135
|
+
],
|
|
136
|
+
[
|
|
137
|
+
"typescript",
|
|
138
|
+
{
|
|
139
|
+
"project": "tsconfig.build.json"
|
|
140
|
+
}
|
|
141
|
+
]
|
|
142
|
+
]
|
|
143
|
+
},
|
|
144
|
+
"codegenConfig": {
|
|
145
|
+
"name": "OtpAutoVerifySpec",
|
|
146
|
+
"type": "modules",
|
|
147
|
+
"jsSrcsDir": "src",
|
|
148
|
+
"android": {
|
|
149
|
+
"javaPackageName": "com.otpautoverify"
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
"prettier": {
|
|
153
|
+
"quoteProps": "consistent",
|
|
154
|
+
"singleQuote": true,
|
|
155
|
+
"tabWidth": 2,
|
|
156
|
+
"trailingComma": "es5",
|
|
157
|
+
"useTabs": false
|
|
158
|
+
},
|
|
159
|
+
"commitlint": {
|
|
160
|
+
"extends": [
|
|
161
|
+
"@commitlint/config-conventional"
|
|
162
|
+
]
|
|
163
|
+
},
|
|
164
|
+
"release-it": {
|
|
165
|
+
"git": {
|
|
166
|
+
"commitMessage": "chore: release ${version}",
|
|
167
|
+
"tagName": "v${version}"
|
|
168
|
+
},
|
|
169
|
+
"npm": {
|
|
170
|
+
"publish": true
|
|
171
|
+
},
|
|
172
|
+
"github": {
|
|
173
|
+
"release": true
|
|
174
|
+
},
|
|
175
|
+
"plugins": {
|
|
176
|
+
"@release-it/conventional-changelog": {
|
|
177
|
+
"preset": {
|
|
178
|
+
"name": "angular"
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
NativeModules,
|
|
3
|
+
TurboModuleRegistry,
|
|
4
|
+
type TurboModule,
|
|
5
|
+
} from 'react-native';
|
|
2
6
|
|
|
3
|
-
export interface
|
|
7
|
+
export interface Spec extends TurboModule {
|
|
4
8
|
getConstants(): { OTP_RECEIVED_EVENT: string };
|
|
5
9
|
getHash(): Promise<ReadonlyArray<string>>;
|
|
6
10
|
startSmsRetriever(): Promise<boolean>;
|
|
@@ -8,6 +12,26 @@ export interface OtpAutoVerifySpec extends TurboModule {
|
|
|
8
12
|
removeListeners(count: number): void;
|
|
9
13
|
}
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
);
|
|
15
|
+
function loadNativeModule(): Spec {
|
|
16
|
+
try {
|
|
17
|
+
return TurboModuleRegistry.getEnforcing<Spec>('OtpAutoVerify');
|
|
18
|
+
} catch {
|
|
19
|
+
const legacy = NativeModules.OtpAutoVerify;
|
|
20
|
+
if (!legacy) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
'OtpAutoVerify native module is not available. Ensure the library is properly linked.'
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
return legacy as Spec;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let cachedModule: Spec | null = null;
|
|
30
|
+
|
|
31
|
+
/** Resolves the native module on first use so importing this package does not throw before APIs run. */
|
|
32
|
+
export function getOtpNativeModule(): Spec {
|
|
33
|
+
if (cachedModule === null) {
|
|
34
|
+
cachedModule = loadNativeModule();
|
|
35
|
+
}
|
|
36
|
+
return cachedModule;
|
|
37
|
+
}
|
package/src/index.tsx
CHANGED
|
@@ -1,30 +1,45 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { NativeEventEmitter,
|
|
3
|
-
import
|
|
2
|
+
import { NativeEventEmitter, Platform } from 'react-native';
|
|
3
|
+
import { getOtpNativeModule } from './NativeOtpAutoVerify';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
Platform.OS === 'android' && OtpAutoVerify
|
|
8
|
-
? new NativeEventEmitter(OtpAutoVerify)
|
|
9
|
-
: null;
|
|
5
|
+
/** Must match native `OTP_RECEIVED_EVENT` (Android/iOS). */
|
|
6
|
+
export const OTP_RECEIVED_EVENT = 'otpReceived';
|
|
10
7
|
|
|
11
|
-
const
|
|
12
|
-
(NativeOtpAutoVerify.getConstants?.()?.OTP_RECEIVED_EVENT as string) ??
|
|
13
|
-
'otpReceived';
|
|
14
|
-
|
|
15
|
-
const TIMEOUT_MESSAGE = 'Timeout Error.';
|
|
8
|
+
export const TIMEOUT_MESSAGE = 'Timeout Error.';
|
|
16
9
|
const DEFAULT_DIGITS = 6;
|
|
17
10
|
|
|
18
|
-
|
|
11
|
+
const MIN_OTP_DIGITS = 4;
|
|
12
|
+
const MAX_OTP_DIGITS = 8;
|
|
19
13
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
export type OtpDigits = 4 | 5 | 6 | 7 | 8;
|
|
15
|
+
|
|
16
|
+
let androidEventEmitter: NativeEventEmitter | null | undefined;
|
|
17
|
+
|
|
18
|
+
function getAndroidEventEmitter(): NativeEventEmitter | null {
|
|
19
|
+
if (Platform.OS !== 'android') return null;
|
|
20
|
+
if (androidEventEmitter !== undefined) {
|
|
21
|
+
return androidEventEmitter;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
androidEventEmitter = new NativeEventEmitter(getOtpNativeModule());
|
|
25
|
+
return androidEventEmitter;
|
|
26
|
+
} catch {
|
|
27
|
+
androidEventEmitter = null;
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getOtpRegex(digits: number): RegExp {
|
|
33
|
+
const d =
|
|
34
|
+
digits >= MIN_OTP_DIGITS && digits <= MAX_OTP_DIGITS
|
|
35
|
+
? digits
|
|
36
|
+
: DEFAULT_DIGITS;
|
|
37
|
+
// Prefer non-`\b` boundaries so OTPs still match after ":" and similar (e.g. "OTP:123456").
|
|
38
|
+
return new RegExp(`(^|[^0-9])(\\d{${d}})(?!\\d)`);
|
|
39
|
+
}
|
|
25
40
|
|
|
26
41
|
export interface UseOtpVerificationOptions {
|
|
27
|
-
/** Extract OTP with this many digits (4
|
|
42
|
+
/** Extract OTP with this many digits (4–8). OTP is set when SMS is received. */
|
|
28
43
|
numberOfDigits?: OtpDigits;
|
|
29
44
|
}
|
|
30
45
|
|
|
@@ -50,8 +65,7 @@ export interface OtpListenerSubscription {
|
|
|
50
65
|
}
|
|
51
66
|
|
|
52
67
|
/**
|
|
53
|
-
* Extracts a numeric OTP of 4
|
|
54
|
-
* Only these lengths are supported (numberOfDigits: 4 | 5 | 6).
|
|
68
|
+
* Extracts a numeric OTP of 4–8 digits from SMS text.
|
|
55
69
|
*/
|
|
56
70
|
export function extractOtp(
|
|
57
71
|
sms: string,
|
|
@@ -60,28 +74,39 @@ export function extractOtp(
|
|
|
60
74
|
if (!sms || typeof sms !== 'string') return null;
|
|
61
75
|
const trimmed = sms.trim();
|
|
62
76
|
if (!trimmed) return null;
|
|
63
|
-
const
|
|
64
|
-
|
|
77
|
+
const regex = getOtpRegex(numberOfDigits);
|
|
78
|
+
const match = trimmed.match(regex);
|
|
79
|
+
return match ? match[2]! : null;
|
|
65
80
|
}
|
|
66
81
|
|
|
67
82
|
/** Returns app hash strings for the current app. Android only; iOS returns []. */
|
|
68
83
|
export async function getHash(): Promise<string[]> {
|
|
69
84
|
if (Platform.OS !== 'android') return [];
|
|
70
|
-
const arr = await
|
|
85
|
+
const arr = await getOtpNativeModule().getHash();
|
|
71
86
|
return Array.from(arr);
|
|
72
87
|
}
|
|
73
88
|
|
|
74
|
-
/**
|
|
89
|
+
/** No-op subscription for platforms where SMS Retriever is not supported (e.g. iOS). */
|
|
90
|
+
const NOOP_SUBSCRIPTION: OtpListenerSubscription = {
|
|
91
|
+
remove: () => {},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/** Starts SMS Retriever and subscribes to OTP events. Returns subscription with remove(). On iOS, returns no-op (call remove() safely). */
|
|
75
95
|
export async function activateOtpListener(
|
|
76
96
|
handler: (sms: string, extractedOtp?: string | null) => void,
|
|
77
97
|
options?: { numberOfDigits?: OtpDigits }
|
|
78
98
|
): Promise<OtpListenerSubscription> {
|
|
79
|
-
if (Platform.OS !== 'android'
|
|
80
|
-
|
|
99
|
+
if (Platform.OS !== 'android') {
|
|
100
|
+
return NOOP_SUBSCRIPTION;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const emitter = getAndroidEventEmitter();
|
|
104
|
+
if (!emitter) {
|
|
105
|
+
return NOOP_SUBSCRIPTION;
|
|
81
106
|
}
|
|
82
107
|
|
|
83
108
|
const numberOfDigits = options?.numberOfDigits ?? DEFAULT_DIGITS;
|
|
84
|
-
const subscription =
|
|
109
|
+
const subscription = emitter.addListener(
|
|
85
110
|
OTP_RECEIVED_EVENT,
|
|
86
111
|
(...args: unknown[]) => {
|
|
87
112
|
const smsText = String(args[0] ?? '');
|
|
@@ -89,11 +114,21 @@ export async function activateOtpListener(
|
|
|
89
114
|
}
|
|
90
115
|
);
|
|
91
116
|
|
|
92
|
-
|
|
117
|
+
try {
|
|
118
|
+
await getOtpNativeModule().startSmsRetriever();
|
|
119
|
+
} catch (err) {
|
|
120
|
+
subscription.remove();
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
|
|
93
124
|
return {
|
|
94
125
|
remove: () => {
|
|
95
126
|
subscription.remove();
|
|
96
|
-
|
|
127
|
+
try {
|
|
128
|
+
getOtpNativeModule().removeListeners(0);
|
|
129
|
+
} catch {
|
|
130
|
+
// Native may be unavailable during teardown
|
|
131
|
+
}
|
|
97
132
|
},
|
|
98
133
|
};
|
|
99
134
|
}
|
|
@@ -103,8 +138,11 @@ export async function activateOtpListener(
|
|
|
103
138
|
* The native module ignores the count parameter and always unregisters the SMS receiver.
|
|
104
139
|
*/
|
|
105
140
|
export function removeListener(): void {
|
|
106
|
-
if (Platform.OS
|
|
107
|
-
|
|
141
|
+
if (Platform.OS !== 'android') return;
|
|
142
|
+
try {
|
|
143
|
+
getOtpNativeModule().removeListeners(0);
|
|
144
|
+
} catch {
|
|
145
|
+
// Native not linked or already torn down
|
|
108
146
|
}
|
|
109
147
|
}
|
|
110
148
|
|
|
@@ -119,26 +157,37 @@ export function useOtpVerification(
|
|
|
119
157
|
const [timeoutError, setTimeoutError] = React.useState(false);
|
|
120
158
|
const [error, setError] = React.useState<Error | null>(null);
|
|
121
159
|
const subscriptionRef = React.useRef<OtpListenerSubscription | null>(null);
|
|
160
|
+
const isStartingRef = React.useRef(false);
|
|
122
161
|
|
|
123
162
|
const stopListening = React.useCallback(() => {
|
|
124
163
|
subscriptionRef.current?.remove();
|
|
125
164
|
subscriptionRef.current = null;
|
|
165
|
+
isStartingRef.current = false;
|
|
126
166
|
removeListener();
|
|
127
167
|
}, []);
|
|
128
168
|
|
|
129
169
|
const startListening = React.useCallback(async () => {
|
|
130
170
|
if (Platform.OS !== 'android') return;
|
|
171
|
+
if (isStartingRef.current) return;
|
|
172
|
+
|
|
173
|
+
isStartingRef.current = true;
|
|
174
|
+
subscriptionRef.current?.remove();
|
|
175
|
+
subscriptionRef.current = null;
|
|
131
176
|
setOtp(null);
|
|
132
177
|
setSms(null);
|
|
133
178
|
setTimeoutError(false);
|
|
134
179
|
setError(null);
|
|
180
|
+
|
|
135
181
|
try {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
182
|
+
try {
|
|
183
|
+
const hashes = await getHash();
|
|
184
|
+
setHashCode(hashes[0] ?? '');
|
|
185
|
+
} catch (err) {
|
|
186
|
+
const wrapped = err instanceof Error ? err : new Error(String(err));
|
|
187
|
+
setError(wrapped);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
142
191
|
const sub = await activateOtpListener(
|
|
143
192
|
(smsText, extractedOtp) => {
|
|
144
193
|
setSms(smsText);
|
|
@@ -156,6 +205,8 @@ export function useOtpVerification(
|
|
|
156
205
|
const wrapped = new Error('Failed to start OTP listener', { cause: err });
|
|
157
206
|
setError(wrapped);
|
|
158
207
|
throw wrapped;
|
|
208
|
+
} finally {
|
|
209
|
+
isStartingRef.current = false;
|
|
159
210
|
}
|
|
160
211
|
}, [numberOfDigits]);
|
|
161
212
|
|
|
@@ -174,5 +225,3 @@ export function useOtpVerification(
|
|
|
174
225
|
[hashCode, otp, sms, timeoutError, error, startListening, stopListening]
|
|
175
226
|
);
|
|
176
227
|
}
|
|
177
|
-
|
|
178
|
-
export { OTP_RECEIVED_EVENT };
|