@smileid/usesmileid 12.0.0 → 12.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 +4 -0
- package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.d.ts +11 -0
- package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.d.ts.map +1 -0
- package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.js +17 -0
- package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.js.map +1 -0
- package/dist/core/exception/capture_validation_exceptions.d.ts +1 -0
- package/dist/core/exception/capture_validation_exceptions.d.ts.map +1 -1
- package/dist/core/exception/capture_validation_exceptions.js +1 -0
- package/dist/core/exception/capture_validation_exceptions.js.map +1 -1
- package/dist/core/exception/index.d.ts +1 -0
- package/dist/core/exception/index.d.ts.map +1 -1
- package/dist/core/exception/index.js +1 -0
- package/dist/core/exception/index.js.map +1 -1
- package/dist/core/exception/use_smile_id_error_code.d.ts +1 -0
- package/dist/core/exception/use_smile_id_error_code.d.ts.map +1 -1
- package/dist/core/exception/use_smile_id_error_code.js +1 -0
- package/dist/core/exception/use_smile_id_error_code.js.map +1 -1
- package/dist/core/models/config.d.ts +19 -0
- package/dist/core/models/config.d.ts.map +1 -1
- package/dist/core/models/document_type.d.ts +1 -1
- package/dist/core/models/document_type.d.ts.map +1 -1
- package/dist/core/models/document_type.js +2 -2
- package/dist/core/models/document_type.js.map +1 -1
- package/dist/core/models/global_privacy_control.d.ts +36 -0
- package/dist/core/models/global_privacy_control.d.ts.map +1 -0
- package/dist/core/models/global_privacy_control.js +39 -0
- package/dist/core/models/global_privacy_control.js.map +1 -0
- package/dist/core/models/network_config.d.ts +2 -2
- package/dist/core/models/network_config.js +2 -2
- package/dist/data/api/interceptors/smile_auth_interceptor.d.ts.map +1 -1
- package/dist/data/api/interceptors/smile_auth_interceptor.js +3 -17
- package/dist/data/api/interceptors/smile_auth_interceptor.js.map +1 -1
- package/dist/data/api/smile_id_api.d.ts +1 -1
- package/dist/data/api/smile_id_api.d.ts.map +1 -1
- package/dist/data/dsl/builder/retry_config_builder.d.ts +4 -4
- package/dist/data/dsl/builder/retry_config_builder.d.ts.map +1 -1
- package/dist/data/dsl/builder/retry_config_builder.js +6 -6
- package/dist/data/dsl/builder/retry_config_builder.js.map +1 -1
- package/dist/data/smile_id_urls.d.ts +1 -1
- package/dist/data/smile_id_urls.js +1 -1
- package/dist/data/smile_id_urls.js.map +1 -1
- package/dist/data/util/retry_util.d.ts +1 -1
- package/dist/data/util/retry_util.d.ts.map +1 -1
- package/dist/data/util/retry_util.js +5 -0
- package/dist/data/util/retry_util.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/metadata/builder_configuration.d.ts +5 -0
- package/dist/metadata/builder_configuration.d.ts.map +1 -1
- package/dist/metadata/builder_configuration.js +1 -0
- package/dist/metadata/builder_configuration.js.map +1 -1
- package/dist/metadata/metadata_configuration_mappers.d.ts +2 -3
- package/dist/metadata/metadata_configuration_mappers.d.ts.map +1 -1
- package/dist/metadata/metadata_configuration_mappers.js +13 -6
- package/dist/metadata/metadata_configuration_mappers.js.map +1 -1
- package/dist/metadata/use_smile_id_metadata_factory.d.ts.map +1 -1
- package/dist/metadata/use_smile_id_metadata_factory.js +14 -5
- package/dist/metadata/use_smile_id_metadata_factory.js.map +1 -1
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts +5 -2
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts.map +1 -1
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js +8 -3
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js.map +1 -1
- package/dist/presentation/components/use_smile_id_instructions_hero.d.ts.map +1 -1
- package/dist/presentation/components/use_smile_id_instructions_hero.js +8 -3
- package/dist/presentation/components/use_smile_id_instructions_hero.js.map +1 -1
- package/dist/presentation/flow/dsl/builder/config_builder.d.ts +16 -0
- package/dist/presentation/flow/dsl/builder/config_builder.d.ts.map +1 -1
- package/dist/presentation/flow/dsl/builder/config_builder.js +18 -0
- package/dist/presentation/flow/dsl/builder/config_builder.js.map +1 -1
- package/dist/presentation/flow/dsl/builder/screens_builder.d.ts +3 -6
- package/dist/presentation/flow/dsl/builder/screens_builder.d.ts.map +1 -1
- package/dist/presentation/flow/dsl/builder/screens_builder.js +3 -6
- package/dist/presentation/flow/dsl/builder/screens_builder.js.map +1 -1
- package/dist/presentation/flow/flow_navigation_manager.d.ts +5 -1
- package/dist/presentation/flow/flow_navigation_manager.d.ts.map +1 -1
- package/dist/presentation/flow/flow_navigation_manager.js +7 -1
- package/dist/presentation/flow/flow_navigation_manager.js.map +1 -1
- package/dist/presentation/flow/model/instructions_defaults.d.ts.map +1 -1
- package/dist/presentation/flow/model/instructions_defaults.js +1 -1
- package/dist/presentation/flow/model/instructions_defaults.js.map +1 -1
- package/dist/presentation/flow/screen/orchestrated_capture_screen.d.ts.map +1 -1
- package/dist/presentation/flow/screen/orchestrated_capture_screen.js +13 -3
- package/dist/presentation/flow/screen/orchestrated_capture_screen.js.map +1 -1
- package/dist/presentation/flow/screen/orchestrated_consent_screen.d.ts.map +1 -1
- package/dist/presentation/flow/screen/orchestrated_consent_screen.js +3 -1
- package/dist/presentation/flow/screen/orchestrated_consent_screen.js.map +1 -1
- package/dist/presentation/flow/screen/orchestrated_preview_screen.d.ts.map +1 -1
- package/dist/presentation/flow/screen/orchestrated_preview_screen.js +0 -8
- package/dist/presentation/flow/screen/orchestrated_preview_screen.js.map +1 -1
- package/dist/presentation/flow/use_smile_id_flow.d.ts.map +1 -1
- package/dist/presentation/flow/use_smile_id_flow.js +10 -4
- package/dist/presentation/flow/use_smile_id_flow.js.map +1 -1
- package/dist/presentation/flow/validation/flow_validator.d.ts.map +1 -1
- package/dist/presentation/flow/validation/flow_validator.js +42 -20
- package/dist/presentation/flow/validation/flow_validator.js.map +1 -1
- package/dist/presentation/screen/helpers/capture_ui_state.d.ts +6 -6
- package/dist/presentation/screen/helpers/capture_ui_state.d.ts.map +1 -1
- package/dist/presentation/screen/helpers/capture_ui_state.js +19 -19
- package/dist/presentation/screen/helpers/capture_ui_state.js.map +1 -1
- package/dist/presentation/screen/helpers/ess_turn_haptics.d.ts +2 -2
- package/dist/presentation/screen/helpers/ess_turn_haptics.d.ts.map +1 -1
- package/dist/presentation/screen/helpers/ess_turn_haptics.js +6 -6
- package/dist/presentation/screen/helpers/ess_turn_haptics.js.map +1 -1
- package/dist/presentation/screen/helpers/scan_state_to_overlay_state.d.ts +5 -5
- package/dist/presentation/screen/helpers/scan_state_to_overlay_state.d.ts.map +1 -1
- package/dist/presentation/screen/helpers/scan_state_to_overlay_state.js +11 -11
- package/dist/presentation/screen/helpers/scan_state_to_overlay_state.js.map +1 -1
- package/dist/presentation/screen/helpers/use_capture_orientation_lock.d.ts +13 -3
- package/dist/presentation/screen/helpers/use_capture_orientation_lock.d.ts.map +1 -1
- package/dist/presentation/screen/helpers/use_capture_orientation_lock.js +48 -6
- package/dist/presentation/screen/helpers/use_capture_orientation_lock.js.map +1 -1
- package/dist/presentation/screen/use_smile_id_capture_screen.d.ts +4 -2
- package/dist/presentation/screen/use_smile_id_capture_screen.d.ts.map +1 -1
- package/dist/presentation/screen/use_smile_id_capture_screen.js +36 -15
- package/dist/presentation/screen/use_smile_id_capture_screen.js.map +1 -1
- package/dist/presentation/screen/use_smile_id_consent_screen.d.ts +7 -0
- package/dist/presentation/screen/use_smile_id_consent_screen.d.ts.map +1 -1
- package/dist/presentation/screen/use_smile_id_consent_screen.js +6 -5
- package/dist/presentation/screen/use_smile_id_consent_screen.js.map +1 -1
- package/dist/presentation/screen/use_smile_id_document_back_instructions.d.ts.map +1 -1
- package/dist/presentation/screen/use_smile_id_document_back_instructions.js +5 -13
- package/dist/presentation/screen/use_smile_id_document_back_instructions.js.map +1 -1
- package/dist/presentation/screen/use_smile_id_document_preview_screen.js +1 -1
- package/dist/presentation/screen/use_smile_id_document_preview_screen.js.map +1 -1
- package/dist/presentation/screen/use_smile_id_preview_screen.d.ts +1 -21
- package/dist/presentation/screen/use_smile_id_preview_screen.d.ts.map +1 -1
- package/dist/presentation/screen/use_smile_id_preview_screen.js +3 -14
- package/dist/presentation/screen/use_smile_id_preview_screen.js.map +1 -1
- package/dist/presentation/screen/use_smile_id_processing_screen.js +1 -1
- package/dist/presentation/screen/use_smile_id_processing_screen.js.map +1 -1
- package/dist/presentation/utils/locale_utils.d.ts +10 -4
- package/dist/presentation/utils/locale_utils.d.ts.map +1 -1
- package/dist/presentation/utils/locale_utils.js +13 -5
- package/dist/presentation/utils/locale_utils.js.map +1 -1
- package/dist/testing/fake_face_analyzer.d.ts +43 -0
- package/dist/testing/fake_face_analyzer.d.ts.map +1 -0
- package/dist/testing/fake_face_analyzer.js +52 -0
- package/dist/testing/fake_face_analyzer.js.map +1 -0
- package/dist/testing/index.d.ts +5 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +7 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/scenario_expectations.d.ts +28 -0
- package/dist/testing/scenario_expectations.d.ts.map +1 -0
- package/dist/testing/scenario_expectations.js +65 -0
- package/dist/testing/scenario_expectations.js.map +1 -0
- package/dist/testing/scenario_loader.d.ts +11 -0
- package/dist/testing/scenario_loader.d.ts.map +1 -0
- package/dist/testing/scenario_loader.js +64 -0
- package/dist/testing/scenario_loader.js.map +1 -0
- package/dist/testing/scripted_scan_results.d.ts +17 -0
- package/dist/testing/scripted_scan_results.d.ts.map +1 -0
- package/dist/testing/scripted_scan_results.js +52 -0
- package/dist/testing/scripted_scan_results.js.map +1 -0
- package/package.json +11 -4
- package/src/__tests__/dsl.test.ts +51 -1
- package/src/__tests__/setup_react_native.ts +17 -2
- package/src/__tests__/snapshots/__snapshots__/screens_structural.test.tsx.snap +5261 -0
- package/src/__tests__/snapshots/screens_structural.test.tsx +230 -0
- package/src/camera/__tests__/use_smile_id_camera_system.test.ts +103 -0
- package/src/core/exception/__tests__/builder_validation_exceptions.test.ts +67 -0
- package/src/core/exception/agent_mode_with_enhanced_liveness_exception.ts +20 -0
- package/src/core/exception/capture_validation_exceptions.ts +1 -0
- package/src/core/exception/index.ts +1 -0
- package/src/core/exception/use_smile_id_error_code.ts +1 -0
- package/src/core/models/__tests__/document_type.test.ts +2 -2
- package/src/core/models/__tests__/flow_models.test.ts +26 -0
- package/src/core/models/config.ts +22 -0
- package/src/core/models/document_type.ts +2 -2
- package/src/core/models/global_privacy_control.ts +45 -0
- package/src/core/models/network_config.ts +2 -2
- package/src/data/api/__tests__/smile_id_api.test.ts +1 -1
- package/src/data/api/interceptors/__tests__/gzip_interceptor.test.ts +83 -0
- package/src/data/api/interceptors/smile_auth_interceptor.ts +3 -18
- package/src/data/compression/__tests__/payload_compressor_real_jpeg.test.ts +6 -2
- package/src/data/dsl/builder/__tests__/config_builders.test.ts +62 -0
- package/src/data/dsl/builder/retry_config_builder.ts +6 -6
- package/src/data/factory/__tests__/retry_gzip_mac_composition.test.ts +163 -0
- package/src/data/factory/__tests__/signed_job_submission_headers.test.ts +1 -1
- package/src/data/repository/__tests__/network_retries.test.ts +1 -1
- package/src/data/repository/__tests__/use_smile_id_repository.test.ts +1 -1
- package/src/data/security/__tests__/request_canonicalizer.test.ts +132 -0
- package/src/data/smile_id_urls.ts +1 -1
- package/src/data/util/__tests__/retry_util.test.ts +25 -0
- package/src/data/util/retry_util.ts +9 -1
- package/src/index.ts +6 -4
- package/src/metadata/__tests__/metadata_configuration_mappers.test.ts +39 -0
- package/src/metadata/__tests__/metadata_configuration_objects.test.ts +2 -1
- package/src/metadata/__tests__/metadata_device_context.test.ts +3 -3
- package/src/metadata/__tests__/metadata_device_os_android.test.ts +18 -0
- package/src/metadata/__tests__/metadata_golden_fixture.json +1 -1
- package/src/metadata/__tests__/metadata_payload_golden.test.ts +4 -4
- package/src/metadata/__tests__/use_smile_id_metadata.test.ts +1 -1
- package/src/metadata/builder_configuration.ts +6 -0
- package/src/metadata/metadata_configuration_mappers.ts +21 -7
- package/src/metadata/use_smile_id_metadata_factory.ts +17 -6
- package/src/presentation/assets/si_document_green_book_hero_animation.json +1 -4668
- package/src/presentation/assets/si_document_hero_animation.json +1 -4799
- package/src/presentation/assets/si_document_hero_back_animation.json +1 -2039
- package/src/presentation/assets/si_document_passport_hero_animation.json +1 -14491
- package/src/presentation/assets/si_ess_all_directions_hero_animation.json +1 -0
- package/src/presentation/assets/si_ess_animation_all_directions.json +1 -6589
- package/src/presentation/assets/si_selfie_hero_animation.json +1 -1439
- package/src/presentation/assets/si_smile_to_capture_animation.json +1 -1439
- package/src/presentation/components/__tests__/use_smile_id_enhanced_smart_selfie_clip.test.tsx +9 -4
- package/src/presentation/components/__tests__/use_smile_id_instructions_hero.test.tsx +44 -0
- package/src/presentation/components/use_smile_id_enhanced_smart_selfie_clip.tsx +9 -3
- package/src/presentation/components/use_smile_id_instructions_hero.tsx +10 -0
- package/src/presentation/flow/__tests__/document_flow_navigation.test.ts +3 -3
- package/src/presentation/flow/__tests__/flow_navigation_manager.test.ts +49 -0
- package/src/presentation/flow/dsl/builder/__tests__/config_builder.test.ts +25 -0
- package/src/presentation/flow/dsl/builder/config_builder.ts +21 -0
- package/src/presentation/flow/dsl/builder/screens_builder.ts +3 -6
- package/src/presentation/flow/flow_navigation_manager.ts +6 -1
- package/src/presentation/flow/model/instructions_defaults.ts +1 -1
- package/src/presentation/flow/screen/orchestrated_capture_screen.tsx +18 -3
- package/src/presentation/flow/screen/orchestrated_consent_screen.tsx +5 -0
- package/src/presentation/flow/screen/orchestrated_preview_screen.tsx +0 -12
- package/src/presentation/flow/use_smile_id_flow.tsx +11 -4
- package/src/presentation/flow/validation/__tests__/flow_validator.test.ts +61 -1
- package/src/presentation/flow/validation/flow_validator.ts +55 -26
- package/src/presentation/screen/__tests__/capture_ui_state.test.ts +66 -47
- package/src/presentation/screen/__tests__/ess_turn_haptics.test.ts +15 -11
- package/src/presentation/screen/__tests__/scan_state_to_overlay_state.test.ts +47 -27
- package/src/presentation/screen/__tests__/use_smile_id_capture_screen.test.tsx +176 -14
- package/src/presentation/screen/__tests__/use_smile_id_document_back_instructions.test.tsx +1 -1
- package/src/presentation/screen/__tests__/use_smile_id_preview_screen.test.tsx +10 -40
- package/src/presentation/screen/helpers/__tests__/use_capture_orientation_lock.test.tsx +212 -0
- package/src/presentation/screen/helpers/capture_ui_state.ts +27 -27
- package/src/presentation/screen/helpers/ess_turn_haptics.ts +9 -9
- package/src/presentation/screen/helpers/scan_state_to_overlay_state.ts +13 -13
- package/src/presentation/screen/helpers/use_capture_orientation_lock.ts +69 -7
- package/src/presentation/screen/use_smile_id_capture_screen.tsx +47 -19
- package/src/presentation/screen/use_smile_id_consent_screen.tsx +16 -5
- package/src/presentation/screen/use_smile_id_document_back_instructions.tsx +14 -23
- package/src/presentation/screen/use_smile_id_document_preview_screen.tsx +1 -1
- package/src/presentation/screen/use_smile_id_preview_screen.tsx +13 -45
- package/src/presentation/screen/use_smile_id_processing_screen.tsx +1 -1
- package/src/presentation/utils/__tests__/locale_utils.test.ts +46 -5
- package/src/presentation/utils/locale_utils.ts +13 -4
- package/src/testing/__tests__/fake_face_analyzer.test.ts +42 -0
- package/src/testing/__tests__/scenario_expectations.test.ts +110 -0
- package/src/testing/__tests__/scenario_loader.test.ts +64 -0
- package/src/testing/fake_face_analyzer.ts +82 -0
- package/src/testing/index.ts +15 -0
- package/src/testing/scenario_expectations.ts +87 -0
- package/src/testing/scenario_loader.ts +83 -0
- package/src/testing/scripted_scan_results.ts +66 -0
- package/dist/presentation/components/use_smile_id_face_silhouette_overlay.d.ts +0 -15
- package/dist/presentation/components/use_smile_id_face_silhouette_overlay.d.ts.map +0 -1
- package/dist/presentation/components/use_smile_id_face_silhouette_overlay.js +0 -19
- package/dist/presentation/components/use_smile_id_face_silhouette_overlay.js.map +0 -1
- package/src/presentation/components/use_smile_id_face_silhouette_overlay.tsx +0 -27
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
ScanHeadTurnDirection,
|
|
3
|
-
|
|
3
|
+
UseSmileIDScanState,
|
|
4
4
|
} from "@smileid/usesmileid_bridge";
|
|
5
|
-
import {
|
|
5
|
+
import { UseSmileIDScanStateType } from "@smileid/usesmileid_bridge";
|
|
6
6
|
import { CaptureType } from "../../../core/models/ml_config";
|
|
7
7
|
import { UseSmileIDOverlayState } from "../../state/use_smile_id_overlay_state";
|
|
8
8
|
import type { SiKey } from "../../../l10n/strings_en";
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
selfieFeedbackKeyFor,
|
|
13
13
|
} from "./capture_screen_helpers";
|
|
14
14
|
|
|
15
|
-
/** Immutable per-frame UI snapshot derived from the native `
|
|
15
|
+
/** Immutable per-frame UI snapshot derived from the native `UseSmileIDScanState`. */
|
|
16
16
|
export type UseSmileIDCaptureUiState = {
|
|
17
17
|
readonly overlayState: UseSmileIDOverlayState;
|
|
18
18
|
/** Stable si_* key behind the guidance copy and the coaching-animation lookup. */
|
|
@@ -128,21 +128,21 @@ export function preCaptureUiState(
|
|
|
128
128
|
* `unsatisfied`, or `timeout`/`finished` returning to `initial`.
|
|
129
129
|
*/
|
|
130
130
|
export function isSessionBoundary(
|
|
131
|
-
previous:
|
|
132
|
-
next:
|
|
131
|
+
previous: UseSmileIDScanState,
|
|
132
|
+
next: UseSmileIDScanState,
|
|
133
133
|
): boolean {
|
|
134
134
|
if (previous.type === next.type) return false;
|
|
135
|
-
if (previous.type ===
|
|
135
|
+
if (previous.type === UseSmileIDScanStateType.Unsatisfied) return true;
|
|
136
136
|
return (
|
|
137
|
-
(previous.type ===
|
|
138
|
-
previous.type ===
|
|
139
|
-
next.type ===
|
|
137
|
+
(previous.type === UseSmileIDScanStateType.Timeout ||
|
|
138
|
+
previous.type === UseSmileIDScanStateType.Finished) &&
|
|
139
|
+
next.type === UseSmileIDScanStateType.Initial
|
|
140
140
|
);
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
/** Dispatch on capture type — see {@link mapSelfieUiState} / {@link mapDocumentUiState}. */
|
|
144
144
|
export function mapCaptureUiState(
|
|
145
|
-
scanState:
|
|
145
|
+
scanState: UseSmileIDScanState,
|
|
146
146
|
captureType: CaptureType,
|
|
147
147
|
enableEnhancedLiveness: boolean,
|
|
148
148
|
): UseSmileIDCaptureUiState {
|
|
@@ -157,7 +157,7 @@ export function mapCaptureUiState(
|
|
|
157
157
|
* still…") and zeroes the fill.
|
|
158
158
|
*/
|
|
159
159
|
export function mapSelfieUiState(
|
|
160
|
-
scanState:
|
|
160
|
+
scanState: UseSmileIDScanState,
|
|
161
161
|
enableEnhancedLiveness: boolean,
|
|
162
162
|
): UseSmileIDCaptureUiState {
|
|
163
163
|
return withEnhancedLivenessPolicy(
|
|
@@ -167,11 +167,11 @@ export function mapSelfieUiState(
|
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
function selfieUiState(
|
|
170
|
-
scanState:
|
|
170
|
+
scanState: UseSmileIDScanState,
|
|
171
171
|
enableEnhancedLiveness: boolean,
|
|
172
172
|
): UseSmileIDCaptureUiState {
|
|
173
173
|
switch (scanState.type) {
|
|
174
|
-
case
|
|
174
|
+
case UseSmileIDScanStateType.Initial:
|
|
175
175
|
return uiState(
|
|
176
176
|
UseSmileIDOverlayState.INSTRUCTION,
|
|
177
177
|
enableEnhancedLiveness
|
|
@@ -180,7 +180,7 @@ function selfieUiState(
|
|
|
180
180
|
0,
|
|
181
181
|
);
|
|
182
182
|
|
|
183
|
-
case
|
|
183
|
+
case UseSmileIDScanStateType.Found: {
|
|
184
184
|
const overlayState = mapScanStateToOverlayState(
|
|
185
185
|
scanState,
|
|
186
186
|
CaptureType.selfie,
|
|
@@ -191,7 +191,7 @@ function selfieUiState(
|
|
|
191
191
|
return uiState(overlayState, feedbackKey, scanState.captureProgress ?? 0);
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
case
|
|
194
|
+
case UseSmileIDScanStateType.Satisfied:
|
|
195
195
|
// Progress forced to 1 so the ring completes even when the last
|
|
196
196
|
// interim frame undershot; transient before `finished`.
|
|
197
197
|
return uiState(
|
|
@@ -200,7 +200,7 @@ function selfieUiState(
|
|
|
200
200
|
1,
|
|
201
201
|
);
|
|
202
202
|
|
|
203
|
-
case
|
|
203
|
+
case UseSmileIDScanStateType.Unsatisfied:
|
|
204
204
|
// Unsatisfied collapses onto one requirements-not-met copy —
|
|
205
205
|
// the per-check `reason` only feeds telemetry, not guidance.
|
|
206
206
|
return uiState(
|
|
@@ -209,13 +209,13 @@ function selfieUiState(
|
|
|
209
209
|
0,
|
|
210
210
|
);
|
|
211
211
|
|
|
212
|
-
case
|
|
212
|
+
case UseSmileIDScanStateType.Finished:
|
|
213
213
|
return scannedUiState(enableEnhancedLiveness);
|
|
214
214
|
|
|
215
|
-
case
|
|
215
|
+
case UseSmileIDScanStateType.Timeout:
|
|
216
216
|
return uiState(UseSmileIDOverlayState.IDLE, "si_capture_timeout", 0);
|
|
217
217
|
|
|
218
|
-
case
|
|
218
|
+
case UseSmileIDScanStateType.HeadTurn: {
|
|
219
219
|
const direction = scanState.currentDirection ?? null;
|
|
220
220
|
const turnPrompt: SiKey =
|
|
221
221
|
direction === "left"
|
|
@@ -243,20 +243,20 @@ function selfieUiState(
|
|
|
243
243
|
|
|
244
244
|
/** Document scan-state → UI-state mapping. */
|
|
245
245
|
export function mapDocumentUiState(
|
|
246
|
-
scanState:
|
|
246
|
+
scanState: UseSmileIDScanState,
|
|
247
247
|
): UseSmileIDCaptureUiState {
|
|
248
248
|
switch (scanState.type) {
|
|
249
|
-
case
|
|
249
|
+
case UseSmileIDScanStateType.Initial:
|
|
250
250
|
// No head-turn phase for documents; a degenerate headTurn maps to
|
|
251
251
|
// the pre-capture instruction.
|
|
252
|
-
case
|
|
252
|
+
case UseSmileIDScanStateType.HeadTurn:
|
|
253
253
|
return uiState(
|
|
254
254
|
UseSmileIDOverlayState.INSTRUCTION,
|
|
255
255
|
"si_document_initial_instruction",
|
|
256
256
|
0,
|
|
257
257
|
);
|
|
258
258
|
|
|
259
|
-
case
|
|
259
|
+
case UseSmileIDScanStateType.Found: {
|
|
260
260
|
const overlayState = mapScanStateToOverlayState(
|
|
261
261
|
scanState,
|
|
262
262
|
CaptureType.document,
|
|
@@ -275,20 +275,20 @@ export function mapDocumentUiState(
|
|
|
275
275
|
|
|
276
276
|
// Documents collapse satisfied/finished onto the same terminal
|
|
277
277
|
// snapshot: the still is captured and the screen is moving on.
|
|
278
|
-
case
|
|
278
|
+
case UseSmileIDScanStateType.Satisfied:
|
|
279
279
|
return scannedDocumentUiState();
|
|
280
280
|
|
|
281
|
-
case
|
|
281
|
+
case UseSmileIDScanStateType.Unsatisfied:
|
|
282
282
|
return uiState(
|
|
283
283
|
UseSmileIDOverlayState.IDLE,
|
|
284
284
|
"si_document_capture_unsatisfied",
|
|
285
285
|
0,
|
|
286
286
|
);
|
|
287
287
|
|
|
288
|
-
case
|
|
288
|
+
case UseSmileIDScanStateType.Finished:
|
|
289
289
|
return scannedDocumentUiState();
|
|
290
290
|
|
|
291
|
-
case
|
|
291
|
+
case UseSmileIDScanStateType.Timeout:
|
|
292
292
|
return uiState(
|
|
293
293
|
UseSmileIDOverlayState.IDLE,
|
|
294
294
|
"si_document_capture_timeout",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as Haptics from "expo-haptics";
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
2
|
+
import type { UseSmileIDScanState } from "@smileid/usesmileid_bridge";
|
|
3
|
+
import { UseSmileIDScanStateType } from "@smileid/usesmileid_bridge";
|
|
4
4
|
|
|
5
5
|
/** Which haptic a completed Enhanced Smart Selfie head turn warrants. */
|
|
6
6
|
export type EssTurnHaptic = "intermediate" | "final";
|
|
@@ -20,16 +20,16 @@ export type EssTurnHaptic = "intermediate" | "final";
|
|
|
20
20
|
* scan state so the same completion is reported exactly once.
|
|
21
21
|
*/
|
|
22
22
|
export function essTurnHaptic(
|
|
23
|
-
previous:
|
|
24
|
-
next:
|
|
23
|
+
previous: UseSmileIDScanState,
|
|
24
|
+
next: UseSmileIDScanState,
|
|
25
25
|
): EssTurnHaptic | null {
|
|
26
26
|
// Only a transition out of an active, directed head turn can complete one.
|
|
27
|
-
if (previous.type !==
|
|
27
|
+
if (previous.type !== UseSmileIDScanStateType.HeadTurn) return null;
|
|
28
28
|
if (previous.currentDirection === undefined) return null;
|
|
29
29
|
|
|
30
30
|
// The requested direction advanced — the previous turn completed.
|
|
31
31
|
if (
|
|
32
|
-
next.type ===
|
|
32
|
+
next.type === UseSmileIDScanStateType.HeadTurn &&
|
|
33
33
|
next.currentDirection !== undefined &&
|
|
34
34
|
next.currentDirection !== previous.currentDirection
|
|
35
35
|
) {
|
|
@@ -39,9 +39,9 @@ export function essTurnHaptic(
|
|
|
39
39
|
// Leaving the head-turn phase toward capture is the final turn's
|
|
40
40
|
// completion; `initial` / `timeout` are resets, not completions.
|
|
41
41
|
if (
|
|
42
|
-
next.type ===
|
|
43
|
-
next.type ===
|
|
44
|
-
next.type ===
|
|
42
|
+
next.type === UseSmileIDScanStateType.Found ||
|
|
43
|
+
next.type === UseSmileIDScanStateType.Satisfied ||
|
|
44
|
+
next.type === UseSmileIDScanStateType.Finished
|
|
45
45
|
) {
|
|
46
46
|
return "final";
|
|
47
47
|
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
1
|
+
import type { UseSmileIDScanState } from "@smileid/usesmileid_bridge";
|
|
2
|
+
import { UseSmileIDScanStateType } from "@smileid/usesmileid_bridge";
|
|
3
3
|
import { CaptureType } from "../../../core/models/ml_config";
|
|
4
4
|
import { UseSmileIDOverlayState } from "../../state/use_smile_id_overlay_state";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Map an analyzer's `
|
|
7
|
+
* Map an analyzer's `UseSmileIDScanState` into the SDK's visual `OverlayState`.
|
|
8
8
|
*
|
|
9
9
|
* The overlay enum drives the face/document oval's border colour, dash
|
|
10
10
|
* pattern, and fill animation — it's a presentation concept layered on
|
|
11
11
|
* top of the scan-state machine. The mapping reads `state.feedback`
|
|
12
|
-
* (when `type ===
|
|
13
|
-
* `type ===
|
|
12
|
+
* (when `type === UseSmileIDScanStateType.Found`) and `state.reason` (when
|
|
13
|
+
* `type === UseSmileIDScanStateType.Unsatisfied`) to pick the fine-grained
|
|
14
14
|
* overlay state; other scan-state types map coarsely to the matching
|
|
15
15
|
* lifecycle entry.
|
|
16
16
|
*
|
|
@@ -20,14 +20,14 @@ import { UseSmileIDOverlayState } from "../../state/use_smile_id_overlay_state";
|
|
|
20
20
|
* a new feedback key the JS side hasn't been updated for.
|
|
21
21
|
*/
|
|
22
22
|
export function mapScanStateToOverlayState(
|
|
23
|
-
scanState:
|
|
23
|
+
scanState: UseSmileIDScanState,
|
|
24
24
|
captureType: CaptureType,
|
|
25
25
|
): UseSmileIDOverlayState {
|
|
26
26
|
switch (scanState.type) {
|
|
27
|
-
case
|
|
27
|
+
case UseSmileIDScanStateType.Initial:
|
|
28
28
|
return UseSmileIDOverlayState.INSTRUCTION;
|
|
29
29
|
|
|
30
|
-
case
|
|
30
|
+
case UseSmileIDScanStateType.Found:
|
|
31
31
|
// `feedback` is the canonical `si_capture_*` key the native side
|
|
32
32
|
// emits; partner code never sees raw English copy.
|
|
33
33
|
return resolveFeedbackOverlay(
|
|
@@ -38,10 +38,10 @@ export function mapScanStateToOverlayState(
|
|
|
38
38
|
: UseSmileIDOverlayState.DOCUMENT_READY,
|
|
39
39
|
);
|
|
40
40
|
|
|
41
|
-
case
|
|
41
|
+
case UseSmileIDScanStateType.Satisfied:
|
|
42
42
|
return UseSmileIDOverlayState.READY;
|
|
43
43
|
|
|
44
|
-
case
|
|
44
|
+
case UseSmileIDScanStateType.Unsatisfied:
|
|
45
45
|
return resolveFeedbackOverlay(
|
|
46
46
|
scanState.reason,
|
|
47
47
|
captureType,
|
|
@@ -50,13 +50,13 @@ export function mapScanStateToOverlayState(
|
|
|
50
50
|
: UseSmileIDOverlayState.DOCUMENT_NOT_CENTERED,
|
|
51
51
|
);
|
|
52
52
|
|
|
53
|
-
case
|
|
53
|
+
case UseSmileIDScanStateType.Finished:
|
|
54
54
|
return UseSmileIDOverlayState.CAPTURING;
|
|
55
55
|
|
|
56
|
-
case
|
|
56
|
+
case UseSmileIDScanStateType.HeadTurn:
|
|
57
57
|
return UseSmileIDOverlayState.HEAD_TURN;
|
|
58
58
|
|
|
59
|
-
case
|
|
59
|
+
case UseSmileIDScanStateType.Timeout:
|
|
60
60
|
// Back to the gray pre-capture state while partners re-prompt.
|
|
61
61
|
return UseSmileIDOverlayState.IDLE;
|
|
62
62
|
}
|
|
@@ -3,6 +3,14 @@ import * as ScreenOrientation from "expo-screen-orientation";
|
|
|
3
3
|
import { UseSmileIDCrashReporting } from "@smileid/usesmileid_bridge";
|
|
4
4
|
import type { DocumentOrientation } from "../../../core/models/document_type";
|
|
5
5
|
|
|
6
|
+
export type CaptureOrientationOptions = {
|
|
7
|
+
/**
|
|
8
|
+
* Lock applied when the capture screen unmounts. Defaults to portrait-up;
|
|
9
|
+
* threaded from the builder's `exitOrientations`.
|
|
10
|
+
*/
|
|
11
|
+
readonly exitLock?: ScreenOrientation.OrientationLock;
|
|
12
|
+
};
|
|
13
|
+
|
|
6
14
|
/**
|
|
7
15
|
* Lock device orientation for a capture screen, scoped to its lifetime
|
|
8
16
|
* (`"Landscape"` → landscape, `"Portrait"` → portrait-up).
|
|
@@ -10,26 +18,76 @@ import type { DocumentOrientation } from "../../../core/models/document_type";
|
|
|
10
18
|
* Each screen asserts its OWN orientation on mount, so document → selfie
|
|
11
19
|
* reverts deterministically rather than depending on the previous
|
|
12
20
|
* screen's teardown winning a race against the next camera initialising.
|
|
13
|
-
* Unmount restores portrait-up as a safety net.
|
|
14
21
|
*
|
|
15
22
|
* Locks are global: the host app must permit the locked orientations at
|
|
16
23
|
* the OS level (iOS `UISupportedInterfaceOrientations`, Android activity
|
|
17
|
-
* `screenOrientation`) for the lock to take effect
|
|
24
|
+
* `screenOrientation`) for the lock to take effect — a host that does not
|
|
25
|
+
* is warned about and reported rather than silently staying upright.
|
|
18
26
|
*/
|
|
19
27
|
export function useCaptureOrientationLock(
|
|
20
28
|
orientation: DocumentOrientation,
|
|
29
|
+
{
|
|
30
|
+
exitLock = ScreenOrientation.OrientationLock.PORTRAIT_UP,
|
|
31
|
+
}: CaptureOrientationOptions = {},
|
|
21
32
|
): void {
|
|
22
33
|
useEffect(() => {
|
|
23
34
|
const target =
|
|
24
35
|
orientation === "Landscape"
|
|
25
36
|
? ScreenOrientation.OrientationLock.LANDSCAPE
|
|
26
37
|
: ScreenOrientation.OrientationLock.PORTRAIT_UP;
|
|
38
|
+
void reportIfLockUnsupported(target);
|
|
27
39
|
void lockOrientation(target);
|
|
28
40
|
|
|
29
41
|
return () => {
|
|
30
|
-
void lockOrientation(
|
|
42
|
+
void lockOrientation(exitLock);
|
|
31
43
|
};
|
|
32
|
-
}, [orientation]);
|
|
44
|
+
}, [orientation, exitLock]);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Surface a host build that cannot honour `target`. `lockAsync` resolves
|
|
49
|
+
* successfully in that case, so without this preflight the capture screen
|
|
50
|
+
* just stays in the wrong orientation with no signal. (Expo's `app.json`
|
|
51
|
+
* `orientation` value is not such a case: the module's app-delegate hook
|
|
52
|
+
* outranks the baked `Info.plist` on iOS — device-verified.)
|
|
53
|
+
*/
|
|
54
|
+
async function reportIfLockUnsupported(
|
|
55
|
+
target: ScreenOrientation.OrientationLock,
|
|
56
|
+
): Promise<void> {
|
|
57
|
+
let supported: boolean;
|
|
58
|
+
try {
|
|
59
|
+
supported = await ScreenOrientation.supportsOrientationLockAsync(target);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
reportOrientationFailure(error);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (supported) return;
|
|
65
|
+
|
|
66
|
+
// `typeof` guard: `__DEV__` is bundler-injected and absent outside Metro-like
|
|
67
|
+
// environments, where a bare reference throws rather than reading falsy.
|
|
68
|
+
const isDevBuild = typeof __DEV__ !== "undefined" && __DEV__;
|
|
69
|
+
if (isDevBuild && !warnedUnsupportedTargets.has(target)) {
|
|
70
|
+
warnedUnsupportedTargets.add(target);
|
|
71
|
+
console.warn(
|
|
72
|
+
"[UseSmileID] The capture screen needs to lock the device orientation, " +
|
|
73
|
+
"but this build does not permit it. Allow the required orientations " +
|
|
74
|
+
"in your app's iOS Info.plist / Android manifest.",
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
reportOrientationFailure(
|
|
78
|
+
new Error(`orientation lock unsupported by host build: ${target}`),
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Dedupes the dev warning per target: a flow remounts the capture screen for the back
|
|
84
|
+
* side and interstitial, and the misconfiguration can't change mid-session.
|
|
85
|
+
*/
|
|
86
|
+
const warnedUnsupportedTargets = new Set<ScreenOrientation.OrientationLock>();
|
|
87
|
+
|
|
88
|
+
/** Test seam: clears the once-per-target dev-warning dedupe. */
|
|
89
|
+
export function resetUnsupportedLockWarningsForTesting(): void {
|
|
90
|
+
warnedUnsupportedTargets.clear();
|
|
33
91
|
}
|
|
34
92
|
|
|
35
93
|
/** Lock to `target`, routing a native failure through crash reporting. */
|
|
@@ -39,8 +97,12 @@ async function lockOrientation(
|
|
|
39
97
|
try {
|
|
40
98
|
await ScreenOrientation.lockAsync(target);
|
|
41
99
|
} catch (error) {
|
|
42
|
-
|
|
43
|
-
error instanceof Error ? error : new Error(String(error)),
|
|
44
|
-
);
|
|
100
|
+
reportOrientationFailure(error);
|
|
45
101
|
}
|
|
46
102
|
}
|
|
103
|
+
|
|
104
|
+
function reportOrientationFailure(error: unknown): void {
|
|
105
|
+
UseSmileIDCrashReporting.captureException(
|
|
106
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
107
|
+
);
|
|
108
|
+
}
|
|
@@ -28,11 +28,14 @@ import type {
|
|
|
28
28
|
DocumentAnalyzer,
|
|
29
29
|
FaceAnalyzer,
|
|
30
30
|
} from "@smileid/usesmileid_platform_interface";
|
|
31
|
-
import type {
|
|
31
|
+
import type {
|
|
32
|
+
CaptureReadyEvent,
|
|
33
|
+
UseSmileIDScanState,
|
|
34
|
+
} from "@smileid/usesmileid_bridge";
|
|
32
35
|
import {
|
|
33
36
|
CaptureReadyEventKind,
|
|
34
37
|
DocumentSide,
|
|
35
|
-
|
|
38
|
+
UseSmileIDScanStateType,
|
|
36
39
|
} from "@smileid/usesmileid_bridge";
|
|
37
40
|
import { CaptureType } from "../../core/models/ml_config";
|
|
38
41
|
import type { DocumentCaptureMode } from "../../core/models/document_capture_mode";
|
|
@@ -108,7 +111,7 @@ export type UseSmileIDCaptureScreenProps = {
|
|
|
108
111
|
readonly onDocumentOrigin?: (origin: DocumentImageOrigin) => void;
|
|
109
112
|
|
|
110
113
|
/** Fires when the state machine reaches `"timeout"`; the screen stops streaming. */
|
|
111
|
-
readonly onCaptureTimeout?: (state:
|
|
114
|
+
readonly onCaptureTimeout?: (state: UseSmileIDScanState) => void;
|
|
112
115
|
|
|
113
116
|
readonly onBack?: () => void;
|
|
114
117
|
readonly showAttribution?: boolean;
|
|
@@ -116,6 +119,9 @@ export type UseSmileIDCaptureScreenProps = {
|
|
|
116
119
|
/** Keeps the oval rim neutral through CAPTURING/PROCESSING — ESS has no green oval treatment. */
|
|
117
120
|
readonly enableEnhancedLiveness?: boolean;
|
|
118
121
|
|
|
122
|
+
/** Runs a selfie capture on the rear camera. Ignored for document capture. */
|
|
123
|
+
readonly allowAgentMode?: boolean;
|
|
124
|
+
|
|
119
125
|
/**
|
|
120
126
|
* Document aspect ratio (width / height) for the document capture
|
|
121
127
|
* guide cutout. Ignored for selfie capture. Omitted ⇒ the overlay's
|
|
@@ -162,9 +168,12 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
|
|
|
162
168
|
const theme = props.theme ?? defaultLightTheme;
|
|
163
169
|
const showAttribution = props.showAttribution ?? true;
|
|
164
170
|
const enableEnhancedLiveness = props.enableEnhancedLiveness ?? false;
|
|
171
|
+
const allowAgentMode = props.allowAgentMode ?? false;
|
|
165
172
|
// Literal union — useCameraDevice takes the lowercase string directly.
|
|
166
173
|
const cameraFacing: "front" | "back" =
|
|
167
|
-
props.captureType === CaptureType.selfie
|
|
174
|
+
props.captureType === CaptureType.selfie && !allowAgentMode
|
|
175
|
+
? "front"
|
|
176
|
+
: "back";
|
|
168
177
|
|
|
169
178
|
const { hasPermission, requestPermission } = useCameraPermission();
|
|
170
179
|
const device = useCameraDevice(cameraFacing);
|
|
@@ -202,8 +211,8 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
|
|
|
202
211
|
}, [hasPermission, requestPermission]);
|
|
203
212
|
|
|
204
213
|
// Per-frame scan state drives the overlay + feedback bubble.
|
|
205
|
-
const [scanState, setScanState] = useState<
|
|
206
|
-
type:
|
|
214
|
+
const [scanState, setScanState] = useState<UseSmileIDScanState>({
|
|
215
|
+
type: UseSmileIDScanStateType.Initial,
|
|
207
216
|
});
|
|
208
217
|
const [streaming, setStreaming] = useState<boolean>(false);
|
|
209
218
|
// Bumped on every Start tap to reset the feedback debouncer.
|
|
@@ -213,6 +222,11 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
|
|
|
213
222
|
const [hasAnalyzedFrame, setHasAnalyzedFrame] = useState<boolean>(false);
|
|
214
223
|
// Fallback shutter, revealed after activateManualAfterMs. Resets per mount.
|
|
215
224
|
const [manualShutterRevealed, setManualShutterRevealed] = useState(false);
|
|
225
|
+
// Set when a document scan times out, so the recovery controls stay on screen
|
|
226
|
+
// after the scan (and `streaming`) stops. `timedOutRef` keeps the terminal
|
|
227
|
+
// branch idempotent without latching the one-shot delivery gate.
|
|
228
|
+
const [captureTimedOut, setCaptureTimedOut] = useState(false);
|
|
229
|
+
const timedOutRef = useRef(false);
|
|
216
230
|
// True while a manual/gallery delivery is in flight — disables the controls.
|
|
217
231
|
const [deliveringDocument, setDeliveringDocument] = useState(false);
|
|
218
232
|
// Transient gallery-import error; overrides the live chip, self-clears so the user can re-pick.
|
|
@@ -232,7 +246,7 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
|
|
|
232
246
|
const completedRef = useRef<boolean>(false);
|
|
233
247
|
// Raw (not committed) frame state for the session-boundary check, so
|
|
234
248
|
// transitions the 1% bucketing skips committing are still seen.
|
|
235
|
-
const previousScanStateRef = useRef<
|
|
249
|
+
const previousScanStateRef = useRef<UseSmileIDScanState | null>(null);
|
|
236
250
|
|
|
237
251
|
// Capture telemetry: wall-clock start (first analyzed frame) and a
|
|
238
252
|
// rising-edge `unsatisfied` retry counter, surfaced via onCaptureComplete.
|
|
@@ -475,13 +489,14 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
|
|
|
475
489
|
if (captureStartedAtRef.current === null) {
|
|
476
490
|
captureStartedAtRef.current = Date.now();
|
|
477
491
|
}
|
|
478
|
-
const isUnsatisfied =
|
|
492
|
+
const isUnsatisfied =
|
|
493
|
+
result.state.type === UseSmileIDScanStateType.Unsatisfied;
|
|
479
494
|
if (isUnsatisfied && !wasUnsatisfiedRef.current) {
|
|
480
495
|
captureRetriesRef.current += 1;
|
|
481
496
|
}
|
|
482
497
|
wasUnsatisfiedRef.current = isUnsatisfied;
|
|
483
498
|
|
|
484
|
-
if (result.state.type ===
|
|
499
|
+
if (result.state.type === UseSmileIDScanStateType.Unsatisfied) {
|
|
485
500
|
capturedEventsRef.current = [];
|
|
486
501
|
} else if (result.capturedEvents.length > 0) {
|
|
487
502
|
capturedEventsRef.current = [
|
|
@@ -507,7 +522,7 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
|
|
|
507
522
|
|
|
508
523
|
// Terminal states: finished → flush + complete; timeout → failure
|
|
509
524
|
// without flushing (no captured artifacts to wait on).
|
|
510
|
-
if (result.state.type ===
|
|
525
|
+
if (result.state.type === UseSmileIDScanStateType.Finished) {
|
|
511
526
|
if (completedRef.current) return;
|
|
512
527
|
completedRef.current = true;
|
|
513
528
|
setStreaming(false);
|
|
@@ -524,10 +539,18 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
|
|
|
524
539
|
onDocumentOrigin?.(DocumentImageOrigin.CameraAutoCapture);
|
|
525
540
|
onCaptureComplete?.(capturedEventsRef.current, captureTelemetry());
|
|
526
541
|
});
|
|
527
|
-
} else if (result.state.type ===
|
|
528
|
-
if (completedRef.current) return;
|
|
529
|
-
completedRef.current = true;
|
|
542
|
+
} else if (result.state.type === UseSmileIDScanStateType.Timeout) {
|
|
543
|
+
if (completedRef.current || timedOutRef.current) return;
|
|
530
544
|
setStreaming(false);
|
|
545
|
+
if (isDocumentCapture) {
|
|
546
|
+
// Document capture has no Start button, so a timeout must leave a way
|
|
547
|
+
// forward: reveal the manual shutter and keep the delivery gate open
|
|
548
|
+
// so the tap can still produce a capture.
|
|
549
|
+
timedOutRef.current = true;
|
|
550
|
+
setCaptureTimedOut(true);
|
|
551
|
+
} else {
|
|
552
|
+
completedRef.current = true;
|
|
553
|
+
}
|
|
531
554
|
onCaptureTimeout?.(result.state);
|
|
532
555
|
}
|
|
533
556
|
},
|
|
@@ -549,7 +572,7 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
|
|
|
549
572
|
sessionEpoch,
|
|
550
573
|
);
|
|
551
574
|
const isInstructionState =
|
|
552
|
-
scanState.type ===
|
|
575
|
+
scanState.type === UseSmileIDScanStateType.Initial && !streaming;
|
|
553
576
|
const canStart = !streaming && !completedRef.current;
|
|
554
577
|
|
|
555
578
|
const previewReady = hasPermission && device != null;
|
|
@@ -687,13 +710,18 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
|
|
|
687
710
|
// Document controls — live capture only (after Start). Manual shutter
|
|
688
711
|
// follows the capture mode + fallback timer; gallery follows
|
|
689
712
|
// allowGalleryUpload. Renderer also hides both during CAPTURING/PROCESSING.
|
|
713
|
+
// A timeout offers the shutter in every capture mode — it is the recovery
|
|
714
|
+
// affordance, so it outranks `captureMode` and survives `streaming` going
|
|
715
|
+
// false with the scan.
|
|
690
716
|
const manualShutterAvailable =
|
|
691
717
|
isDocumentCapture &&
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
718
|
+
(captureTimedOut ||
|
|
719
|
+
(streaming &&
|
|
720
|
+
(captureMode?.kind === "manualCapture" ||
|
|
721
|
+
(captureMode?.kind === "autoCaptureWithManualFallback" &&
|
|
722
|
+
manualShutterRevealed))));
|
|
723
|
+
const galleryAvailable =
|
|
724
|
+
isDocumentCapture && (streaming || captureTimedOut) && allowGalleryUpload;
|
|
697
725
|
|
|
698
726
|
// Deps are scalar fields (mapCaptureUiState returns a fresh object per
|
|
699
727
|
// render). captureProgress intentionally stays a separate per-frame prop.
|
|
@@ -26,6 +26,7 @@ import { UseSmileIDLearnMoreContent } from "../components/use_smile_id_learn_mor
|
|
|
26
26
|
import { UseSmileIDPartnerIcon } from "../components/use_smile_id_partner_icon";
|
|
27
27
|
import { UseSmileIDRoundedCheckbox } from "../components/use_smile_id_rounded_checkbox";
|
|
28
28
|
import { useUseSmileIDLocalizations } from "../../l10n/use_smile_id_localizations_provider";
|
|
29
|
+
import { GlobalPrivacyControl } from "../../core/models/global_privacy_control";
|
|
29
30
|
import { getDefaultConsentState, getDeviceLocale } from "../utils/locale_utils";
|
|
30
31
|
import {
|
|
31
32
|
buildConsentRows,
|
|
@@ -59,6 +60,12 @@ export type UseSmileIDConsentScreenProps = {
|
|
|
59
60
|
readonly showAttribution?: boolean;
|
|
60
61
|
readonly onAllow?: (info: ConsentInformation) => void;
|
|
61
62
|
readonly onDeny?: () => void;
|
|
63
|
+
/**
|
|
64
|
+
* Partner-declared consent posture; seeds the checkbox's initial state
|
|
65
|
+
* via `getDefaultConsentState`. Defaults to the universal opt-out
|
|
66
|
+
* baseline (`GlobalPrivacyControl.OptOut`).
|
|
67
|
+
*/
|
|
68
|
+
readonly globalPrivacyControl?: GlobalPrivacyControl;
|
|
62
69
|
readonly theme?: UseSmileIDTheme;
|
|
63
70
|
/**
|
|
64
71
|
* Render the bottom-pinned Allow button via a partner-supplied
|
|
@@ -107,11 +114,15 @@ export function UseSmileIDConsentScreen(props: UseSmileIDConsentScreenProps) {
|
|
|
107
114
|
const loc = useUseSmileIDLocalizations();
|
|
108
115
|
const showAttribution = props.showAttribution ?? true;
|
|
109
116
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
//
|
|
114
|
-
|
|
117
|
+
const globalPrivacyControl =
|
|
118
|
+
props.globalPrivacyControl ?? GlobalPrivacyControl.OptOut;
|
|
119
|
+
|
|
120
|
+
// Posture-aware default: the opt-out baseline starts ticked everywhere;
|
|
121
|
+
// the legacy opt-in posture consults the resolved BCP-47 locale and
|
|
122
|
+
// starts unticked for EU member states (GDPR opt-in).
|
|
123
|
+
const [consented, setConsented] = useState(() =>
|
|
124
|
+
getDefaultConsentState(globalPrivacyControl),
|
|
125
|
+
);
|
|
115
126
|
const [learnMoreOpen, setLearnMoreOpen] = useState(false);
|
|
116
127
|
|
|
117
128
|
const subtitleParts = composeConsentSubtitleParts(props.partnerName, loc);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StyleSheet, Text, View } from "react-native";
|
|
2
2
|
import { SafeAreaView } from "react-native-safe-area-context";
|
|
3
3
|
import type { UseSmileIDTheme } from "../theme/theme";
|
|
4
4
|
import { useUseSmileIDTheme } from "../theme/theme_context";
|
|
5
|
+
import { UseSmileIDAttribution } from "../components/use_smile_id_attribution";
|
|
5
6
|
import { UseSmileIDBackButton } from "../components/use_smile_id_back_button";
|
|
6
7
|
import { UseSmileIDButton } from "../components/use_smile_id_button";
|
|
7
8
|
import { UseSmileIDInstructionsHero } from "../components/use_smile_id_instructions_hero";
|
|
@@ -48,7 +49,7 @@ export function UseSmileIDDocumentBackInstructions(
|
|
|
48
49
|
styles.root,
|
|
49
50
|
{
|
|
50
51
|
backgroundColor: theme.colors.background,
|
|
51
|
-
paddingHorizontal: theme.spacing.
|
|
52
|
+
paddingHorizontal: theme.spacing.medium,
|
|
52
53
|
paddingVertical: theme.spacing.medium,
|
|
53
54
|
},
|
|
54
55
|
]}
|
|
@@ -70,12 +71,15 @@ export function UseSmileIDDocumentBackInstructions(
|
|
|
70
71
|
...theme.typography.pageHeading,
|
|
71
72
|
fontWeight: "800",
|
|
72
73
|
color: theme.colors.titleText,
|
|
74
|
+
textAlign: "center",
|
|
73
75
|
}}
|
|
74
76
|
testID="si_document_back_instructions_title"
|
|
75
77
|
>
|
|
76
78
|
{localizations.siDocumentFlipTitle(props.documentTypeDisplayName)}
|
|
77
79
|
</Text>
|
|
78
80
|
|
|
81
|
+
<View style={{ height: theme.spacing.medium }} />
|
|
82
|
+
|
|
79
83
|
<View style={styles.hero} testID="si_document_back_instructions_hero">
|
|
80
84
|
<UseSmileIDInstructionsHero
|
|
81
85
|
source={heroSource}
|
|
@@ -98,24 +102,17 @@ export function UseSmileIDDocumentBackInstructions(
|
|
|
98
102
|
{allowSkip ? (
|
|
99
103
|
<>
|
|
100
104
|
<View style={{ height: theme.spacing.small }} />
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
<UseSmileIDButton
|
|
106
|
+
label={localizations.siDocumentSkipBack}
|
|
107
|
+
onClick={onSkip}
|
|
108
|
+
variant="secondary"
|
|
109
|
+
theme={theme}
|
|
104
110
|
testID="si_document_skip_back_button"
|
|
105
|
-
|
|
106
|
-
>
|
|
107
|
-
<Text
|
|
108
|
-
style={{
|
|
109
|
-
...theme.typography.button,
|
|
110
|
-
color: theme.colors.primary,
|
|
111
|
-
textAlign: "center",
|
|
112
|
-
}}
|
|
113
|
-
>
|
|
114
|
-
{localizations.siDocumentSkipBack}
|
|
115
|
-
</Text>
|
|
116
|
-
</Pressable>
|
|
111
|
+
/>
|
|
117
112
|
</>
|
|
118
113
|
) : null}
|
|
114
|
+
<View style={{ height: theme.spacing.small }} />
|
|
115
|
+
<UseSmileIDAttribution theme={theme} />
|
|
119
116
|
</View>
|
|
120
117
|
</SafeAreaView>
|
|
121
118
|
);
|
|
@@ -125,10 +122,4 @@ const styles = StyleSheet.create({
|
|
|
125
122
|
root: { flex: 1 },
|
|
126
123
|
hero: { flex: 1 },
|
|
127
124
|
bottom: { alignItems: "center" },
|
|
128
|
-
skip: {
|
|
129
|
-
height: 48,
|
|
130
|
-
alignItems: "center",
|
|
131
|
-
justifyContent: "center",
|
|
132
|
-
alignSelf: "stretch",
|
|
133
|
-
},
|
|
134
125
|
});
|