@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.
Files changed (255) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.d.ts +11 -0
  3. package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.d.ts.map +1 -0
  4. package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.js +17 -0
  5. package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.js.map +1 -0
  6. package/dist/core/exception/capture_validation_exceptions.d.ts +1 -0
  7. package/dist/core/exception/capture_validation_exceptions.d.ts.map +1 -1
  8. package/dist/core/exception/capture_validation_exceptions.js +1 -0
  9. package/dist/core/exception/capture_validation_exceptions.js.map +1 -1
  10. package/dist/core/exception/index.d.ts +1 -0
  11. package/dist/core/exception/index.d.ts.map +1 -1
  12. package/dist/core/exception/index.js +1 -0
  13. package/dist/core/exception/index.js.map +1 -1
  14. package/dist/core/exception/use_smile_id_error_code.d.ts +1 -0
  15. package/dist/core/exception/use_smile_id_error_code.d.ts.map +1 -1
  16. package/dist/core/exception/use_smile_id_error_code.js +1 -0
  17. package/dist/core/exception/use_smile_id_error_code.js.map +1 -1
  18. package/dist/core/models/config.d.ts +19 -0
  19. package/dist/core/models/config.d.ts.map +1 -1
  20. package/dist/core/models/document_type.d.ts +1 -1
  21. package/dist/core/models/document_type.d.ts.map +1 -1
  22. package/dist/core/models/document_type.js +2 -2
  23. package/dist/core/models/document_type.js.map +1 -1
  24. package/dist/core/models/global_privacy_control.d.ts +36 -0
  25. package/dist/core/models/global_privacy_control.d.ts.map +1 -0
  26. package/dist/core/models/global_privacy_control.js +39 -0
  27. package/dist/core/models/global_privacy_control.js.map +1 -0
  28. package/dist/core/models/network_config.d.ts +2 -2
  29. package/dist/core/models/network_config.js +2 -2
  30. package/dist/data/api/interceptors/smile_auth_interceptor.d.ts.map +1 -1
  31. package/dist/data/api/interceptors/smile_auth_interceptor.js +3 -17
  32. package/dist/data/api/interceptors/smile_auth_interceptor.js.map +1 -1
  33. package/dist/data/api/smile_id_api.d.ts +1 -1
  34. package/dist/data/api/smile_id_api.d.ts.map +1 -1
  35. package/dist/data/dsl/builder/retry_config_builder.d.ts +4 -4
  36. package/dist/data/dsl/builder/retry_config_builder.d.ts.map +1 -1
  37. package/dist/data/dsl/builder/retry_config_builder.js +6 -6
  38. package/dist/data/dsl/builder/retry_config_builder.js.map +1 -1
  39. package/dist/data/smile_id_urls.d.ts +1 -1
  40. package/dist/data/smile_id_urls.js +1 -1
  41. package/dist/data/smile_id_urls.js.map +1 -1
  42. package/dist/data/util/retry_util.d.ts +1 -1
  43. package/dist/data/util/retry_util.d.ts.map +1 -1
  44. package/dist/data/util/retry_util.js +5 -0
  45. package/dist/data/util/retry_util.js.map +1 -1
  46. package/dist/index.d.ts +2 -2
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +2 -2
  49. package/dist/index.js.map +1 -1
  50. package/dist/metadata/builder_configuration.d.ts +5 -0
  51. package/dist/metadata/builder_configuration.d.ts.map +1 -1
  52. package/dist/metadata/builder_configuration.js +1 -0
  53. package/dist/metadata/builder_configuration.js.map +1 -1
  54. package/dist/metadata/metadata_configuration_mappers.d.ts +2 -3
  55. package/dist/metadata/metadata_configuration_mappers.d.ts.map +1 -1
  56. package/dist/metadata/metadata_configuration_mappers.js +13 -6
  57. package/dist/metadata/metadata_configuration_mappers.js.map +1 -1
  58. package/dist/metadata/use_smile_id_metadata_factory.d.ts.map +1 -1
  59. package/dist/metadata/use_smile_id_metadata_factory.js +14 -5
  60. package/dist/metadata/use_smile_id_metadata_factory.js.map +1 -1
  61. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts +5 -2
  62. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts.map +1 -1
  63. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js +8 -3
  64. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js.map +1 -1
  65. package/dist/presentation/components/use_smile_id_instructions_hero.d.ts.map +1 -1
  66. package/dist/presentation/components/use_smile_id_instructions_hero.js +8 -3
  67. package/dist/presentation/components/use_smile_id_instructions_hero.js.map +1 -1
  68. package/dist/presentation/flow/dsl/builder/config_builder.d.ts +16 -0
  69. package/dist/presentation/flow/dsl/builder/config_builder.d.ts.map +1 -1
  70. package/dist/presentation/flow/dsl/builder/config_builder.js +18 -0
  71. package/dist/presentation/flow/dsl/builder/config_builder.js.map +1 -1
  72. package/dist/presentation/flow/dsl/builder/screens_builder.d.ts +3 -6
  73. package/dist/presentation/flow/dsl/builder/screens_builder.d.ts.map +1 -1
  74. package/dist/presentation/flow/dsl/builder/screens_builder.js +3 -6
  75. package/dist/presentation/flow/dsl/builder/screens_builder.js.map +1 -1
  76. package/dist/presentation/flow/flow_navigation_manager.d.ts +5 -1
  77. package/dist/presentation/flow/flow_navigation_manager.d.ts.map +1 -1
  78. package/dist/presentation/flow/flow_navigation_manager.js +7 -1
  79. package/dist/presentation/flow/flow_navigation_manager.js.map +1 -1
  80. package/dist/presentation/flow/model/instructions_defaults.d.ts.map +1 -1
  81. package/dist/presentation/flow/model/instructions_defaults.js +1 -1
  82. package/dist/presentation/flow/model/instructions_defaults.js.map +1 -1
  83. package/dist/presentation/flow/screen/orchestrated_capture_screen.d.ts.map +1 -1
  84. package/dist/presentation/flow/screen/orchestrated_capture_screen.js +13 -3
  85. package/dist/presentation/flow/screen/orchestrated_capture_screen.js.map +1 -1
  86. package/dist/presentation/flow/screen/orchestrated_consent_screen.d.ts.map +1 -1
  87. package/dist/presentation/flow/screen/orchestrated_consent_screen.js +3 -1
  88. package/dist/presentation/flow/screen/orchestrated_consent_screen.js.map +1 -1
  89. package/dist/presentation/flow/screen/orchestrated_preview_screen.d.ts.map +1 -1
  90. package/dist/presentation/flow/screen/orchestrated_preview_screen.js +0 -8
  91. package/dist/presentation/flow/screen/orchestrated_preview_screen.js.map +1 -1
  92. package/dist/presentation/flow/use_smile_id_flow.d.ts.map +1 -1
  93. package/dist/presentation/flow/use_smile_id_flow.js +10 -4
  94. package/dist/presentation/flow/use_smile_id_flow.js.map +1 -1
  95. package/dist/presentation/flow/validation/flow_validator.d.ts.map +1 -1
  96. package/dist/presentation/flow/validation/flow_validator.js +42 -20
  97. package/dist/presentation/flow/validation/flow_validator.js.map +1 -1
  98. package/dist/presentation/screen/helpers/capture_ui_state.d.ts +6 -6
  99. package/dist/presentation/screen/helpers/capture_ui_state.d.ts.map +1 -1
  100. package/dist/presentation/screen/helpers/capture_ui_state.js +19 -19
  101. package/dist/presentation/screen/helpers/capture_ui_state.js.map +1 -1
  102. package/dist/presentation/screen/helpers/ess_turn_haptics.d.ts +2 -2
  103. package/dist/presentation/screen/helpers/ess_turn_haptics.d.ts.map +1 -1
  104. package/dist/presentation/screen/helpers/ess_turn_haptics.js +6 -6
  105. package/dist/presentation/screen/helpers/ess_turn_haptics.js.map +1 -1
  106. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.d.ts +5 -5
  107. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.d.ts.map +1 -1
  108. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.js +11 -11
  109. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.js.map +1 -1
  110. package/dist/presentation/screen/helpers/use_capture_orientation_lock.d.ts +13 -3
  111. package/dist/presentation/screen/helpers/use_capture_orientation_lock.d.ts.map +1 -1
  112. package/dist/presentation/screen/helpers/use_capture_orientation_lock.js +48 -6
  113. package/dist/presentation/screen/helpers/use_capture_orientation_lock.js.map +1 -1
  114. package/dist/presentation/screen/use_smile_id_capture_screen.d.ts +4 -2
  115. package/dist/presentation/screen/use_smile_id_capture_screen.d.ts.map +1 -1
  116. package/dist/presentation/screen/use_smile_id_capture_screen.js +36 -15
  117. package/dist/presentation/screen/use_smile_id_capture_screen.js.map +1 -1
  118. package/dist/presentation/screen/use_smile_id_consent_screen.d.ts +7 -0
  119. package/dist/presentation/screen/use_smile_id_consent_screen.d.ts.map +1 -1
  120. package/dist/presentation/screen/use_smile_id_consent_screen.js +6 -5
  121. package/dist/presentation/screen/use_smile_id_consent_screen.js.map +1 -1
  122. package/dist/presentation/screen/use_smile_id_document_back_instructions.d.ts.map +1 -1
  123. package/dist/presentation/screen/use_smile_id_document_back_instructions.js +5 -13
  124. package/dist/presentation/screen/use_smile_id_document_back_instructions.js.map +1 -1
  125. package/dist/presentation/screen/use_smile_id_document_preview_screen.js +1 -1
  126. package/dist/presentation/screen/use_smile_id_document_preview_screen.js.map +1 -1
  127. package/dist/presentation/screen/use_smile_id_preview_screen.d.ts +1 -21
  128. package/dist/presentation/screen/use_smile_id_preview_screen.d.ts.map +1 -1
  129. package/dist/presentation/screen/use_smile_id_preview_screen.js +3 -14
  130. package/dist/presentation/screen/use_smile_id_preview_screen.js.map +1 -1
  131. package/dist/presentation/screen/use_smile_id_processing_screen.js +1 -1
  132. package/dist/presentation/screen/use_smile_id_processing_screen.js.map +1 -1
  133. package/dist/presentation/utils/locale_utils.d.ts +10 -4
  134. package/dist/presentation/utils/locale_utils.d.ts.map +1 -1
  135. package/dist/presentation/utils/locale_utils.js +13 -5
  136. package/dist/presentation/utils/locale_utils.js.map +1 -1
  137. package/dist/testing/fake_face_analyzer.d.ts +43 -0
  138. package/dist/testing/fake_face_analyzer.d.ts.map +1 -0
  139. package/dist/testing/fake_face_analyzer.js +52 -0
  140. package/dist/testing/fake_face_analyzer.js.map +1 -0
  141. package/dist/testing/index.d.ts +5 -0
  142. package/dist/testing/index.d.ts.map +1 -0
  143. package/dist/testing/index.js +7 -0
  144. package/dist/testing/index.js.map +1 -0
  145. package/dist/testing/scenario_expectations.d.ts +28 -0
  146. package/dist/testing/scenario_expectations.d.ts.map +1 -0
  147. package/dist/testing/scenario_expectations.js +65 -0
  148. package/dist/testing/scenario_expectations.js.map +1 -0
  149. package/dist/testing/scenario_loader.d.ts +11 -0
  150. package/dist/testing/scenario_loader.d.ts.map +1 -0
  151. package/dist/testing/scenario_loader.js +64 -0
  152. package/dist/testing/scenario_loader.js.map +1 -0
  153. package/dist/testing/scripted_scan_results.d.ts +17 -0
  154. package/dist/testing/scripted_scan_results.d.ts.map +1 -0
  155. package/dist/testing/scripted_scan_results.js +52 -0
  156. package/dist/testing/scripted_scan_results.js.map +1 -0
  157. package/package.json +11 -4
  158. package/src/__tests__/dsl.test.ts +51 -1
  159. package/src/__tests__/setup_react_native.ts +17 -2
  160. package/src/__tests__/snapshots/__snapshots__/screens_structural.test.tsx.snap +5261 -0
  161. package/src/__tests__/snapshots/screens_structural.test.tsx +230 -0
  162. package/src/camera/__tests__/use_smile_id_camera_system.test.ts +103 -0
  163. package/src/core/exception/__tests__/builder_validation_exceptions.test.ts +67 -0
  164. package/src/core/exception/agent_mode_with_enhanced_liveness_exception.ts +20 -0
  165. package/src/core/exception/capture_validation_exceptions.ts +1 -0
  166. package/src/core/exception/index.ts +1 -0
  167. package/src/core/exception/use_smile_id_error_code.ts +1 -0
  168. package/src/core/models/__tests__/document_type.test.ts +2 -2
  169. package/src/core/models/__tests__/flow_models.test.ts +26 -0
  170. package/src/core/models/config.ts +22 -0
  171. package/src/core/models/document_type.ts +2 -2
  172. package/src/core/models/global_privacy_control.ts +45 -0
  173. package/src/core/models/network_config.ts +2 -2
  174. package/src/data/api/__tests__/smile_id_api.test.ts +1 -1
  175. package/src/data/api/interceptors/__tests__/gzip_interceptor.test.ts +83 -0
  176. package/src/data/api/interceptors/smile_auth_interceptor.ts +3 -18
  177. package/src/data/compression/__tests__/payload_compressor_real_jpeg.test.ts +6 -2
  178. package/src/data/dsl/builder/__tests__/config_builders.test.ts +62 -0
  179. package/src/data/dsl/builder/retry_config_builder.ts +6 -6
  180. package/src/data/factory/__tests__/retry_gzip_mac_composition.test.ts +163 -0
  181. package/src/data/factory/__tests__/signed_job_submission_headers.test.ts +1 -1
  182. package/src/data/repository/__tests__/network_retries.test.ts +1 -1
  183. package/src/data/repository/__tests__/use_smile_id_repository.test.ts +1 -1
  184. package/src/data/security/__tests__/request_canonicalizer.test.ts +132 -0
  185. package/src/data/smile_id_urls.ts +1 -1
  186. package/src/data/util/__tests__/retry_util.test.ts +25 -0
  187. package/src/data/util/retry_util.ts +9 -1
  188. package/src/index.ts +6 -4
  189. package/src/metadata/__tests__/metadata_configuration_mappers.test.ts +39 -0
  190. package/src/metadata/__tests__/metadata_configuration_objects.test.ts +2 -1
  191. package/src/metadata/__tests__/metadata_device_context.test.ts +3 -3
  192. package/src/metadata/__tests__/metadata_device_os_android.test.ts +18 -0
  193. package/src/metadata/__tests__/metadata_golden_fixture.json +1 -1
  194. package/src/metadata/__tests__/metadata_payload_golden.test.ts +4 -4
  195. package/src/metadata/__tests__/use_smile_id_metadata.test.ts +1 -1
  196. package/src/metadata/builder_configuration.ts +6 -0
  197. package/src/metadata/metadata_configuration_mappers.ts +21 -7
  198. package/src/metadata/use_smile_id_metadata_factory.ts +17 -6
  199. package/src/presentation/assets/si_document_green_book_hero_animation.json +1 -4668
  200. package/src/presentation/assets/si_document_hero_animation.json +1 -4799
  201. package/src/presentation/assets/si_document_hero_back_animation.json +1 -2039
  202. package/src/presentation/assets/si_document_passport_hero_animation.json +1 -14491
  203. package/src/presentation/assets/si_ess_all_directions_hero_animation.json +1 -0
  204. package/src/presentation/assets/si_ess_animation_all_directions.json +1 -6589
  205. package/src/presentation/assets/si_selfie_hero_animation.json +1 -1439
  206. package/src/presentation/assets/si_smile_to_capture_animation.json +1 -1439
  207. package/src/presentation/components/__tests__/use_smile_id_enhanced_smart_selfie_clip.test.tsx +9 -4
  208. package/src/presentation/components/__tests__/use_smile_id_instructions_hero.test.tsx +44 -0
  209. package/src/presentation/components/use_smile_id_enhanced_smart_selfie_clip.tsx +9 -3
  210. package/src/presentation/components/use_smile_id_instructions_hero.tsx +10 -0
  211. package/src/presentation/flow/__tests__/document_flow_navigation.test.ts +3 -3
  212. package/src/presentation/flow/__tests__/flow_navigation_manager.test.ts +49 -0
  213. package/src/presentation/flow/dsl/builder/__tests__/config_builder.test.ts +25 -0
  214. package/src/presentation/flow/dsl/builder/config_builder.ts +21 -0
  215. package/src/presentation/flow/dsl/builder/screens_builder.ts +3 -6
  216. package/src/presentation/flow/flow_navigation_manager.ts +6 -1
  217. package/src/presentation/flow/model/instructions_defaults.ts +1 -1
  218. package/src/presentation/flow/screen/orchestrated_capture_screen.tsx +18 -3
  219. package/src/presentation/flow/screen/orchestrated_consent_screen.tsx +5 -0
  220. package/src/presentation/flow/screen/orchestrated_preview_screen.tsx +0 -12
  221. package/src/presentation/flow/use_smile_id_flow.tsx +11 -4
  222. package/src/presentation/flow/validation/__tests__/flow_validator.test.ts +61 -1
  223. package/src/presentation/flow/validation/flow_validator.ts +55 -26
  224. package/src/presentation/screen/__tests__/capture_ui_state.test.ts +66 -47
  225. package/src/presentation/screen/__tests__/ess_turn_haptics.test.ts +15 -11
  226. package/src/presentation/screen/__tests__/scan_state_to_overlay_state.test.ts +47 -27
  227. package/src/presentation/screen/__tests__/use_smile_id_capture_screen.test.tsx +176 -14
  228. package/src/presentation/screen/__tests__/use_smile_id_document_back_instructions.test.tsx +1 -1
  229. package/src/presentation/screen/__tests__/use_smile_id_preview_screen.test.tsx +10 -40
  230. package/src/presentation/screen/helpers/__tests__/use_capture_orientation_lock.test.tsx +212 -0
  231. package/src/presentation/screen/helpers/capture_ui_state.ts +27 -27
  232. package/src/presentation/screen/helpers/ess_turn_haptics.ts +9 -9
  233. package/src/presentation/screen/helpers/scan_state_to_overlay_state.ts +13 -13
  234. package/src/presentation/screen/helpers/use_capture_orientation_lock.ts +69 -7
  235. package/src/presentation/screen/use_smile_id_capture_screen.tsx +47 -19
  236. package/src/presentation/screen/use_smile_id_consent_screen.tsx +16 -5
  237. package/src/presentation/screen/use_smile_id_document_back_instructions.tsx +14 -23
  238. package/src/presentation/screen/use_smile_id_document_preview_screen.tsx +1 -1
  239. package/src/presentation/screen/use_smile_id_preview_screen.tsx +13 -45
  240. package/src/presentation/screen/use_smile_id_processing_screen.tsx +1 -1
  241. package/src/presentation/utils/__tests__/locale_utils.test.ts +46 -5
  242. package/src/presentation/utils/locale_utils.ts +13 -4
  243. package/src/testing/__tests__/fake_face_analyzer.test.ts +42 -0
  244. package/src/testing/__tests__/scenario_expectations.test.ts +110 -0
  245. package/src/testing/__tests__/scenario_loader.test.ts +64 -0
  246. package/src/testing/fake_face_analyzer.ts +82 -0
  247. package/src/testing/index.ts +15 -0
  248. package/src/testing/scenario_expectations.ts +87 -0
  249. package/src/testing/scenario_loader.ts +83 -0
  250. package/src/testing/scripted_scan_results.ts +66 -0
  251. package/dist/presentation/components/use_smile_id_face_silhouette_overlay.d.ts +0 -15
  252. package/dist/presentation/components/use_smile_id_face_silhouette_overlay.d.ts.map +0 -1
  253. package/dist/presentation/components/use_smile_id_face_silhouette_overlay.js +0 -19
  254. package/dist/presentation/components/use_smile_id_face_silhouette_overlay.js.map +0 -1
  255. 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
- ScanState,
3
+ UseSmileIDScanState,
4
4
  } from "@smileid/usesmileid_bridge";
5
- import { ScanStateType } from "@smileid/usesmileid_bridge";
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 `ScanState`. */
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: ScanState,
132
- next: ScanState,
131
+ previous: UseSmileIDScanState,
132
+ next: UseSmileIDScanState,
133
133
  ): boolean {
134
134
  if (previous.type === next.type) return false;
135
- if (previous.type === ScanStateType.Unsatisfied) return true;
135
+ if (previous.type === UseSmileIDScanStateType.Unsatisfied) return true;
136
136
  return (
137
- (previous.type === ScanStateType.Timeout ||
138
- previous.type === ScanStateType.Finished) &&
139
- next.type === ScanStateType.Initial
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: 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: 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: ScanState,
170
+ scanState: UseSmileIDScanState,
171
171
  enableEnhancedLiveness: boolean,
172
172
  ): UseSmileIDCaptureUiState {
173
173
  switch (scanState.type) {
174
- case ScanStateType.Initial:
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 ScanStateType.Found: {
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 ScanStateType.Satisfied:
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 ScanStateType.Unsatisfied:
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 ScanStateType.Finished:
212
+ case UseSmileIDScanStateType.Finished:
213
213
  return scannedUiState(enableEnhancedLiveness);
214
214
 
215
- case ScanStateType.Timeout:
215
+ case UseSmileIDScanStateType.Timeout:
216
216
  return uiState(UseSmileIDOverlayState.IDLE, "si_capture_timeout", 0);
217
217
 
218
- case ScanStateType.HeadTurn: {
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: ScanState,
246
+ scanState: UseSmileIDScanState,
247
247
  ): UseSmileIDCaptureUiState {
248
248
  switch (scanState.type) {
249
- case ScanStateType.Initial:
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 ScanStateType.HeadTurn:
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 ScanStateType.Found: {
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 ScanStateType.Satisfied:
278
+ case UseSmileIDScanStateType.Satisfied:
279
279
  return scannedDocumentUiState();
280
280
 
281
- case ScanStateType.Unsatisfied:
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 ScanStateType.Finished:
288
+ case UseSmileIDScanStateType.Finished:
289
289
  return scannedDocumentUiState();
290
290
 
291
- case ScanStateType.Timeout:
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 { ScanState } from "@smileid/usesmileid_bridge";
3
- import { ScanStateType } from "@smileid/usesmileid_bridge";
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: ScanState,
24
- next: ScanState,
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 !== ScanStateType.HeadTurn) return null;
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 === ScanStateType.HeadTurn &&
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 === ScanStateType.Found ||
43
- next.type === ScanStateType.Satisfied ||
44
- next.type === ScanStateType.Finished
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 { ScanState } from "@smileid/usesmileid_bridge";
2
- import { ScanStateType } from "@smileid/usesmileid_bridge";
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 `ScanState` into the SDK's visual `OverlayState`.
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 === ScanStateType.Found`) and `state.reason` (when
13
- * `type === ScanStateType.Unsatisfied`) to pick the fine-grained
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: ScanState,
23
+ scanState: UseSmileIDScanState,
24
24
  captureType: CaptureType,
25
25
  ): UseSmileIDOverlayState {
26
26
  switch (scanState.type) {
27
- case ScanStateType.Initial:
27
+ case UseSmileIDScanStateType.Initial:
28
28
  return UseSmileIDOverlayState.INSTRUCTION;
29
29
 
30
- case ScanStateType.Found:
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 ScanStateType.Satisfied:
41
+ case UseSmileIDScanStateType.Satisfied:
42
42
  return UseSmileIDOverlayState.READY;
43
43
 
44
- case ScanStateType.Unsatisfied:
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 ScanStateType.Finished:
53
+ case UseSmileIDScanStateType.Finished:
54
54
  return UseSmileIDOverlayState.CAPTURING;
55
55
 
56
- case ScanStateType.HeadTurn:
56
+ case UseSmileIDScanStateType.HeadTurn:
57
57
  return UseSmileIDOverlayState.HEAD_TURN;
58
58
 
59
- case ScanStateType.Timeout:
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(ScreenOrientation.OrientationLock.PORTRAIT_UP);
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
- UseSmileIDCrashReporting.captureException(
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 { CaptureReadyEvent, ScanState } from "@smileid/usesmileid_bridge";
31
+ import type {
32
+ CaptureReadyEvent,
33
+ UseSmileIDScanState,
34
+ } from "@smileid/usesmileid_bridge";
32
35
  import {
33
36
  CaptureReadyEventKind,
34
37
  DocumentSide,
35
- ScanStateType,
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: ScanState) => void;
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 ? "front" : "back";
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<ScanState>({
206
- type: ScanStateType.Initial,
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<ScanState | null>(null);
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 = result.state.type === ScanStateType.Unsatisfied;
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 === ScanStateType.Unsatisfied) {
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 === ScanStateType.Finished) {
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 === ScanStateType.Timeout) {
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 === ScanStateType.Initial && !streaming;
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
- streaming &&
693
- (captureMode?.kind === "manualCapture" ||
694
- (captureMode?.kind === "autoCaptureWithManualFallback" &&
695
- manualShutterRevealed));
696
- const galleryAvailable = isDocumentCapture && streaming && allowGalleryUpload;
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
- // Locale-aware default: ticked outside the EU (`true`), unticked
111
- // inside (`false`) to honour GDPR opt-in. `getDefaultConsentState()`
112
- // reads the resolved BCP-47 locale and checks the country segment
113
- // against the EU member-state set.
114
- const [consented, setConsented] = useState(() => getDefaultConsentState());
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 { Pressable, StyleSheet, Text, View } from "react-native";
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.large,
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
- <Pressable
102
- onPress={onSkip}
103
- accessibilityRole="button"
105
+ <UseSmileIDButton
106
+ label={localizations.siDocumentSkipBack}
107
+ onClick={onSkip}
108
+ variant="secondary"
109
+ theme={theme}
104
110
  testID="si_document_skip_back_button"
105
- style={styles.skip}
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
  });