@smileid/usesmileid 12.2.0 → 12.3.0
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 +5 -1
- package/README.md +57 -18
- package/dist/camera/bounding_box_metering_region.d.ts +10 -0
- package/dist/camera/bounding_box_metering_region.js +1 -0
- package/dist/camera/camera_defaults.d.ts +0 -2
- package/dist/camera/camera_defaults.js +1 -1
- package/dist/camera/document_image_pipeline.d.ts +9 -0
- package/dist/camera/document_image_pipeline.js +1 -1
- package/dist/camera/imported_document_folder.d.ts +15 -0
- package/dist/camera/imported_document_folder.js +1 -0
- package/dist/camera/kamera_config_factory.d.ts +11 -1
- package/dist/camera/kamera_config_factory.js +1 -1
- package/dist/camera/use_smile_id_frame_stream.js +1 -1
- package/dist/camera/use_smile_id_kamera_camera_system.d.ts +46 -2
- package/dist/camera/use_smile_id_kamera_camera_system.js +1 -1
- package/dist/core/exception/use_smile_id_error_code.js +1 -1
- package/dist/core/exception/use_smile_id_network_exception.d.ts +10 -3
- package/dist/core/exception/use_smile_id_network_exception.js +1 -1
- package/dist/core/is_development_build.d.ts +6 -0
- package/dist/core/is_development_build.js +1 -0
- package/dist/core/models/config.d.ts +6 -0
- package/dist/core/models/ml_config.d.ts +1 -1
- package/dist/core/models/network_config.d.ts +4 -1
- package/dist/core/models/params.d.ts +8 -6
- package/dist/core/models/result.d.ts +11 -0
- package/dist/core/models/result.js +1 -1
- package/dist/core/perf/capture_perf_trace.d.ts +90 -0
- package/dist/core/perf/capture_perf_trace.js +1 -0
- package/dist/data/api/interceptors/logging_interceptor.js +1 -1
- package/dist/data/api/interceptors/smile_auth_interceptor.js +1 -1
- package/dist/data/api/request_capture.d.ts +16 -0
- package/dist/data/api/request_capture.js +1 -0
- package/dist/data/api/smile_id_api.d.ts +1 -1
- package/dist/data/api/smile_id_api.js +1 -1
- package/dist/data/factory/networking_factory.d.ts +3 -1
- package/dist/data/factory/networking_factory.js +1 -1
- package/dist/data/model/captured_job.d.ts +49 -0
- package/dist/data/model/captured_job.js +1 -0
- package/dist/data/model/job_request.d.ts +1 -1
- package/dist/data/model/sealed_job_request.d.ts +15 -0
- package/dist/data/model/sealed_job_request.js +1 -0
- package/dist/data/model/token_payload.d.ts +5 -7
- package/dist/data/model/token_payload.js +1 -1
- package/dist/data/model/user_details_payload.d.ts +6 -4
- package/dist/data/repository/use_smile_id_repository.d.ts +7 -0
- package/dist/data/repository/use_smile_id_repository_impl.d.ts +29 -2
- package/dist/data/repository/use_smile_id_repository_impl.js +1 -1
- package/dist/data/security/job_package_crypto.d.ts +41 -0
- package/dist/data/security/job_package_crypto.js +1 -0
- package/dist/data/security/job_package_sealer.d.ts +35 -0
- package/dist/data/security/job_package_sealer.js +1 -0
- package/dist/data/security/signing_crypto.d.ts +34 -0
- package/dist/data/security/signing_crypto.js +1 -1
- package/dist/data/util/retry_util.d.ts +9 -6
- package/dist/data/util/retry_util.js +1 -1
- package/dist/debug/selfie_preview_debug_geometry.d.ts +13 -13
- package/dist/debug/selfie_preview_debug_geometry.js +1 -1
- package/dist/debug/use_smile_id_debug.d.ts +25 -2
- package/dist/debug/use_smile_id_debug.js +1 -1
- package/dist/debug/use_smile_id_document_preview_debug_overlay.js +1 -1
- package/dist/debug/use_smile_id_selfie_preview_debug_overlay.d.ts +5 -1
- package/dist/debug/use_smile_id_selfie_preview_debug_overlay.js +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +1 -1
- package/dist/l10n/strings_en.d.ts +3 -3
- package/dist/l10n/strings_en.js +2 -2
- package/dist/l10n/use_smile_id_localizations.d.ts +5 -7
- package/dist/l10n/use_smile_id_localizations.js +1 -1
- package/dist/metadata/metadata_configuration_mappers.d.ts +3 -5
- package/dist/metadata/metadata_configuration_mappers.js +1 -1
- package/dist/metadata/use_smile_id_metadata_factory.js +1 -1
- package/dist/presentation/assets/si_dark_lighting_animation.json +1 -927
- package/dist/presentation/assets/si_device_orientation_animation.json +1 -850
- package/dist/presentation/assets/si_document_green_book_hero_animation.json +1 -4740
- package/dist/presentation/assets/si_document_hero_animation.json +1 -4870
- package/dist/presentation/assets/si_document_hero_back_animation.json +1 -2122
- package/dist/presentation/assets/si_document_passport_hero_animation.json +1 -14540
- package/dist/presentation/assets/si_ess_all_directions_hero_animation.json +1 -5906
- package/dist/presentation/assets/si_ess_animation_all_directions.json +1 -5906
- package/dist/presentation/assets/si_selfie_hero_animation.json +1 -1468
- package/dist/presentation/assets/si_smile_to_capture_animation.json +1 -1412
- package/dist/presentation/components/helpers/captured_image_uri.d.ts +11 -0
- package/dist/presentation/components/helpers/captured_image_uri.js +1 -0
- package/dist/presentation/components/helpers/captured_oval_region.d.ts +40 -0
- package/dist/presentation/components/helpers/captured_oval_region.js +1 -0
- package/dist/presentation/components/helpers/document_interior_crop.d.ts +13 -2
- package/dist/presentation/components/helpers/document_interior_crop.js +1 -1
- package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.d.ts +35 -29
- package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.js +1 -1
- package/dist/presentation/components/helpers/selfie_interior_crop.js +1 -1
- package/dist/presentation/components/use_smile_id_capture_content.d.ts +5 -2
- package/dist/presentation/components/use_smile_id_capture_content.js +1 -1
- package/dist/presentation/components/use_smile_id_captured_document_image.d.ts +8 -4
- package/dist/presentation/components/use_smile_id_captured_document_image.js +1 -1
- package/dist/presentation/components/use_smile_id_captured_image_oval.d.ts +6 -0
- package/dist/presentation/components/use_smile_id_captured_image_oval.js +1 -1
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts +17 -7
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js +1 -1
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_direction_animation.d.ts +13 -14
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_direction_animation.js +1 -1
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_full_animation.js +1 -1
- package/dist/presentation/components/use_smile_id_face_oval_image.js +1 -1
- package/dist/presentation/flow/analytics/unsatisfied_reasons.d.ts +14 -0
- package/dist/presentation/flow/analytics/unsatisfied_reasons.js +1 -0
- package/dist/presentation/flow/analytics/use_smile_id_analytics_event.d.ts +8 -7
- package/dist/presentation/flow/analytics/use_smile_id_analytics_event.js +1 -1
- package/dist/presentation/flow/analytics/use_smile_id_analytics_event_kind.d.ts +9 -0
- package/dist/presentation/flow/analytics/use_smile_id_analytics_event_kind.js +1 -1
- package/dist/presentation/flow/config/flow_configuration_camera.d.ts +9 -0
- package/dist/presentation/flow/config/flow_configuration_camera.js +1 -0
- package/dist/presentation/flow/dsl/builder/config_builder.d.ts +6 -0
- package/dist/presentation/flow/dsl/builder/config_builder.js +1 -1
- package/dist/presentation/flow/dsl/builder/ml_builder.d.ts +1 -7
- package/dist/presentation/flow/dsl/builder/ml_builder.js +1 -1
- package/dist/presentation/flow/dsl/use_smile_id_flow_builder.d.ts +0 -9
- package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js +1 -1
- package/dist/presentation/flow/flow_navigation_manager.d.ts +39 -70
- package/dist/presentation/flow/flow_navigation_manager.js +1 -1
- package/dist/presentation/flow/flow_navigation_manager_analytics.d.ts +23 -0
- package/dist/presentation/flow/flow_navigation_manager_analytics.js +1 -0
- package/dist/presentation/flow/flow_navigation_manager_documents.d.ts +18 -0
- package/dist/presentation/flow/flow_navigation_manager_documents.js +1 -0
- package/dist/presentation/flow/flow_navigation_manager_job_request.d.ts +7 -0
- package/dist/presentation/flow/flow_navigation_manager_job_request.js +1 -0
- package/dist/presentation/flow/flow_navigation_manager_routing.d.ts +12 -0
- package/dist/presentation/flow/flow_navigation_manager_routing.js +1 -0
- package/dist/presentation/flow/flow_navigation_manager_submission.d.ts +17 -0
- package/dist/presentation/flow/flow_navigation_manager_submission.js +1 -0
- package/dist/presentation/flow/job_submission_state.d.ts +4 -0
- package/dist/presentation/flow/screen/helpers/face_session_config.d.ts +1 -1
- package/dist/presentation/flow/screen/helpers/failure_message.js +1 -1
- package/dist/presentation/flow/screen/orchestrated_capture_screen.js +1 -1
- package/dist/presentation/flow/screen/orchestrated_instructions_screen.js +1 -1
- package/dist/presentation/flow/screen/orchestrated_preview_screen.js +1 -1
- package/dist/presentation/flow/screen/orchestrated_processing_screen.js +1 -1
- package/dist/presentation/flow/use_smile_id_flow.js +1 -1
- package/dist/presentation/flow/validation/flow_validator.d.ts +0 -19
- package/dist/presentation/flow/validation/flow_validator.js +1 -1
- package/dist/presentation/flow/validation/job_type_validator.js +1 -1
- package/dist/presentation/screen/helpers/capture_screen_helpers.d.ts +0 -12
- package/dist/presentation/screen/helpers/capture_screen_helpers.js +1 -1
- package/dist/presentation/screen/helpers/capture_ui_state.d.ts +33 -6
- package/dist/presentation/screen/helpers/capture_ui_state.js +1 -1
- package/dist/presentation/screen/helpers/dropped_frames.d.ts +6 -0
- package/dist/presentation/screen/helpers/dropped_frames.js +1 -0
- package/dist/presentation/screen/helpers/ess_turn_haptics.d.ts +2 -0
- package/dist/presentation/screen/helpers/ess_turn_haptics.js +1 -1
- package/dist/presentation/screen/helpers/instructions_screen_helpers.d.ts +5 -1
- package/dist/presentation/screen/helpers/instructions_screen_helpers.js +1 -1
- package/dist/presentation/screen/helpers/use_capture_orientation_lock.d.ts +23 -1
- package/dist/presentation/screen/helpers/use_capture_orientation_lock.js +1 -1
- package/dist/presentation/screen/helpers/use_debounced_feedback_key.d.ts +1 -1
- package/dist/presentation/screen/helpers/use_debounced_feedback_key.js +1 -1
- package/dist/presentation/screen/use_smile_id_capture_screen.d.ts +4 -1
- package/dist/presentation/screen/use_smile_id_capture_screen.js +1 -1
- package/dist/presentation/screen/use_smile_id_consent_screen.js +1 -1
- package/dist/presentation/screen/use_smile_id_preview_screen.d.ts +3 -0
- package/dist/presentation/screen/use_smile_id_preview_screen.js +1 -1
- package/dist/presentation/screen/use_smile_id_processing_screen.d.ts +3 -0
- package/dist/presentation/screen/use_smile_id_processing_screen.js +1 -1
- package/dist/presentation/utils/locale_utils.d.ts +3 -6
- package/dist/presentation/utils/locale_utils.js +1 -1
- package/dist/testing/scenario_expectations.d.ts +7 -2
- package/dist/testing/scenario_expectations.js +1 -1
- package/dist/testing/scripted_scan_results.d.ts +2 -1
- package/dist/testing/scripted_scan_results.js +1 -1
- package/package.json +7 -7
- package/dist/presentation/flow/validation/user_details_validator.d.ts +0 -8
- package/dist/presentation/flow/validation/user_details_validator.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{jsx as r}from"react/jsx-runtime";import{useEffect as a}from"react";import{DocumentSide as d}from"@smileid/usesmileid_bridge";import{UseSmileIDProcessingScreen as m}from"../../screen/use_smile_id_processing_screen";import{flowShowAttribution as f}from"../config/attribution_resolver";import{UseSmileIDOrchestratedScreenHost as g}from"./use_smile_id_orchestrated_screen_host";import{ProcessingCancelledException as
|
|
1
|
+
"use strict";import{jsx as r}from"react/jsx-runtime";import{useEffect as a}from"react";import{DocumentSide as d}from"@smileid/usesmileid_bridge";import{UseSmileIDProcessingScreen as m}from"../../screen/use_smile_id_processing_screen";import{flowShowAttribution as f}from"../config/attribution_resolver";import{UseSmileIDOrchestratedScreenHost as g}from"./use_smile_id_orchestrated_screen_host";import{ProcessingCancelledException as p}from"../../../core/exception";import{alwaysEnabledSlot as i}from"./helpers/always_enabled_slot";import{resolveFailureMessage as l}from"./helpers/failure_message";import{useUseSmileIDLocalizations as S}from"../../../l10n/use_smile_id_localizations_provider";import{JobType as b,requiresDocument as c,requiresSelfie as P}from"../../../core/models/job_type";export function OrchestratedProcessingScreen({configuration:n}){return r(g,{content:({navigationManager:e,onNavigateNext:o})=>r(h,{processingConfig:n,navigationManager:e,onContinue:o})})}function h(n){const{navigationManager:e}=n;a(()=>{e.beginSubmissionIfNeeded()},[e]);const o=e.configuration.network?.config?.jobType??b.unknown,t=c(o)?e.captured.frontDocumentPath:void 0,s=e.documentFrameAspectRatio,u=S();return r(m,{state:B(e.jobSubmissionState,u),showFaceOval:P(o)||c(o),...e.selfieCaptureGeometry?{selfieCaptureGeometry:e.selfieCaptureGeometry}:{},showProgressPercentage:n.processingConfig.showProgressPercentage,showAttribution:f(e.configuration),...t!==void 0?{documentImageUri:t,documentBorderFraction:e.documentBorderFraction(d.Front)}:{},...t!==void 0&&s!==void 0?{documentAspectRatio:s}:{},...e.captured.selfiePath!==void 0?{capturedSelfiePath:e.captured.selfiePath}:{},...y(n.processingConfig),onContinue:n.onContinue,onRetry:()=>e.retrySubmission(),onExit:()=>e.exit({kind:"failed",error:w(e.jobSubmissionState)})})}function y(n){const e=i(n.continueButton),o=i(n.retryButton),t=i(n.exitButton);return{...e!==void 0?{continueButton:e}:{},...o!==void 0?{retryButton:o}:{},...t!==void 0?{exitButton:t}:{}}}function w(n){return n.kind==="failure"?n.error:new p}function B(n,e){switch(n.kind){case"notAttempted":case"loading":case"skipped":return{kind:"loading"};case"success":return{kind:"success",message:n.response.message};case"failure":return{kind:"failed",errorMessage:l(n.error,e)}}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{jsx as
|
|
1
|
+
"use strict";import{jsx as i,jsxs as H}from"react/jsx-runtime";import{useEffect as s,useMemo as R,useReducer as j,useRef as m,useState as J}from"react";import{AppState as Y,Text as F,useColorScheme as q,View as P}from"react-native";import{prewarmCameraIfNeeded as Q}from"./config/flow_configuration_camera";import{FlowNavigationManager as X}from"./flow_navigation_manager";import{FlowNavigationProvider as Z}from"./flow_navigation_context";import{ScreenRenderer as ee}from"./graph/screen_renderer";import{ScreenTransition as re}from"./graph/screen_transition";import{defaultDarkTheme as te,defaultLightTheme as oe}from"../theme/theme";import{UseSmileIDThemeProvider as ne}from"../theme/theme_context";import{useUseSmileIDFonts as ie}from"../theme/use_use_smile_id_fonts";import{UseSmileIDCrashReporting as c}from"@smileid/usesmileid_bridge";import{generateSessionId as se}from"../../metadata/use_smile_id_metadata_factory";import{UseSmileIDLocalizationsProvider as ce}from"../../l10n/use_smile_id_localizations_provider";import{UseSmileIDLocalizations as k}from"../../l10n/use_smile_id_localizations";import{cancelled as _,captured as ae,failure as I,success as le}from"../../core/models/result";import{ValidationErrorScreen as de}from"./screen/validation_error_screen";import{BuilderValidationException as A}from"../../core/exception/builder_validation_exception";import{UseSmileIDBuilderException as me}from"../../core/exception/use_smile_id_builder_exception";import{ValidationState as ue}from"./validation/validation_state";import{makeNetworkingFactory as N}from"../../data/factory/networking_factory";import{UseSmileIDRepositoryImpl as pe}from"../../data/repository/use_smile_id_repository_impl";import{JobPackageSealer as fe}from"../../data/security/job_package_sealer";import{EncryptionSecretProvider as he,SigningSecretProvider as Se}from"@smileid/usesmileid_bridge/internal";import{RetryStrategy as ge}from"../../core/models/network_config";const ye=Object.freeze({maxAttempts:1,strategy:ge.none,initialDelay:1,multiplier:2,maxDelay:32,jitter:.2,fixedDelay:1});export function FlowInvalidDispatcher({result:e,onResult:y,enableDebugMode:f}){const o=m(y);o.current=y;const a=m(e.validation);return a.current=e.validation,s(()=>{const v=new A(a.current);try{o.current(I(v))}catch(b){c.captureException(b)}},[]),f?i(de,{validationState:e.validation}):null}const ve=1e3;export function FlowSuccessRenderer({configuration:e}){const f=q()==="dark",o=e.theme?f?e.theme.dark:e.theme.light:f?te:oe,a=ie(),[v,b]=J(!1);s(()=>{if(a)return;const r=setTimeout(()=>b(!0),ve);return()=>clearTimeout(r)},[a]);const D=e.config?.enableCrashReporting!==!1,T=e.network?.config?.partnerConfig?.useSandbox===!0,E=m(null);E.current===null&&(E.current=se()),s(()=>(D&&(c.disable(),c.enable({useSandbox:T}),c.setUserId(E.current),c.captureBreadcrumb({category:"flow.navigation",message:"flow mounted"})),()=>{c.disable()}),[D,T]);const h=m(e.onResult);h.current=e.onResult;const u=m(!1),[,z]=j(r=>r+1,0),l=R(()=>{const r=e.network;if(r?.config?.partnerConfig)try{let t;const n=()=>t??=Se.signingSecret(),{api:x,metadata:W}=N(r,n),$=e.config?.skipApiSubmission?{api:N(r,n,{capturing:!0}).api,sealer:new fe(()=>he.encryptionSecret())}:void 0,G=r.retry??ye;return{repository:new pe(x,G,$),metadataProvider:W}}catch(t){if(t instanceof me)return{configError:t};throw t}},[e.network,e.config?.skipApiSubmission]),p=l!==void 0&&"configError"in l?l.configError:void 0,S=l!==void 0&&"repository"in l?l:void 0;s(()=>{if(p===void 0||u.current)return;u.current=!0;const r=ue.invalid([p]),t=r.valid?p:new A(r);try{h.current(I(t))}catch(n){c.captureException(n)}},[p]);const d=R(()=>{const r=t=>{u.current=!0;const n=h.current;try{t.kind==="completed"?n(le(t.job)):t.kind==="cancelled"?n(_()):t.kind==="captured"?n(ae(t.job)):n(I(t.error))}catch(x){c.captureException(x)}};return new X({configuration:e,onExit:r,...S?{repository:S.repository,metadataProvider:S.metadataProvider}:{}})},[e,S]);s(()=>{Q(e)},[e]),s(()=>{const r=d.subscribe(z);return()=>{r(),d.dispose()}},[d]),s(()=>()=>{u.current||(u.current=!0,h.current(_()))},[]);const[M,O]=j(r=>r+1,0);s(()=>{const r=Y.addEventListener("change",t=>{t==="active"&&O()});return()=>r.remove()},[]);const V=k.activeLocaleKey,U=R(()=>e.localizations?e.localizations:k.hasRegisteredLocales()?k.forActiveLocale():null,[e.localizations,V,M]),w=d.currentScreen,g=d.currentDepth,L=m(g),B=g>=L.current;L.current=g;const K=w?i(Z,{manager:d,children:i(re,{forward:B,children:i(ee,{step:w})},`${g}:${w.kind}`)}):H(P,{style:{flex:1,alignItems:"center",justifyContent:"center",backgroundColor:o.colors.background,padding:o.spacing.large},children:[i(F,{style:{...o.typography.pageHeading,color:o.colors.titleText},children:"UseSmileID"}),i(F,{style:{...o.typography.body,color:o.colors.cardText},children:"No screens configured \u2014 call `b.screens((screens) => screens.instructions(); ...)`"})]}),C=i(ne,{value:o,children:a||v?K:i(P,{style:{flex:1,backgroundColor:o.colors.background},testID:"si_flow_font_gate"})});return p!==void 0?null:U?i(ce,{value:U,children:C}):C}
|
|
@@ -89,23 +89,4 @@ export declare function runResidencyDocumentVerificationParamsValidation(params:
|
|
|
89
89
|
* pulled from their own storage before assigning it to the builder.
|
|
90
90
|
*/
|
|
91
91
|
export declare function runConsentValidation(consent: ConsentInformation): ValidationIssue[];
|
|
92
|
-
/**
|
|
93
|
-
* Validate a top-level `UserDetails` payload. Required fields use the
|
|
94
|
-
* dotted path (`userDetails.givenNames`) so partner regex-matchers
|
|
95
|
-
* keep working unchanged after the consolidation away from nesting
|
|
96
|
-
* `userDetails` inside the KYC param shapes.
|
|
97
|
-
*
|
|
98
|
-
* Each field requirement is evaluated against the union of builder
|
|
99
|
-
* input and token bindings: a field the token binds (see
|
|
100
|
-
* `TokenPayload`) never has to be supplied locally — the server
|
|
101
|
-
* injects it, and the SDK drops the local value from the wire. With
|
|
102
|
-
* no `tokenPayload` the behavior is exactly the pre-token rule set.
|
|
103
|
-
*
|
|
104
|
-
* Exposed via `b.validateUserDetails(payload)` so partners can
|
|
105
|
-
* pre-flight a payload before assigning to `b.userDetails`, mirroring
|
|
106
|
-
* the existing `b.validateConsent` / `b.validateEnhancedKYCParams`
|
|
107
|
-
* pattern (the pre-flight helper always validates strictly, without
|
|
108
|
-
* token context).
|
|
109
|
-
*/
|
|
110
|
-
export declare function runUserDetailsValidation(userDetails: UserDetails, tokenPayload?: TokenPayload): ValidationIssue[];
|
|
111
92
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{CaptureType as d}from"../../../core/models/ml_config";import{JobType as g,jobTypeFromValue as w}from"../../../core/models/job_type";import{InvalidConsentConfigException as c,InvalidConsentInformationException as m}from"../../../core/exception/consent_validation_exceptions";import{InvalidFieldValueException as s,MissingJobTypeException as P,MissingMLConfigurationBlockException as k,MissingNetworkConfigurationBlockException as b}from"../../../core/exception/job_validation_exceptions";import{AgentModeWithEnhancedLivenessException as C,InvalidDocumentCaptureConfigException as u,InvalidMissingCaptureTypeException as I}from"../../../core/exception/capture_validation_exceptions";import{EmptyScreensBlockException as
|
|
1
|
+
"use strict";import{CaptureType as d}from"../../../core/models/ml_config";import{JobType as g,jobTypeFromValue as w}from"../../../core/models/job_type";import{InvalidConsentConfigException as c,InvalidConsentInformationException as m}from"../../../core/exception/consent_validation_exceptions";import{InvalidFieldValueException as s,MissingJobTypeException as P,MissingMLConfigurationBlockException as k,MissingNetworkConfigurationBlockException as b}from"../../../core/exception/job_validation_exceptions";import{AgentModeWithEnhancedLivenessException as C,InvalidDocumentCaptureConfigException as u,InvalidMissingCaptureTypeException as I}from"../../../core/exception/capture_validation_exceptions";import{EmptyScreensBlockException as p}from"../../../core/exception/screen_config_validation_exceptions";import{isIdFieldBoundByToken as a}from"../../../data/model/token_payload";import{collectJobTypeIssues as T}from"./job_type_validator";function V(e){return e?.allowAgentMode&&e.enableEnhancedLiveness?[new C]:[]}function x(e){return e===void 0?[new u({reason:"document is required when mode is Document"})]:e.documentType===void 0?[new u({reason:"documentType is required for document capture"})]:e.knownIdAspectRatio!==void 0&&e.knownIdAspectRatio<=0?[new u({reason:"knownIdAspectRatio must be > 0 if provided"})]:[]}export function runBuilderValidation(e){return e.length===0?[new p]:[...l(e),...y(e)]}function l(e){const n=[];for(const o of e)o.kind==="capture"&&(o.config.captureType===void 0&&n.push(new I),o.config.captureType===d.selfie&&n.push(...V(o.config.selfieConfig)),o.config.captureType===d.document&&n.push(...x(o.config.documentConfig)));return n}function y(e){const n=[],o=e.find(i=>i.kind==="consent");return o&&(!o.config.partnerName||!o.config.partnerName.trim())&&n.push(new c({reason:"partnerName cannot be empty"})),o&&o.config.partnerIcon===void 0&&n.push(new c({reason:"partnerIcon is required for the consent screen"})),o&&(o.config.partnerPrivacyPolicyUrl===void 0||!o.config.partnerPrivacyPolicyUrl.trim())&&n.push(new c({reason:"partnerPrivacyPolicyUrl is required for the consent screen"})),n}export function runFlowValidation(e){const n=[];if(e.steps.length===0)return n.push(new p),n;n.push(...l(e.steps));const o=e.steps.some(t=>t.kind==="capture"),i=e.steps.some(t=>t.kind==="processing"),r=e.network?.config?.jobType,f=r===void 0||w(r)===g.unknown;return o&&!e.ml&&f&&n.push(new k),i&&!e.network&&n.push(new b),i&&f&&n.push(new P),n.push(...y(e.steps)),e.steps.some(t=>t.kind==="consent")&&e.consentInformation!==void 0&&n.push(new c({reason:"Exactly one form of consent is required \u2014 remove the consent { } screen if using consentInformation, or clear consentInformation if using the consent screen"})),e.consentInformation&&e.tokenPayload?.consent===void 0&&n.push(...runConsentValidation(e.consentInformation)),e.enhancedKYCParams&&n.push(...runEnhancedKYCParamsValidation(e.enhancedKYCParams,e.tokenPayload)),e.biometricKYCParams&&n.push(...runBiometricKYCParamsValidation(e.biometricKYCParams,e.tokenPayload)),e.documentVerificationParams&&n.push(...runDocumentVerificationParamsValidation(e.documentVerificationParams,e.tokenPayload)),e.enhancedDocumentVerificationParams&&n.push(...runEnhancedDocumentVerificationParamsValidation(e.enhancedDocumentVerificationParams,e.tokenPayload)),e.residencyDocumentVerificationParams&&n.push(...runResidencyDocumentVerificationParamsValidation(e.residencyDocumentVerificationParams,e.tokenPayload)),T(e,n),n}export function runEnhancedKYCParamsValidation(e,n){return h(e,n)}export function runBiometricKYCParamsValidation(e,n){return h(e,n)}export function runDocumentVerificationParamsValidation(e,n){const o=[];return!a(n,"country")&&!e.country?.trim()&&o.push(new s({fieldName:"country",reason:"must not be blank"})),!a(n,"idType")&&e.idType!==void 0&&!e.idType.trim()&&o.push(new s({fieldName:"idType",reason:"must not be blank"})),o}export function runEnhancedDocumentVerificationParamsValidation(e,n){const o=[];for(const i of["country","idType"]){const r=e[i];!a(n,i)&&!r?.trim()&&o.push(new s({fieldName:i,reason:"must not be blank"}))}return o}export function runResidencyDocumentVerificationParamsValidation(e,n){const o=[];for(const i of["country","idType"]){const r=e[i];!a(n,i)&&!r?.trim()&&o.push(new s({fieldName:i,reason:"must not be blank"}))}return o}export function runConsentValidation(e){const n=[];e.granted||n.push(new m({detail:"granted must be true"}));for(const o of["grantedAt","noticeLanguage","noticePrivacyPolicyUrl"])(!e[o]||!e[o].trim())&&n.push(new m({detail:`${o} must not be blank`}));return n}function h(e,n){const o=[];for(const i of["idType","idNumber","country"]){const r=e[i];!a(n,i)&&!r?.trim()&&o.push(new s({fieldName:i,reason:"must not be blank"}))}return o}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{RESIDENCY_ID_TYPE as l}from"../../../core/models/params";import{JobType as t}from"../../../core/models/job_type";import{CaptureType as h}from"../dsl/builder/screens_builder";import{InvalidJobTypeScreenConfigException as c}from"../../../core/exception/job_validation_exceptions";import{JOB_PARAMS_OWNERS as C,UnusedJobParamsException as D}from"../../../core/exception/unused_job_params_exception";import{DuplicateScreenTypeException as P,InvalidScreenOrderException as d,MissingCaptureForPreviewException as x}from"../../../core/exception/screen_config_validation_exceptions";import{IssueSeverity as p}from"../../../core/exception/issue_severity";import{tokenConsentMissingSubfields as V,bindsRequiredUserDetails as E}from"../../../data/model/token_payload";import{missingUserDetailsFieldIssues as I}from"./user_details_validator";const J=new Map([[t.biometricKyc,"BiometricKyc"],[t.smartSelfieAuthentication,"SmartSelfieAuthentication"],[t.smartSelfieEnrollment,"SmartSelfieEnrollment"],[t.enhancedKyc,"EnhancedKyc"],[t.documentVerification,"DocumentVerification"],[t.bvn,"BVN"],[t.enhancedDocumentVerification,"EnhancedDocumentVerification"],[t.residencyDocumentVerification,"ResidencyDocumentVerification"],[t.unknown,"Unknown"]]);function y(o){return J.get(o)??"Unknown"}function b(o,e,n){const r=e.steps.some(s=>s.kind==="consent"),i=e.consentInformation!==void 0,a=e.tokenPayload?.consent;if(a!==void 0){const s=V(a);s.length>0&&n.push(c.forJob({jobType:o,reason:`the token's consent binding is incomplete \u2014 missing: ${s.join(", ")}`,suggestedFix:"Bind all four consent fields when minting the token at /v3/token, or bind none and collect consent in the app"}));return}!r&&!i&&n.push(c.forJob({jobType:o,reason:"must include either a Consent screen or a pre-supplied consentInformation",suggestedFix:"Add a consent { } screen to your flow, or supply consentInformation via the builder"}))}export const JOB_PARAMS_SUPPLIERS={enhancedKYCParams:o=>o.enhancedKYCParams,biometricKYCParams:o=>o.biometricKYCParams,documentVerificationParams:o=>o.documentVerificationParams,enhancedDocumentVerificationParams:o=>o.enhancedDocumentVerificationParams,residencyDocumentVerificationParams:o=>o.residencyDocumentVerificationParams};function T(o,e,n){for(const{fieldName:r,owner:i}of C)i!==o&&JOB_PARAMS_SUPPLIERS[r]?.(e)!==void 0&&n.push(new D({jobType:o,fieldName:r}))}function g(o,e,n){if(e.userDetails===void 0&&!E(e.tokenPayload)){if(e.tokenPayload===void 0){n.push(c.forJob({jobType:o,reason:`userDetails is required for ${y(o)} jobs`,suggestedFix:"Set builder.userDetails with the end user PII before building"}));return}n.push(...I(void 0,e.tokenPayload))}}function u(o,e){const n=new Map;o.forEach((r,i)=>{if(r.kind==="capture"||r.kind==="preview")return;const a=n.get(r.kind)??[];a.push(i),n.set(r.kind,a)});for(const[r,i]of n)i.length>1&&e.push(new P({screenType:r,indices:i}))}function f(o,e){o.findIndex(a=>a.kind==="consent")>0&&e.push(new d({description:"Consent screen must be the first screen in the flow",severity:p.error}));const r=o.findIndex(a=>a.kind==="processing");r!==-1&&r!==o.length-1&&e.push(new d({description:"Processing screen must be the last screen in the flow",severity:p.error}));let i=!1;o.forEach((a,s)=>{a.kind==="capture"?i=!0:a.kind==="instructions"&&i&&e.push(new d({description:`Instructions screen at index ${s} must appear before the Capture screen`,severity:p.error}))})}function m(o,e,n){let r=!1,i=0;o.forEach((a,s)=>{a.kind==="capture"?r=!0:a.kind==="preview"&&(i+=1,r?i>e&&n.push(new d({description:`Preview screen at index ${s} exceeds the maximum of ${e} preview screen(s) allowed for this flow`,severity:p.error})):n.push(new x({previewIndex:s})))})}function j(o,e,n){e.steps.some(i=>i.kind==="processing")||n.push(c.forJob({jobType:o,reason:"must include a Processing screen",suggestedFix:"Add a processing { } screen as the last screen in the flow"}))}function w(o,e,n){const r=e.steps.filter(i=>i.kind==="capture"&&i.config.captureType===h.selfie);r.length===0?n.push(c.forJob({jobType:o,reason:"must include exactly one Capture screen with captureType = SELFIE",suggestedFix:"Add a capture { captureType = CaptureType.SELFIE } screen to your flow"})):r.length>1&&n.push(c.forJob({jobType:o,reason:`must include exactly one Capture screen with captureType = SELFIE, but found ${r.length}`,suggestedFix:"Remove the extra selfie capture screens \u2014 only one is allowed"}))}function S(o,e,n,r){e.ml?e.ml.analyzers.selfie||r.push(c.forJob({jobType:o,reason:"ml { } block must register analyzers for CaptureType.SELFIE",suggestedFix:"Add forCaptureType(CaptureType.SELFIE) { } inside the ml { } block"})):r.push(c.forJob({jobType:o,reason:"ml { } block is required and must register analyzers for CaptureType.SELFIE",suggestedFix:n}))}export class SelfieJobTypeValidator{jobType;constructor(e){this.jobType=e}validate(e,n){u(e.steps,n),f(e.steps,n),m(e.steps,1,n),b(this.jobType,e,n),T(this.jobType,e,n),w(this.jobType,e,n),e.steps.some(i=>i.kind==="capture"&&i.config.captureType===h.document)&&n.push(c.forJob({jobType:this.jobType,reason:"must not include a Document capture screen",suggestedFix:`Remove the Document capture screen \u2014 ${y(this.jobType)} requires selfie capture only`})),j(this.jobType,e,n),S(this.jobType,e,"Add the ml { } block with forCaptureType(CaptureType.SELFIE) { } inside",n),e.ml?.analyzers.document&&n.push(c.forJob({jobType:this.jobType,reason:"ml { } block must not register analyzers for CaptureType.DOCUMENT",suggestedFix:`Remove forCaptureType(CaptureType.DOCUMENT) { } from the ml { } block \u2014 ${y(this.jobType)} does not use document capture`})),this.jobType===t.biometricKyc&&e.biometricKYCParams===void 0&&n.push(c.forJob({jobType:this.jobType,reason:"biometricKYCParams is required for BiometricKyc jobs",suggestedFix:"Set builder.biometricKYCParams with the identity fields before building"})),this.jobType===t.smartSelfieAuthentication&&(e.userId===void 0||e.userId.trim()==="")&&n.push(c.forJob({jobType:this.jobType,reason:"userId is required for SmartSelfieAuthentication jobs",suggestedFix:"Set builder.userId with the identifier returned by a prior Smart Selfie Enrollment before building"})),g(this.jobType,e,n)}}export class DocumentJobTypeValidator{jobType;constructor(e){this.jobType=e}validate(e,n){u(e.steps,n),f(e.steps,n),m(e.steps,2,n),b(this.jobType,e,n),T(this.jobType,e,n),w(this.jobType,e,n);const r=e.steps.filter(i=>i.kind==="capture"&&i.config.captureType===h.document);if(r.length===0?n.push(c.forJob({jobType:this.jobType,reason:"must include exactly one Capture screen with captureType = DOCUMENT",suggestedFix:"Add a capture { captureType = CaptureType.DOCUMENT } screen to your flow"})):r.length>1&&n.push(c.forJob({jobType:this.jobType,reason:`must include exactly one Capture screen with captureType = DOCUMENT, but found ${r.length}`,suggestedFix:"Remove the extra document capture screens \u2014 only one is allowed"})),j(this.jobType,e,n),S(this.jobType,e,"Add the ml { } block with forCaptureType(CaptureType.SELFIE) { } inside \u2014 document capture uses NoopDocumentDetectorAnalyzer by default",n),this.appendParamsRule(e,n),this.jobType===t.enhancedDocumentVerification&&r.some(i=>i.kind==="capture"&&i.config.documentConfig?.documentType?.kind==="southAfricaGreenBook")&&n.push(c.forJob({jobType:this.jobType,reason:"South Africa Green Book is not supported for Enhanced Document Verification",suggestedFix:"Use JobType.documentVerification for the Green Book, or choose a supported document type for Enhanced Document Verification"})),this.jobType===t.residencyDocumentVerification)for(const i of r){if(i.kind!=="capture")continue;const a=i.config.documentConfig,s=a?.documentType;s?.kind!=="passport"&&n.push(c.forJob({jobType:this.jobType,reason:`documentType must be passport \u2014 ${s?.kind??"no document type"} is not a residency primary document`,suggestedFix:"Set documentType to the passport preset on the document capture screen"})),a&&a.allowSkipBack===!0&&n.push(c.forJob({jobType:this.jobType,reason:"allowSkipBack must be false \u2014 the visa capture is mandatory",suggestedFix:"Remove allowSkipBack from the document capture screen"}))}g(this.jobType,e,n)}appendParamsRule(e,n){this.jobType===t.documentVerification&&e.documentVerificationParams===void 0&&n.push(c.forJob({jobType:this.jobType,reason:"documentVerificationParams is required for DocumentVerification jobs",suggestedFix:"Set builder.documentVerificationParams with the country (and optional idType) before building"})),this.jobType===t.enhancedDocumentVerification&&e.enhancedDocumentVerificationParams===void 0&&n.push(c.forJob({jobType:this.jobType,reason:"enhancedDocumentVerificationParams is required for EnhancedDocumentVerification jobs",suggestedFix:"Set builder.enhancedDocumentVerificationParams with the country and idType before building"})),this.jobType===t.residencyDocumentVerification&&e.tokenPayload?.hasIdType!==!0&&e.residencyDocumentVerificationParams!==void 0&&e.residencyDocumentVerificationParams.idType!==l&&n.push(residencyIdTypeIssue()),this.jobType===t.residencyDocumentVerification&&e.residencyDocumentVerificationParams===void 0&&n.push(c.forJob({jobType:this.jobType,reason:"residencyDocumentVerificationParams is required for ResidencyDocumentVerification jobs",suggestedFix:"Set builder.residencyDocumentVerificationParams with the country and idType before building"}))}}export class NonCaptureJobTypeValidator{jobType;constructor(e){this.jobType=e}validate(e,n){u(e.steps,n),f(e.steps,n),m(e.steps,0,n),b(this.jobType,e,n),T(this.jobType,e,n),j(this.jobType,e,n);const r=e.steps.filter(i=>i.kind!=="consent"&&i.kind!=="processing");if(r.length>0){const i=[...new Set(r.map(a=>a.kind))].join(", ");n.push(c.forJob({jobType:this.jobType,reason:`must only contain consent and processing screens, but found: ${i}`,suggestedFix:"Remove all screens except consent { } and processing { } from your flow"}))}this.jobType===t.enhancedKyc&&e.enhancedKYCParams===void 0&&n.push(c.forJob({jobType:this.jobType,reason:"enhancedKYCParams is required for EnhancedKyc jobs",suggestedFix:"Set builder.enhancedKYCParams with the identity fields before building"})),this.jobType===t.enhancedKyc&&g(this.jobType,e,n)}}export class DefaultStructuralValidator{validate(e,n){u(e.steps,n),f(e.steps,n),m(e.steps,1,n)}}export const jobTypeValidators=new Map([[t.biometricKyc,new SelfieJobTypeValidator(t.biometricKyc)],[t.smartSelfieAuthentication,new SelfieJobTypeValidator(t.smartSelfieAuthentication)],[t.smartSelfieEnrollment,new SelfieJobTypeValidator(t.smartSelfieEnrollment)],[t.documentVerification,new DocumentJobTypeValidator(t.documentVerification)],[t.enhancedDocumentVerification,new DocumentJobTypeValidator(t.enhancedDocumentVerification)],[t.residencyDocumentVerification,new DocumentJobTypeValidator(t.residencyDocumentVerification)],[t.enhancedKyc,new NonCaptureJobTypeValidator(t.enhancedKyc)],[t.bvn,new NonCaptureJobTypeValidator(t.bvn)]]);const k=new DefaultStructuralValidator;export function resolveJobTypeValidator(o){return o===void 0||o===t.unknown?k:jobTypeValidators.get(o)??k}export function collectJobTypeIssues(o,e){const n=o.network?.config?.jobType;resolveJobTypeValidator(n).validate(o,e)}export function residencyIdTypeIssue(){return c.forJob({jobType:t.residencyDocumentVerification,reason:`idType must be "${l}" \u2014 the backend rejects any other value for this job type`,suggestedFix:`Set builder.residencyDocumentVerificationParams with idType: "${l}"`})}
|
|
1
|
+
"use strict";import{RESIDENCY_ID_TYPE as h}from"../../../core/models/params";import{JobType as i}from"../../../core/models/job_type";import{CaptureType as y}from"../dsl/builder/screens_builder";import{InvalidJobTypeScreenConfigException as a}from"../../../core/exception/job_validation_exceptions";import{JOB_PARAMS_OWNERS as C,UnusedJobParamsException as x}from"../../../core/exception/unused_job_params_exception";import{DuplicateScreenTypeException as V,InvalidScreenOrderException as p,MissingCaptureForPreviewException as D}from"../../../core/exception/screen_config_validation_exceptions";import{IssueSeverity as u}from"../../../core/exception/issue_severity";import{tokenConsentMissingSubfields as P}from"../../../data/model/token_payload";const E=new Map([[i.biometricKyc,"BiometricKyc"],[i.smartSelfieAuthentication,"SmartSelfieAuthentication"],[i.smartSelfieEnrollment,"SmartSelfieEnrollment"],[i.enhancedKyc,"EnhancedKyc"],[i.documentVerification,"DocumentVerification"],[i.bvn,"BVN"],[i.enhancedDocumentVerification,"EnhancedDocumentVerification"],[i.residencyDocumentVerification,"ResidencyDocumentVerification"],[i.unknown,"Unknown"]]);function j(o){return E.get(o)??"Unknown"}function b(o,e,n){const r=e.steps.some(s=>s.kind==="consent"),t=e.consentInformation!==void 0,c=e.tokenPayload?.consent;if(c!==void 0){const s=P(c);s.length>0&&n.push(a.forJob({jobType:o,reason:`the token's consent binding is incomplete \u2014 missing: ${s.join(", ")}`,suggestedFix:"Bind all four consent fields when minting the token at /v3/token, or bind none and collect consent in the app"}));return}!r&&!t&&n.push(a.forJob({jobType:o,reason:"must include either a Consent screen or a pre-supplied consentInformation",suggestedFix:"Add a consent { } screen to your flow, or supply consentInformation via the builder"}))}export const JOB_PARAMS_SUPPLIERS={enhancedKYCParams:o=>o.enhancedKYCParams,biometricKYCParams:o=>o.biometricKYCParams,documentVerificationParams:o=>o.documentVerificationParams,enhancedDocumentVerificationParams:o=>o.enhancedDocumentVerificationParams,residencyDocumentVerificationParams:o=>o.residencyDocumentVerificationParams};function T(o,e,n){for(const{fieldName:r,owner:t}of C)t!==o&&JOB_PARAMS_SUPPLIERS[r]?.(e)!==void 0&&n.push(new x({jobType:o,fieldName:r}))}function f(o,e){const n=new Map;let r=0;o.forEach((t,c)=>{if(t.kind==="capture"){r++;return}if(t.kind==="preview")return;const s=t.kind==="instructions"?`${t.kind}:${r}`:t.kind,d=n.get(s)??{kind:t.kind,indices:[]};d.indices.push(c),n.set(s,d)});for(const{kind:t,indices:c}of n.values())c.length>1&&e.push(new V({screenType:t,indices:c}))}function m(o,e){o.findIndex(c=>c.kind==="consent")>0&&e.push(new p({description:"Consent screen must be the first screen in the flow",severity:u.error}));const r=o.findIndex(c=>c.kind==="processing");r!==-1&&r!==o.length-1&&e.push(new p({description:"Processing screen must be the last screen in the flow",severity:u.error}));const t=o.some(c=>c.kind==="capture");o.forEach((c,s)=>{const d=o[s+1]?.kind;c.kind==="instructions"&&t&&d!=="capture"&&d!=="instructions"&&e.push(new p({description:`Instructions screen at index ${s} must be followed by a Capture screen`,severity:u.error}))})}function l(o,e,n){let r=!1,t=0;o.forEach((c,s)=>{c.kind==="capture"?r=!0:c.kind==="preview"&&(t+=1,r?t>e&&n.push(new p({description:`Preview screen at index ${s} exceeds the maximum of ${e} preview screen(s) allowed for this flow`,severity:u.error})):n.push(new D({previewIndex:s})))})}function g(o,e,n){e.steps.some(t=>t.kind==="processing")||n.push(a.forJob({jobType:o,reason:"must include a Processing screen",suggestedFix:"Add a processing { } screen as the last screen in the flow"}))}function w(o,e,n){const r=e.steps.filter(t=>t.kind==="capture"&&t.config.captureType===y.selfie);r.length===0?n.push(a.forJob({jobType:o,reason:"must include exactly one Capture screen with captureType = SELFIE",suggestedFix:"Add a capture { captureType = CaptureType.SELFIE } screen to your flow"})):r.length>1&&n.push(a.forJob({jobType:o,reason:`must include exactly one Capture screen with captureType = SELFIE, but found ${r.length}`,suggestedFix:"Remove the extra selfie capture screens \u2014 only one is allowed"}))}function S(o,e,n,r){e.ml?e.ml.analyzers.selfie||r.push(a.forJob({jobType:o,reason:"ml { } block must register analyzers for CaptureType.SELFIE",suggestedFix:"Add forCaptureType(CaptureType.SELFIE) { } inside the ml { } block"})):r.push(a.forJob({jobType:o,reason:"ml { } block is required and must register analyzers for CaptureType.SELFIE",suggestedFix:n}))}export class SelfieJobTypeValidator{jobType;constructor(e){this.jobType=e}validate(e,n){f(e.steps,n),m(e.steps,n),l(e.steps,1,n),b(this.jobType,e,n),T(this.jobType,e,n),w(this.jobType,e,n),e.steps.some(t=>t.kind==="capture"&&t.config.captureType===y.document)&&n.push(a.forJob({jobType:this.jobType,reason:"must not include a Document capture screen",suggestedFix:`Remove the Document capture screen \u2014 ${j(this.jobType)} requires selfie capture only`})),g(this.jobType,e,n),S(this.jobType,e,"Add the ml { } block with forCaptureType(CaptureType.SELFIE) { } inside",n),e.ml?.analyzers.document&&n.push(a.forJob({jobType:this.jobType,reason:"ml { } block must not register analyzers for CaptureType.DOCUMENT",suggestedFix:`Remove forCaptureType(CaptureType.DOCUMENT) { } from the ml { } block \u2014 ${j(this.jobType)} does not use document capture`})),this.jobType===i.biometricKyc&&e.biometricKYCParams===void 0&&n.push(a.forJob({jobType:this.jobType,reason:"biometricKYCParams is required for BiometricKyc jobs",suggestedFix:"Set builder.biometricKYCParams with the identity fields before building"})),this.jobType===i.smartSelfieAuthentication&&(e.userId===void 0||e.userId.trim()==="")&&n.push(a.forJob({jobType:this.jobType,reason:"userId is required for SmartSelfieAuthentication jobs",suggestedFix:"Set builder.userId with the identifier returned by a prior Smart Selfie Enrollment before building"}))}}export class DocumentJobTypeValidator{jobType;constructor(e){this.jobType=e}validate(e,n){f(e.steps,n),m(e.steps,n),l(e.steps,2,n),b(this.jobType,e,n),T(this.jobType,e,n),w(this.jobType,e,n);const r=e.steps.filter(t=>t.kind==="capture"&&t.config.captureType===y.document);if(r.length===0?n.push(a.forJob({jobType:this.jobType,reason:"must include exactly one Capture screen with captureType = DOCUMENT",suggestedFix:"Add a capture { captureType = CaptureType.DOCUMENT } screen to your flow"})):r.length>1&&n.push(a.forJob({jobType:this.jobType,reason:`must include exactly one Capture screen with captureType = DOCUMENT, but found ${r.length}`,suggestedFix:"Remove the extra document capture screens \u2014 only one is allowed"})),g(this.jobType,e,n),S(this.jobType,e,"Add the ml { } block with forCaptureType(CaptureType.SELFIE) { } inside \u2014 document capture uses NoopDocumentDetectorAnalyzer by default",n),this.appendParamsRule(e,n),this.jobType===i.enhancedDocumentVerification&&r.some(t=>t.kind==="capture"&&t.config.documentConfig?.documentType?.kind==="southAfricaGreenBook")&&n.push(a.forJob({jobType:this.jobType,reason:"South Africa Green Book is not supported for Enhanced Document Verification",suggestedFix:"Use JobType.documentVerification for the Green Book, or choose a supported document type for Enhanced Document Verification"})),this.jobType===i.residencyDocumentVerification)for(const t of r){if(t.kind!=="capture")continue;const c=t.config.documentConfig,s=c?.documentType;s?.kind!=="passport"&&n.push(a.forJob({jobType:this.jobType,reason:`documentType must be passport \u2014 ${s?.kind??"no document type"} is not a residency primary document`,suggestedFix:"Set documentType to the passport preset on the document capture screen"})),c&&c.allowSkipBack===!0&&n.push(a.forJob({jobType:this.jobType,reason:"allowSkipBack must be false \u2014 the visa capture is mandatory",suggestedFix:"Remove allowSkipBack from the document capture screen"}))}}appendParamsRule(e,n){this.jobType===i.documentVerification&&e.documentVerificationParams===void 0&&n.push(a.forJob({jobType:this.jobType,reason:"documentVerificationParams is required for DocumentVerification jobs",suggestedFix:"Set builder.documentVerificationParams with the country (and optional idType) before building"})),this.jobType===i.enhancedDocumentVerification&&e.enhancedDocumentVerificationParams===void 0&&n.push(a.forJob({jobType:this.jobType,reason:"enhancedDocumentVerificationParams is required for EnhancedDocumentVerification jobs",suggestedFix:"Set builder.enhancedDocumentVerificationParams with the country and idType before building"})),this.jobType===i.residencyDocumentVerification&&e.tokenPayload?.hasIdType!==!0&&e.residencyDocumentVerificationParams!==void 0&&e.residencyDocumentVerificationParams.idType!==h&&n.push(residencyIdTypeIssue()),this.jobType===i.residencyDocumentVerification&&e.residencyDocumentVerificationParams===void 0&&n.push(a.forJob({jobType:this.jobType,reason:"residencyDocumentVerificationParams is required for ResidencyDocumentVerification jobs",suggestedFix:"Set builder.residencyDocumentVerificationParams with the country and idType before building"}))}}export class NonCaptureJobTypeValidator{jobType;constructor(e){this.jobType=e}validate(e,n){f(e.steps,n),m(e.steps,n),l(e.steps,0,n),b(this.jobType,e,n),T(this.jobType,e,n),g(this.jobType,e,n);const r=e.steps.filter(t=>t.kind!=="consent"&&t.kind!=="processing");if(r.length>0){const t=[...new Set(r.map(c=>c.kind))].join(", ");n.push(a.forJob({jobType:this.jobType,reason:`must only contain consent and processing screens, but found: ${t}`,suggestedFix:"Remove all screens except consent { } and processing { } from your flow"}))}this.jobType===i.enhancedKyc&&e.enhancedKYCParams===void 0&&n.push(a.forJob({jobType:this.jobType,reason:"enhancedKYCParams is required for EnhancedKyc jobs",suggestedFix:"Set builder.enhancedKYCParams with the identity fields before building"}))}}export class DefaultStructuralValidator{validate(e,n){f(e.steps,n),m(e.steps,n),l(e.steps,1,n)}}export const jobTypeValidators=new Map([[i.biometricKyc,new SelfieJobTypeValidator(i.biometricKyc)],[i.smartSelfieAuthentication,new SelfieJobTypeValidator(i.smartSelfieAuthentication)],[i.smartSelfieEnrollment,new SelfieJobTypeValidator(i.smartSelfieEnrollment)],[i.documentVerification,new DocumentJobTypeValidator(i.documentVerification)],[i.enhancedDocumentVerification,new DocumentJobTypeValidator(i.enhancedDocumentVerification)],[i.residencyDocumentVerification,new DocumentJobTypeValidator(i.residencyDocumentVerification)],[i.enhancedKyc,new NonCaptureJobTypeValidator(i.enhancedKyc)],[i.bvn,new NonCaptureJobTypeValidator(i.bvn)]]);const k=new DefaultStructuralValidator;export function resolveJobTypeValidator(o){return o===void 0||o===i.unknown?k:jobTypeValidators.get(o)??k}export function collectJobTypeIssues(o,e){const n=o.network?.config?.jobType;resolveJobTypeValidator(n).validate(o,e)}export function residencyIdTypeIssue(){return a.forJob({jobType:i.residencyDocumentVerification,reason:`idType must be "${h}" \u2014 the backend rejects any other value for this job type`,suggestedFix:`Set builder.residencyDocumentVerificationParams with idType: "${h}"`})}
|
|
@@ -14,15 +14,3 @@ export declare function selfieFeedbackKeyFor(state: OverlayState): SiKey | null;
|
|
|
14
14
|
export declare function documentFeedbackKeyFor(state: OverlayState): SiKey | null;
|
|
15
15
|
/** Resolve a stable feedback key to the active locale's copy. */
|
|
16
16
|
export declare function resolveFeedbackCopy(key: SiKey, loc: UseSmileIDLocalizations): string;
|
|
17
|
-
/**
|
|
18
|
-
* Overlay-state-only copy resolution for callers without a scan state;
|
|
19
|
-
* `mapCaptureUiState` is the richer path that also handles Enhanced
|
|
20
|
-
* Smart Selfie copy.
|
|
21
|
-
*/
|
|
22
|
-
export declare function resolveCaptureFeedback(state: OverlayState, loc: UseSmileIDLocalizations): string;
|
|
23
|
-
/**
|
|
24
|
-
* Whether the start-capture button is enabled. "Start" begins the
|
|
25
|
-
* analyzer pipeline (not a snapshot), so it's enabled exactly during
|
|
26
|
-
* the pre-flight `INSTRUCTION` state — before the pipeline has started.
|
|
27
|
-
*/
|
|
28
|
-
export declare function isStartCaptureEnabled(state: OverlayState): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{UseSmileIDOverlayState as t}from"../../state/use_smile_id_overlay_state";export function selfieFeedbackKeyFor(e){switch(e){case t.TOO_CLOSE:return"si_capture_too_close";case t.TOO_FAR:return"si_capture_too_far";case t.FACE_NOT_CENTERED:return"si_capture_center_face";case t.LOW_LIGHT:return"si_capture_low_light";case t.BRIGHT_LIGHT:return"si_capture_bright_light";case t.WRONG_ORIENTATION:return"si_capture_rotate_device";case t.NOT_SMILING:return"si_capture_smile";case t.MOVE_LEFT:return"si_capture_move_left";case t.MOVE_RIGHT:return"si_capture_move_right";case t.MOVE_UP:return"si_capture_move_higher";case t.MOVE_DOWN:return"si_capture_move_lower";default:return null}}export function documentFeedbackKeyFor(e){switch(e){case t.DOCUMENT_TOO_CLOSE:return"si_document_too_close";case t.DOCUMENT_TOO_FAR:return"si_document_too_far";case t.DOCUMENT_NOT_CENTERED:return"si_document_center";case t.DOCUMENT_HOLD_STILL:return"si_document_hold_still";default:return null}}const
|
|
1
|
+
"use strict";import{UseSmileIDOverlayState as t}from"../../state/use_smile_id_overlay_state";export function selfieFeedbackKeyFor(e){switch(e){case t.TOO_CLOSE:return"si_capture_too_close";case t.TOO_FAR:return"si_capture_too_far";case t.FACE_NOT_CENTERED:return"si_capture_center_face";case t.LOW_LIGHT:return"si_capture_low_light";case t.BRIGHT_LIGHT:return"si_capture_bright_light";case t.WRONG_ORIENTATION:return"si_capture_rotate_device";case t.NOT_SMILING:return"si_capture_smile";case t.MOVE_LEFT:return"si_capture_move_left";case t.MOVE_RIGHT:return"si_capture_move_right";case t.MOVE_UP:return"si_capture_move_higher";case t.MOVE_DOWN:return"si_capture_move_lower";default:return null}}export function documentFeedbackKeyFor(e){switch(e){case t.DOCUMENT_TOO_CLOSE:return"si_document_too_close";case t.DOCUMENT_TOO_FAR:return"si_document_too_far";case t.DOCUMENT_NOT_CENTERED:return"si_document_center";case t.DOCUMENT_HOLD_STILL:return"si_document_hold_still";default:return null}}const s={si_capture_initial_instruction:e=>e.siCaptureInitialInstruction,si_start_capture_instruction:e=>e.siStartCaptureInstruction,si_capture_too_close:e=>e.siCaptureTooClose,si_capture_too_far:e=>e.siCaptureTooFar,si_capture_center_face:e=>e.siCaptureCenterFace,si_capture_low_light:e=>e.siCaptureLowLight,si_capture_bright_light:e=>e.siCaptureBrightLight,si_capture_rotate_device:e=>e.siCaptureRotateDevice,si_capture_smile:e=>e.siCaptureSmile,si_capture_move_left:e=>e.siCaptureMoveLeft,si_capture_move_right:e=>e.siCaptureMoveRight,si_capture_move_higher:e=>e.siCaptureMoveHigher,si_capture_move_lower:e=>e.siCaptureMoveLower,si_capture_hold_steady:e=>e.siCaptureHoldSteady,si_capture_timeout:e=>e.siCaptureTimeout,si_capture_camera_unavailable:e=>e.siCaptureCameraUnavailable,si_capture_failed:e=>e.siCaptureFailed,si_capturing:e=>e.siCapturing,si_selfie_capture_complete:e=>e.siSelfieCaptureComplete,si_selfie_capture_unsatisfied:e=>e.siSelfieCaptureUnsatisfied,si_ess_capture_initial_instruction:e=>e.siEssCaptureInitialInstruction,si_ess_hold_still:e=>e.siEssHoldStill,si_ess_look_left:e=>e.siEssLookLeft,si_ess_look_right:e=>e.siEssLookRight,si_ess_look_up:e=>e.siEssLookUp,si_ess_look_at_camera:e=>e.siEssLookAtCamera,si_document_initial_instruction:e=>e.siDocumentInitialInstruction,si_document_ready:e=>e.siDocumentReady,si_document_capturing:e=>e.siDocumentCapturing,si_document_capture_complete:e=>e.siDocumentCaptureComplete,si_document_capture_unsatisfied:e=>e.siDocumentCaptureUnsatisfied,si_document_too_close:e=>e.siDocumentTooClose,si_document_too_far:e=>e.siDocumentTooFar,si_document_center:e=>e.siDocumentCenter,si_document_hold_still:e=>e.siDocumentHoldStill,si_doc_v_capture_directive_wrong_aspect_ratio:e=>e.siDocumentWrongAspectRatio,si_document_gallery_image_too_small:e=>e.siDocumentGalleryImageTooSmall,si_document_gallery_image_failed:e=>e.siDocumentGalleryImageFailed};export function resolveFeedbackCopy(e,i){const _=s[e];return _?_(i):i.siCaptureInitialInstruction}
|
|
@@ -12,16 +12,43 @@ export type UseSmileIDCaptureUiState = {
|
|
|
12
12
|
/** Active ESS head-turn direction, null outside a turn. */
|
|
13
13
|
readonly animationDirection: ScanHeadTurnDirection | null;
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* How much of the current ESS head turn is done, 0..1, rounded down to
|
|
16
|
+
* {@link TURN_PROGRESS_STEP}: the turn toward the pose, then the hold
|
|
17
|
+
* there. Scrubs the direction arrow. 0 outside a head turn.
|
|
17
18
|
*/
|
|
18
|
-
readonly
|
|
19
|
+
readonly turnProgress: number;
|
|
20
|
+
/**
|
|
21
|
+
* Whether the copy is the next thing to do rather than a correction, so it
|
|
22
|
+
* skips the feedback debounce: the selfie's step, the smile prompt in its
|
|
23
|
+
* smiling half, and each turn's prompt.
|
|
24
|
+
*/
|
|
25
|
+
readonly showsAtOnce: boolean;
|
|
19
26
|
};
|
|
20
27
|
/**
|
|
21
|
-
* The oval never fills under Enhanced Smart Selfie
|
|
22
|
-
* applied once at the
|
|
28
|
+
* The oval never fills under Enhanced Smart Selfie: each turn's progress
|
|
29
|
+
* moves the direction arrow instead. Idempotent, applied once at the
|
|
30
|
+
* mapping's exit.
|
|
23
31
|
*/
|
|
24
32
|
export declare function withEnhancedLivenessPolicy(state: UseSmileIDCaptureUiState, enableEnhancedLiveness: boolean): UseSmileIDCaptureUiState;
|
|
33
|
+
/**
|
|
34
|
+
* The step a head turn's progress moves in: fine enough to look smooth,
|
|
35
|
+
* coarse enough that a frame which barely moved the head doesn't re-render.
|
|
36
|
+
* The same step in every SDK.
|
|
37
|
+
*/
|
|
38
|
+
export declare const TURN_PROGRESS_STEP: number;
|
|
39
|
+
/**
|
|
40
|
+
* [progress] clamped to 0..1 and rounded down to {@link TURN_PROGRESS_STEP},
|
|
41
|
+
* so the arrow never reaches the hold (green) before the native turn does. A
|
|
42
|
+
* missing or non-finite value maps to 0.
|
|
43
|
+
*/
|
|
44
|
+
/**
|
|
45
|
+
* [progress], held at least at the hold while the native [poseMatched] latch
|
|
46
|
+
* holds: a head right at the turn's threshold makes the native progress hop
|
|
47
|
+
* across it frame to frame, and the latch (500 ms) keeps the arrow green
|
|
48
|
+
* through that instead of flickering grey.
|
|
49
|
+
*/
|
|
50
|
+
export declare function heldTurnProgress(progress: number, poseMatched: boolean): number;
|
|
51
|
+
export declare function steppedTurnProgress(progress: number | undefined): number;
|
|
25
52
|
/**
|
|
26
53
|
* Terminal snapshot for a completed selfie scan — single source for
|
|
27
54
|
* the `finished` mapping and any post-scan fallback.
|
|
@@ -35,7 +62,7 @@ export declare function scannedDocumentUiState(): UseSmileIDCaptureUiState;
|
|
|
35
62
|
* regression — while the guidance primes with the flow's initial
|
|
36
63
|
* instruction.
|
|
37
64
|
*/
|
|
38
|
-
export declare function preCaptureUiState(captureType: CaptureType, enableEnhancedLiveness: boolean): UseSmileIDCaptureUiState;
|
|
65
|
+
export declare function preCaptureUiState(captureType: CaptureType, enableEnhancedLiveness: boolean, started?: boolean): UseSmileIDCaptureUiState;
|
|
39
66
|
/**
|
|
40
67
|
* True when the native analyzer restarted evaluation: leaving
|
|
41
68
|
* `unsatisfied`, or `timeout`/`finished` returning to `initial`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{UseSmileIDScanStateType as i}from"@smileid/usesmileid_bridge";import{CaptureType as
|
|
1
|
+
"use strict";import{UseSmileIDScanStateType as i}from"@smileid/usesmileid_bridge";import{ENHANCED_SMART_SELFIE_TURN_SHARE as a}from"../../components/helpers/enhanced_smart_selfie_animation_frames";import{CaptureType as c}from"../../../core/models/ml_config";import{UseSmileIDOverlayState as n}from"../../state/use_smile_id_overlay_state";import{mapScanStateToOverlayState as _,resolveFeedbackOverlay as l}from"./scan_state_to_overlay_state";import{documentFeedbackKeyFor as p,selfieFeedbackKeyFor as f}from"./capture_screen_helpers";function s(e,t,r){return{overlayState:e,feedbackKey:t,captureProgress:r,animationDirection:null,turnProgress:0,showsAtOnce:!1}}const m=["si_capture_rotate_device","si_capture_low_light","si_capture_bright_light","si_capture_too_far","si_capture_too_close","si_capture_center_face","si_ess_look_at_camera"];function d(e){if(e===void 0)return null;for(const t of m)if(t===e)return t;return null}export function withEnhancedLivenessPolicy(e,t){return t&&e.captureProgress!==0?{...e,captureProgress:0}:e}export const TURN_PROGRESS_STEP=1/40;export function heldTurnProgress(e,t){return t?Math.max(e,a):e}export function steppedTurnProgress(e){if(e===void 0||!Number.isFinite(e))return 0;const t=Math.min(Math.max(e,0),1);return Math.floor(t/TURN_PROGRESS_STEP+1e-6)*TURN_PROGRESS_STEP}export function scannedUiState(e){return withEnhancedLivenessPolicy(s(n.PROCESSING,"si_selfie_capture_complete",1),e)}export function scannedDocumentUiState(){return s(n.PROCESSING,"si_document_capture_complete",1)}export function preCaptureUiState(e,t,r=!1){const u=e!==c.selfie?"si_document_initial_instruction":r?t?"si_ess_capture_initial_instruction":"si_capture_initial_instruction":"si_start_capture_instruction";return s(n.IDLE,u,0)}export function isSessionBoundary(e,t){return e.type===t.type?!1:e.type===i.Unsatisfied?!0:(e.type===i.Timeout||e.type===i.Finished)&&t.type===i.Initial}export function mapCaptureUiState(e,t,r){return t===c.selfie?mapSelfieUiState(e,r):mapDocumentUiState(e)}export function mapSelfieUiState(e,t){return withEnhancedLivenessPolicy(y(e,t),t)}function y(e,t){switch(e.type){case i.Initial:return s(n.INSTRUCTION,t?"si_ess_capture_initial_instruction":"si_capture_initial_instruction",0);case i.Found:{const r=_(e,c.selfie),u=f(r),o=e.captureStep;return{...s(r,u??(o==="holdStill"?"si_ess_hold_still":o==="smile"?"si_capture_smile":t?"si_ess_hold_still":"si_capturing"),e.captureProgress??0),showsAtOnce:o!==void 0&&(u===null||o==="smile"&&u==="si_capture_smile")}}case i.Satisfied:return s(n.CAPTURING,t?"si_ess_hold_still":"si_capturing",1);case i.Unsatisfied:return s(n.FACE_DETECTED,"si_selfie_capture_unsatisfied",0);case i.Finished:return scannedUiState(t);case i.Timeout:return s(n.IDLE,"si_capture_timeout",0);case i.HeadTurn:{const r=e.currentDirection??null,u=r==="left"?"si_ess_look_left":r==="right"?"si_ess_look_right":r==="up"?"si_ess_look_up":"si_capture_hold_steady";if(e.feedback==="si_ess_look_at_camera")return{...s(n.FACE_DETECTED,"si_ess_look_at_camera",0),animationDirection:r};const o=d(e.feedback);return{overlayState:o!==null?l(o,c.selfie,n.HEAD_TURN):n.HEAD_TURN,feedbackKey:o??u,captureProgress:0,animationDirection:o!==null?null:r,turnProgress:o!==null?0:heldTurnProgress(steppedTurnProgress(e.turnProgress),e.poseMatched??!1),showsAtOnce:o===null}}}}export function mapDocumentUiState(e){switch(e.type){case i.Initial:case i.HeadTurn:return s(n.INSTRUCTION,"si_document_initial_instruction",0);case i.Found:{const t=_(e,c.document),r=p(t);return r===null?s(n.CAPTURING,"si_document_capturing",e.captureProgress??0):s(t,r,e.captureProgress??0)}case i.Satisfied:return scannedDocumentUiState();case i.Unsatisfied:return s(n.IDLE,"si_document_capture_unsatisfied",0);case i.Finished:return scannedDocumentUiState();case i.Timeout:return s(n.IDLE,"si_capture_timeout",0)}}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deletes the frames abandoned takes left on disk that the kept set doesn't use: a take that
|
|
3
|
+
* started over can name its frames anew (by time) or reuse the old names (by index). Best effort,
|
|
4
|
+
* as the flow's own clean-up is.
|
|
5
|
+
*/
|
|
6
|
+
export declare function deleteDroppedFrames(dropped: ReadonlySet<string>, kept: readonly string[]): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{File as r}from"expo-file-system";export function deleteDroppedFrames(o,i){const n=new Set(i);for(const e of o)if(!n.has(e))try{const t=new r(e);t.exists&&t.delete()}catch{}}
|
|
@@ -24,3 +24,5 @@ export declare function essTurnHaptic(previous: UseSmileIDScanState, next: UseSm
|
|
|
24
24
|
* intentionally swallowed.
|
|
25
25
|
*/
|
|
26
26
|
export declare function triggerEssTurnHaptic(kind: EssTurnHaptic): void;
|
|
27
|
+
/** The success haptic that confirms the selfie's end; failures are ignored, as above. */
|
|
28
|
+
export declare function triggerCaptureCompleteHaptic(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import*as
|
|
1
|
+
"use strict";import*as i from"expo-haptics";import{UseSmileIDScanStateType as n}from"@smileid/usesmileid_bridge";export function essTurnHaptic(e,t){return e.type!==n.HeadTurn||e.currentDirection===void 0?null:t.type===n.HeadTurn&&t.currentDirection!==void 0&&t.currentDirection!==e.currentDirection?"intermediate":t.type===n.Found||t.type===n.Satisfied||t.type===n.Finished?"final":null}export function triggerEssTurnHaptic(e){(e==="final"?i.notificationAsync(i.NotificationFeedbackType.Success):i.impactAsync(i.ImpactFeedbackStyle.Light)).catch(()=>{})}export function triggerCaptureCompleteHaptic(){i.notificationAsync(i.NotificationFeedbackType.Success).catch(()=>{})}
|
|
@@ -27,6 +27,9 @@ export type ResolvedInstructions = {
|
|
|
27
27
|
* Swaps the default selfie hero for the ESS hero when enhanced liveness is on
|
|
28
28
|
* and the hero is unset; a partner-supplied hero is always respected.
|
|
29
29
|
*/
|
|
30
|
+
type DocumentNameKey = "siDocumentTypePassport" | "siDocumentTypeGreenBook" | "siDocumentTypeGeneric";
|
|
31
|
+
/** A preset's, or the generic default's, `si_document_type_*` string; a partner-set name as given. */
|
|
32
|
+
export declare function localizedDocumentName(documentType: DocumentType, localizations: Pick<UseSmileIDLocalizations, DocumentNameKey>): string;
|
|
30
33
|
export declare function resolvedHeroAnimation(heroAnimation: HeroAnimationSource | undefined, enhancedLivenessEnabled: boolean): HeroAnimationSource;
|
|
31
34
|
/**
|
|
32
35
|
* Resolve the config into concrete screen props (explicit value > localized /
|
|
@@ -38,4 +41,5 @@ export declare function resolvedHeroAnimation(heroAnimation: HeroAnimationSource
|
|
|
38
41
|
export declare function resolveInstructionsConfig(config: InstructionsConfigInput, options: {
|
|
39
42
|
readonly enhancedLivenessEnabled: boolean;
|
|
40
43
|
readonly documentType?: DocumentType;
|
|
41
|
-
}, localizations: Pick<UseSmileIDLocalizations, "siInstructionsTitlePrefix" | "siInstructionsEmphasisSelfie" | "siInstructionsCaptureGuidelines">): ResolvedInstructions;
|
|
44
|
+
}, localizations: Pick<UseSmileIDLocalizations, "siInstructionsTitlePrefix" | "siInstructionsEmphasisSelfie" | "siInstructionsCaptureGuidelines" | DocumentNameKey>): ResolvedInstructions;
|
|
45
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{documentGuidelines as a,documentHeroAnimation as
|
|
1
|
+
"use strict";import{DocumentType as m}from"../../../core/models/document_type";import{documentGuidelines as a,documentHeroAnimation as l,ESS_HERO_ANIMATION as p,SELFIE_GUIDELINES as E,SELFIE_HERO_ANIMATION as h}from"../../flow/model/instructions_defaults";export function localizedDocumentName(e,n){switch(e.kind){case"passport":return n.siDocumentTypePassport;case"southAfricaGreenBook":return n.siDocumentTypeGreenBook;case"genericDocument":return e.displayName===I?n.siDocumentTypeGeneric:e.displayName}}const I=m.GenericDocument().displayName;export function resolvedHeroAnimation(e,n){return n&&e===void 0?p:e??h}export function resolveInstructionsConfig(e,n,s){const r=n.enhancedLivenessEnabled,i=n.documentType,t=i!==void 0,o=t?a(i):E,u=e.guidelines&&e.guidelines.length>0?e.guidelines:o,c=e.titleEmphasis??(t?localizedDocumentName(i,s):s.siInstructionsEmphasisSelfie),d=t?e.heroAnimation??l(i):resolvedHeroAnimation(e.heroAnimation,r);return{titlePrefix:e.titlePrefix??s.siInstructionsTitlePrefix,titleEmphasis:c,heroSource:d,guidelinesSectionTitle:e.guidelinesSectionTitle??s.siInstructionsCaptureGuidelines,guidelines:u,showHeroOval:t?!1:e.showHeroOval??!0,showHeroOvalProgress:t?!1:!r,showCardBackground:t}}
|
|
@@ -7,6 +7,18 @@ export type CaptureOrientationOptions = {
|
|
|
7
7
|
*/
|
|
8
8
|
readonly exitLock?: ScreenOrientation.OrientationLock;
|
|
9
9
|
};
|
|
10
|
+
/**
|
|
11
|
+
* How long after the window last changed size a capture screen stops waiting for it to reach its
|
|
12
|
+
* lock and shows anyway, so a host that never turns (a tablet that ignores the lock) still gets its
|
|
13
|
+
* camera.
|
|
14
|
+
*/
|
|
15
|
+
export declare const ORIENTATION_SETTLE_TIMEOUT_MS = 1500;
|
|
16
|
+
/**
|
|
17
|
+
* The fallback where the OS may ignore the lock (see {@link lockMayBeIgnored}): long enough for a
|
|
18
|
+
* window that does turn to start resizing, which restarts the wait, short enough that one that
|
|
19
|
+
* doesn't isn't held for long.
|
|
20
|
+
*/
|
|
21
|
+
export declare const ORIENTATION_LOCK_IGNORED_TIMEOUT_MS = 400;
|
|
10
22
|
/**
|
|
11
23
|
* Lock device orientation for a capture screen, scoped to its lifetime
|
|
12
24
|
* (`"Landscape"` → landscape, `"Portrait"` → portrait-up).
|
|
@@ -15,11 +27,21 @@ export type CaptureOrientationOptions = {
|
|
|
15
27
|
* reverts deterministically rather than depending on the previous
|
|
16
28
|
* screen's teardown winning a race against the next camera initialising.
|
|
17
29
|
*
|
|
30
|
+
* Returns whether the window has the lock's shape (landscape = wider than
|
|
31
|
+
* tall). A camera mounted before then is laid out for the old orientation and
|
|
32
|
+
* visibly re-lays out mid-turn (a retake from the portrait preview back to a
|
|
33
|
+
* landscape document), so the caller holds the camera until this is true. It
|
|
34
|
+
* also turns true when the lock fails, the host cannot honour it, or
|
|
35
|
+
* {@link ORIENTATION_SETTLE_TIMEOUT_MS} passes after the window last changed
|
|
36
|
+
* size. A tablet waits like a phone, since some turn for the lock; an Android
|
|
37
|
+
* 16+ window of 600 dp and up, where the OS may ignore it, gives up after
|
|
38
|
+
* {@link ORIENTATION_LOCK_IGNORED_TIMEOUT_MS} instead.
|
|
39
|
+
*
|
|
18
40
|
* Locks are global: the host app must permit the locked orientations at
|
|
19
41
|
* the OS level (iOS `UISupportedInterfaceOrientations`, Android activity
|
|
20
42
|
* `screenOrientation`) for the lock to take effect — a host that does not
|
|
21
43
|
* is warned about and reported rather than silently staying upright.
|
|
22
44
|
*/
|
|
23
|
-
export declare function useCaptureOrientationLock(orientation: DocumentOrientation, { exitLock, }?: CaptureOrientationOptions):
|
|
45
|
+
export declare function useCaptureOrientationLock(orientation: DocumentOrientation, { exitLock, }?: CaptureOrientationOptions): boolean;
|
|
24
46
|
/** Test seam: clears the once-per-target dev-warning dedupe. */
|
|
25
47
|
export declare function resetUnsupportedLockWarningsForTesting(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{useEffect as
|
|
1
|
+
"use strict";import{useEffect as f,useRef as d,useState as _}from"react";import{Platform as O,useWindowDimensions as S}from"react-native";import*as c from"expo-screen-orientation";import{UseSmileIDCrashReporting as w}from"@smileid/usesmileid_bridge";import{isDevelopmentBuild as L}from"../../../core/is_development_build";export const ORIENTATION_SETTLE_TIMEOUT_MS=1500,ORIENTATION_LOCK_IGNORED_TIMEOUT_MS=400;const A=600,D=36;function k(e,t){return O.OS==="android"&&typeof O.Version=="number"&&O.Version>=D&&Math.min(e,t)>=A}export function useCaptureOrientationLock(e,{exitLock:t=c.OrientationLock.PORTRAIT_UP}={}){const r=S(),s=r.width>r.height==(e==="Landscape"),h=k(r.width,r.height)?ORIENTATION_LOCK_IGNORED_TIMEOUT_MS:ORIENTATION_SETTLE_TIMEOUT_MS,a=d(h);a.current=h;const[E,l]=_(null),n=d(void 0),o=d(null);return f(()=>{const i=e==="Landscape"?c.OrientationLock.LANDSCAPE:c.OrientationLock.PORTRAIT_UP;let I=!0;const u=()=>{I&&(o.current=null,l(e))};return o.current=u,n.current=setTimeout(u,a.current),U(i).then(p=>{p||u()}),R(i).then(p=>{p||u()}),()=>{I=!1,o.current=null,clearTimeout(n.current),l(null),R(t)}},[e,t]),f(()=>{const i=o.current;i!==null&&(clearTimeout(n.current),n.current=setTimeout(i,a.current))},[r.width,r.height]),f(()=>{s&&(clearTimeout(n.current),o.current=null,l(e))},[s,e]),E===e||s}async function U(e){let t;try{t=await c.supportsOrientationLockAsync(e)}catch(r){return T(r),!1}return t?!0:(L()&&!m.has(e)&&(m.add(e),console.warn("[UseSmileID] The capture screen needs to lock the device orientation, but this build does not permit it. Allow the required orientations in your app's iOS Info.plist / Android manifest.")),T(new Error(`orientation lock unsupported by host build: ${e}`)),!1)}const m=new Set;export function resetUnsupportedLockWarningsForTesting(){m.clear()}async function R(e){try{return await c.lockAsync(e),!0}catch(t){return T(t),!1}}function T(e){w.captureException(e instanceof Error?e:new Error(String(e)))}
|
|
@@ -5,4 +5,4 @@ import type { SiKey } from "../../../l10n/strings_en";
|
|
|
5
5
|
* The rim, fill, and head-turn direction must NOT go through this hook.
|
|
6
6
|
* Bump `sessionEpoch` on a session restart to reset and re-seed.
|
|
7
7
|
*/
|
|
8
|
-
export declare function useDebouncedFeedbackKey(feedbackKey: SiKey, sessionEpoch?: number): SiKey;
|
|
8
|
+
export declare function useDebouncedFeedbackKey(feedbackKey: SiKey, sessionEpoch?: number, immediate?: boolean): SiKey;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{useEffect as
|
|
1
|
+
"use strict";import{useEffect as s,useRef as u,useState as f}from"react";import{UseSmileIDMotion as a}from"../../theme/use_smile_id_motion";import{UseSmileIDFeedbackDebouncer as d}from"./feedback_debouncer";const p=new Set(["si_selfie_capture_complete","si_document_capture_complete"]);export function useDebouncedFeedbackKey(t,r=0,c=!1){const[l,m]=f(t),o=u(c);o.current=c;const n=u(null);n.current===null&&(n.current=new d({emit:m,delayMs:a.feedbackDebounce,isImmediate:e=>p.has(e)||o.current,initialValue:t}));const i=u(r);return s(()=>{const e=n.current;if(e){if(i.current!==r){i.current=r,e.reset(),e.seed(t);return}e.publish(t)}},[t,r]),s(()=>()=>n.current?.dispose(),[]),l}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
+
import type { SelfieCaptureGeometry } from "../components/helpers/captured_oval_region";
|
|
2
3
|
import type { DocumentAnalyzer, FaceAnalyzer } from "@smileid/usesmileid_platform_interface";
|
|
3
4
|
import type { CaptureReadyEvent, UseSmileIDScanState } from "@smileid/usesmileid_bridge";
|
|
4
5
|
import { DocumentSide } from "@smileid/usesmileid_bridge";
|
|
@@ -39,7 +40,9 @@ export type UseSmileIDCaptureScreenProps = {
|
|
|
39
40
|
* `documentBorderFraction` is the hidden border an auto-captured document was saved with, as a
|
|
40
41
|
* fraction of its short side; 0 for a selfie, a manual-shutter still or a gallery import.
|
|
41
42
|
*/
|
|
42
|
-
readonly onCaptureComplete?: (events: readonly CaptureReadyEvent[], telemetry: CaptureTelemetry, documentBorderFraction?: number
|
|
43
|
+
readonly onCaptureComplete?: (events: readonly CaptureReadyEvent[], telemetry: CaptureTelemetry, documentBorderFraction?: number,
|
|
44
|
+
/** @internal What a selfie's capture screen laid out, for the review screens. */
|
|
45
|
+
selfieCaptureGeometry?: SelfieCaptureGeometry) => void;
|
|
43
46
|
/** Records how a document side was produced (auto/manual/gallery), fired just before delivery. */
|
|
44
47
|
readonly onDocumentOrigin?: (origin: DocumentImageOrigin) => void;
|
|
45
48
|
/** Fires when the state machine reaches `"timeout"`; the screen stops streaming. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{jsx as v,Fragment as qe,jsxs as ve}from"react/jsx-runtime";import{useCallback as B,useEffect as f,useMemo as V,useRef as s,useState as h}from"react";import{Platform as Ee,StatusBar as ze,StyleSheet as ke,View as Ne}from"react-native";import*as Me from"expo-image-picker";import{CaptureReadyEventKind as K,DocumentSide as Ye,UseSmileIDScanStateType as y}from"@smileid/usesmileid_bridge";import{CaptureType as te}from"../../core/models/ml_config";import{resampleImportedDocument as be}from"../../camera/document_image_pipeline";import{defaultLightTheme as $e}from"../theme/theme";import{UseSmileIDThemeProvider as Je}from"../theme/theme_context";import{UseSmileIDCaptureContent as Qe}from"../components/use_smile_id_capture_content";import{DEFAULT_DOCUMENT_ASPECT as Xe,documentGuideRect as Ze}from"../components/use_smile_id_document_overlay";import{UseSmileIDCrashReporting as E}from"@smileid/usesmileid_bridge";import{ForceMaxBrightness as et}from"../utils/force_max_brightness";import{captureDebugBoxesMirrored as Ae}from"../../debug/capture_debug_geometry";import{DocumentCaptureDebugOverlayHost as tt}from"../../debug/use_smile_id_document_capture_debug_overlay";import{SelfieCaptureDebugOverlayHost as rt}from"../../debug/use_smile_id_selfie_capture_debug_overlay";import{CameraDefaults as Re}from"../../camera/camera_defaults";import{DocumentImageOrigin as re}from"../../metadata/document_image_origin";import{DeviceOrientationCollector as nt}from"../../metadata/device_orientation_collector";import{useSmileIDFrameStream as at}from"../../camera/use_smile_id_frame_stream";import{CameraLens as j}from"../../camera/camera_lens";import{UseSmileIDKameraCameraSystem as it}from"../../camera/use_smile_id_kamera_camera_system";import{UseSmileIDKameraPreview as ot}from"../../camera/use_smile_id_kamera_preview";import{useDeviceTiltDegrees as ct,useLockedInterfaceOrientationDegrees as st}from"../../camera/use_kamera_orientation";import{useKameraCameraPermission as ut}from"../../camera/use_kamera_camera_permission";import{isTerminalKameraError as lt}from"../../camera/kamera_error_mapping";import{isSessionBoundary as mt,mapCaptureUiState as dt,preCaptureUiState as ft}from"./helpers/capture_ui_state";import{useDebouncedFeedbackKey as ht}from"./helpers/use_debounced_feedback_key";import{essTurnHaptic as pt,triggerEssTurnHaptic as yt}from"./helpers/ess_turn_haptics";const St=33;export function UseSmileIDCaptureScreen(t){const k=t.theme??$e,L=t.showAttribution??!0,S=t.enableEnhancedLiveness??!1,b=t.allowAgentMode??!1,A=t.captureType===te.selfie&&!b?"front":"back",{hasPermission:R,requestPermission:ne}=ut(),d=V(()=>new it(A==="front"?j.front:j.back,t.createCameraSession),[]),ae=s(!1);f(()=>{!R&&!ae.current&&(ae.current=!0,ne())},[R,ne]);const ie=t.onCameraError;f(()=>{if(!R)return;const e=d.addEventListener(r=>{r.type==="error"&<(r.code)&&ie?.(r.code,r.message)});return d.configure({captureType:t.captureType,lens:A==="front"?j.front:j.back}),d.start().catch(r=>{E.captureException(r instanceof Error?r:new Error(String(r)))}),()=>{e(),d.release()}},[R,d,A,t.captureType,ie]);const[oe,ce]=h({type:y.Initial}),[i,u]=h(!1),[Ie,se]=h(0),[I,ue]=h(!1),[_e,Pe]=h(!1),[q,z]=h(!1),w=s(!1),[le,N]=h(!1),[Fe,g]=h(!1),[G,Y]=h(null);f(()=>{if(G===null)return;const e=setTimeout(()=>Y(null),3e3);return()=>clearTimeout(e)},[G]);const D=s([]),a=s(!1),$=s(null),_=s(null),J=s(0),P=s(!1),F=s(null),H=s(new nt),Q=B(()=>({durationMs:_.current===null?0:Date.now()-_.current,retries:J.current,devicePositions:H.current.devicePositions,deviceMovementDetected:H.current.deviceMovementDetected}),[]),me=t.onStartCapture,X=B(()=>{if(i||a.current)return;me?.(),se(r=>r+1),$.current=null;const e=w.current;w.current=!1,z(!1),N(!1),ce({type:y.Initial}),ue(!1),D.current=[],P.current=!1,F.current=null,e?t.analyzer.reset().catch(r=>E.captureException(r)).finally(()=>{a.current||u(!0)}):u(!0)},[i,me,t.analyzer]),U=t.analyzer,x=t.onCaptureComplete,O=t.onDocumentOrigin,Z=t.onCaptureTimeout,Ue=t.onAnalyticsEvent,W=t.captureMode,xe=t.allowGalleryUpload??!1,C=t.documentSide??Ye.Front,l=t.captureType===te.document;f(()=>{_.current=null,J.current=0,P.current=!1,F.current=null},[C]),f(()=>(i&&H.current.start(),()=>H.current.stop()),[i]);const ee=W?.kind==="autoCaptureWithManualFallback"?W.activateManualAfterMs:null;f(()=>{if(!i||ee===null)return;const e=setTimeout(()=>{Pe(!0)},ee);return()=>clearTimeout(e)},[i,ee]);const Oe=B(()=>{a.current||(a.current=!0,u(!1),g(!0),(async()=>{try{const e=await d.capture(),r=e.filePath.startsWith("file://")?e.filePath:`file://${e.filePath}`,o=await be({uri:r,width:e.width,height:e.height},C);O?.(re.CameraManualCapture),x?.([{kind:K.Document,captureIndex:0,url:o}],Q())}catch(e){E.captureException(e instanceof Error?e:new Error(String(e))),a.current=!1,g(!1),u(!0)}})())},[x,O,C]),Be=B(()=>{a.current||(u(!1),g(!0),(async()=>{try{if(!(await Me.requestMediaLibraryPermissionsAsync()).granted){g(!1),a.current||u(!0);return}const r=await Me.launchImageLibraryAsync({mediaTypes:["images"],allowsMultipleSelection:!1,quality:1}),o=r.canceled?void 0:r.assets[0];if(o===void 0){g(!1),a.current||u(!0);return}if(o.width!==void 0&&o.height!==void 0&&Math.min(o.width,o.height)<Re.minGalleryDocumentDimensionPx){Y("si_document_gallery_image_too_small"),g(!1),a.current||u(!0);return}if(a.current)return;a.current=!0;const M=await be({uri:o.uri,width:o.width,height:o.height},C);O?.(re.Gallery),x?.([{kind:K.Document,captureIndex:0,url:M}],Q())}catch(e){E.captureException(e instanceof Error?e:new Error(String(e))),Y("si_document_gallery_image_failed"),a.current=!1,g(!1),u(!0)}})())},[x,O,C]),de=s(!1),fe=s(X);fe.current=X,f(()=>{!l||de.current||a.current||(de.current=!0,fe.current())},[l]);const Ke=at(i?U:null,e=>{const r=$.current;if(r!==null&&mt(r,e.state)&&se(n=>n+1),r!==null){const n=pt(r,e.state);n!==null&&yt(n)}$.current=e.state,_.current===null&&(_.current=Date.now()),F.current??=Date.now();const o=e.state.type===y.Unsatisfied,M=o&&!P.current,T=D.current.filter(n=>n.kind===K.Selfie||n.kind===K.Liveness).length,je=F.current;if(M?J.current+=1:!o&&P.current&&(F.current=Date.now()),P.current=o,e.state.type===y.Unsatisfied?D.current=[]:e.capturedEvents.length>0&&(D.current=[...D.current,...e.capturedEvents]),M&&!l&&Ue?.({kind:"selfieSessionRestarted",reason:e.state.reason??"unknown",framesDiscarded:T,durationMs:Date.now()-je}),ce(n=>n.type===e.state.type&&n.feedback===e.state.feedback&&n.reason===e.state.reason&&n.currentDirection===e.state.currentDirection&&(n.poseMatched??!1)===(e.state.poseMatched??!1)&&Math.round((n.captureProgress??0)*100)===Math.round((e.state.captureProgress??0)*100)?n:e.state),ue(!0),e.state.type===y.Finished){if(a.current||w.current)return;a.current=!0,u(!1),U.flush().catch(n=>{E.captureException(n)}).then(()=>wt(U,D.current)).then(n=>{O?.(re.CameraAutoCapture),x?.(D.current,Q(),n)})}else if(e.state.type===y.Timeout){if(a.current||w.current)return;u(!1),w.current=!0,z(!0),Z?.(e.state)}},{onError:e=>{E.captureException(e)}}),p=I?dt(oe,t.captureType,S):ft(t.captureType,S),he=ht(p.feedbackKey,Ie),pe=(oe.type===y.Initial&&!i||q)&&!l,Le=!i&&!a.current,ye=R,Se=s(Z);Se.current=Z,f(()=>{if(!i||I)return;const e=setTimeout(()=>{a.current||w.current||(w.current=!0,N(!0),z(!0),u(!1),Se.current?.({type:y.Timeout}))},Re.firstFrameTimeoutMs);return()=>clearTimeout(e)},[i,I]),f(()=>{I&&N(!1)},[I]);const we=Ke.onFrameResult,ge=ct({retainWhenFlat:l}),De=st(),[m,Ge]=h({width:0,height:0}),He=B(e=>{Ge(r=>r.width===e.width&&r.height===e.height?r:e)},[]),c=V(()=>{if(!l||m.width<=0||m.height<=0)return;const e=t.knownIdAspectRatio??Xe,r=Ze(e,m.width,m.height);return{left:r.left/m.width,top:r.top/m.height,width:r.width/m.width,height:r.height/m.height,viewportWidthPx:Math.round(m.width),viewportHeightPx:Math.round(m.height)}},[l,m,t.knownIdAspectRatio]);f(()=>{if(!i)return;let e=!1,r=!1;const M=setInterval(()=>{e||r||(e=!0,U.analyzeKameraFrame({rotation:ge,viewportOrientation:De,cropLeft:c?c.left:0,cropTop:c?c.top:0,cropWidth:c?c.width:0,cropHeight:c?c.height:0,cropViewportWidth:c?c.viewportWidthPx:0,cropViewportHeight:c?c.viewportHeightPx:0}).then(T=>{r||we(T)}).catch(T=>{E.captureException(T instanceof Error?T:new Error(String(T)))}).finally(()=>{e=!1}))},St);return()=>{r=!0,clearInterval(M)}},[i,U,we,ge,De,c]);const Ce=l&&(q||i&&(W?.kind==="manualCapture"||W?.kind==="autoCaptureWithManualFallback"&&_e)),Te=l&&(i||q)&&xe,We=V(()=>({captureType:t.captureType,overlayState:p.overlayState,enableEnhancedLiveness:S,animationDirection:p.animationDirection,poseMatched:p.poseMatched,feedbackKey:le?"si_capture_camera_unavailable":G??he,showCaptureButton:pe,showManualShutter:Ce,showGalleryUpload:Te,documentSide:C,...t.knownIdAspectRatio!==void 0?{knownIdAspectRatio:t.knownIdAspectRatio}:{},...t.documentTypeKind!==void 0?{documentTypeKind:t.documentTypeKind}:{}}),[t.captureType,p.overlayState,S,p.animationDirection,p.poseMatched,he,G,le,pe,l,Ce,Te,C,t.knownIdAspectRatio,t.documentTypeKind]),Ve=V(()=>ve(qe,{children:[v(et,{}),ye?v(ot,{cameraSystem:d,style:ke.absoluteFill,testID:"si_capture_camera_preview"}):v(Ne,{style:[ke.absoluteFill,{backgroundColor:k.colors.offBlack}],testID:"si_capture_camera_placeholder"})]}),[ye,d,k.colors.offBlack]);return ve(Je,{value:k,children:[v(ze,{translucent:!0,backgroundColor:"transparent",barStyle:"dark-content"}),v(Qe,{state:We,captureProgress:p.captureProgress,cameraPreview:Ve,onStartCapture:X,startCaptureEnabled:Le,onBack:t.onBack,showAttribution:L,startButton:t.startButton,onViewportLayout:He,...l?{onManualShutter:Oe,onGalleryUpload:Be,documentControlsEnabled:!Fe,...t.onClose?{onClose:t.onClose}:{}}:{},theme:k}),t.captureType===te.selfie?v(rt,{frameAspect:d.uprightPreviewAspect,mirrorBoxes:Ae(A==="front",Ee.OS==="android")}):l?v(tt,{streamAspect:d.uprightPreviewAspect,mirrorBoxes:Ae(A==="front",Ee.OS==="android")}):null]})}async function wt(t,k){if(t.kind!=="document"||!t.documentBorderFraction)return 0;const L=k.find(b=>b.kind===K.Document);if(!L)return 0;let S;try{return await Promise.race([t.documentBorderFraction(L.url),new Promise(b=>{S=setTimeout(()=>b(0),gt)})])}catch{return 0}finally{clearTimeout(S)}}const gt=1e3;
|
|
1
|
+
"use strict";import{jsx as _,Fragment as mt,jsxs as He}from"react/jsx-runtime";import{useCallback as x,useEffect as p,useLayoutEffect as We,useMemo as Z,useRef as i,useState as h}from"react";import{Platform as Ge,StatusBar as pt,StyleSheet as Ve,View as ht}from"react-native";import*as qe from"expo-image-picker";import{CaptureReadyEventKind as k,DocumentSide as gt,UseSmileIDScanStateType as P}from"@smileid/usesmileid_bridge";import{CaptureType as z}from"../../core/models/ml_config";import{resampleImportedDocument as Ne}from"../../camera/document_image_pipeline";import{defaultLightTheme as St}from"../theme/theme";import{UseSmileIDThemeProvider as yt}from"../theme/theme_context";import{UseSmileIDCaptureContent as wt}from"../components/use_smile_id_capture_content";import{DEFAULT_DOCUMENT_ASPECT as kt,documentGuideRect as Ct}from"../components/use_smile_id_document_overlay";import{UploadImageCheck as Et,UseSmileIDCrashReporting as C}from"@smileid/usesmileid_bridge";import{ForceMaxBrightness as Dt}from"../utils/force_max_brightness";import{captureDebugBoxesMirrored as ze}from"../../debug/capture_debug_geometry";import{UseSmileIDDebugHooks as je}from"../../debug/use_smile_id_debug";import{CapturePerfMark as E,CapturePerfTrace as he,capturePerfOutcomeContinues as Tt}from"../../core/perf/capture_perf_trace";import{DocumentCaptureDebugOverlayHost as Rt}from"../../debug/use_smile_id_document_capture_debug_overlay";import{SelfieCaptureDebugOverlayHost as At}from"../../debug/use_smile_id_selfie_capture_debug_overlay";import{largestFace as vt}from"../../camera/bounding_box_metering_region";import{CameraDefaults as It}from"../../camera/camera_defaults";import{DocumentImageOrigin as ge}from"../../metadata/document_image_origin";import{DeviceOrientationCollector as Pt}from"../../metadata/device_orientation_collector";import{useSmileIDFrameStream as Mt}from"../../camera/use_smile_id_frame_stream";import{CameraLens as ee}from"../../camera/camera_lens";import{SELFIE_ANALYSIS_INTERVAL_MS as bt}from"../../camera/kamera_config_factory";import{deleteDroppedFrames as _t}from"./helpers/dropped_frames";import{setKameraMetricsWanted as xt,UseSmileIDKameraCameraSystem as Ft}from"../../camera/use_smile_id_kamera_camera_system";import{UseSmileIDKameraPreview as Ut}from"../../camera/use_smile_id_kamera_preview";import{useDeviceTiltDegrees as Lt,useLockedInterfaceOrientationDegrees as Ot}from"../../camera/use_kamera_orientation";import{useKameraCameraPermission as Kt}from"../../camera/use_kamera_camera_permission";import{isTerminalKameraError as Bt}from"../../camera/kamera_error_mapping";import{isSessionBoundary as Ht,mapCaptureUiState as Wt,preCaptureUiState as Gt,steppedTurnProgress as Ye}from"./helpers/capture_ui_state";import{useDebouncedFeedbackKey as Vt}from"./helpers/use_debounced_feedback_key";import{essTurnHaptic as qt,triggerCaptureCompleteHaptic as Nt,triggerEssTurnHaptic as zt}from"./helpers/ess_turn_haptics";import{UnsatisfiedReasons as Se}from"../flow/analytics/unsatisfied_reasons";const jt=33,Yt=Math.round(bt/3);export function UseSmileIDCaptureScreen(t){const F=t.theme??St,j=t.showAttribution??!0,A=t.enableEnhancedLiveness??!1,U=t.captureType===z.selfie,$e=t.allowAgentMode??!1,O=t.captureType===z.selfie&&!$e?"front":"back",{hasPermission:K,requestPermission:ye}=Kt(),l=Z(()=>new Ft(O==="front"?ee.front:ee.back,t.createCameraSession),[]),we=i(!1);p(()=>{!K&&!we.current&&(we.current=!0,ye())},[K,ye]);const[B]=h(()=>je.isPerfTraceEnabled()),[Xe]=h(()=>B?new he:null),f=i(Xe),ke=i(!1),H=i(!1);p(()=>{B&&f.current===null&&(f.current=new he)},[B,t.documentSide]);const Ce=i(t.onCameraError);We(()=>{Ce.current=t.onCameraError},[t.onCameraError]),p(()=>{if(!K)return;const e=l.addEventListener(r=>{r.type==="error"&&Bt(r.code)?Ce.current?.(r.code,r.message):r.type==="metric"&&(r.metric.name!=="frameAnalyzed"||!v.current)&&f.current?.onMetric(r.metric)});return xt(B),l.configure({captureType:t.captureType,lens:O==="front"?ee.front:ee.back}),f.current?.mark(E.cameraStartRequested),l.start().then(()=>{f.current?.mark(E.cameraRunning)}).catch(r=>{C.captureException(r instanceof Error?r:new Error(String(r)))}),()=>{e(),l.release()}},[K,l,O,t.captureType,B]);const[Ee,te]=h({type:P.Initial}),[u,S]=h(!1);p(()=>{u&&f.current?.mark(E.streamingStarted)},[u]);const[Je,De]=h(0),[Qe,re]=h(!1),[Ze,et]=h(!1),[ne,Te]=h(!1),v=i(!1),[ae,Re]=h(!1),[ce,ie]=h(!1),Y=i(!1),Ae=i(0),$=i(!0);p(()=>($.current=!0,()=>{$.current=!1}),[]);const[tt,M]=h(!1),[X,oe]=h(null);p(()=>{if(X===null)return;const e=setTimeout(()=>oe(null),3e3);return()=>clearTimeout(e)},[X]);const y=i([]),se=i(new Set),o=i(!1),ue=i(null),W=i(null),le=i(0),G=i(!1),L=i(null),J=i(new Pt),fe=x(()=>({durationMs:W.current===null?0:Date.now()-W.current,retries:le.current,devicePositions:J.current.devicePositions,deviceMovementDetected:J.current.deviceMovementDetected}),[]),ve=t.onStartCapture,de=x(()=>{if(u||o.current||Y.current)return;Y.current=!0,ie(!0),H.current=!0,ve?.(),De(r=>r+1),ue.current=null;const e=v.current;v.current=!1,Te(!1),Re(!1),te({type:P.Initial}),re(!1),y.current=[],G.current=!1,L.current=null,e?t.analyzer.reset().catch(r=>C.captureException(r)).finally(()=>{Y.current=!1,ie(!1),o.current||S(!0)}):(Y.current=!1,ie(!1),S(!0))},[u,ve,t.analyzer]),b=t.analyzer,V=t.onCaptureComplete,q=t.onDocumentOrigin,rt=t.onCaptureTimeout,me=t.onAnalyticsEvent,Q=t.captureMode,nt=t.allowGalleryUpload??!1,D=t.documentSide??gt.Front,g=x(e=>{const r=f.current;if(!r)return;const a=ke.current,T=H.current,m=Tt(e);if(f.current=m?new he:null,ke.current=m,H.current=!1,e==="abandoned"&&a&&!T&&r.snapshot().marks[E.streamingStarted]===void 0)return;const N=t.captureType===z.document?`document ${D}`:String(t.captureType),n=r.report(N,e);console.log(n.line);const w=je.perfTraceListener();w&&setTimeout(()=>{try{w(n)}catch(R){C.captureException(R instanceof Error?R:new Error(String(R)))}},0)},[t.captureType,D]),Ie=i(g);We(()=>{Ie.current=g},[g]),p(()=>()=>Ie.current("abandoned"),[]);const s=t.captureType===z.document;p(()=>{W.current=null,le.current=0,G.current=!1,L.current=null},[D]),p(()=>(u&&J.current.start(),()=>J.current.stop()),[u]);const pe=Q?.kind==="autoCaptureWithManualFallback"?Q.activateManualAfterMs:null;p(()=>{if(!u||pe===null)return;const e=setTimeout(()=>{et(!0)},pe);return()=>clearTimeout(e)},[u,pe]);const at=x(()=>{o.current||(H.current=!0,o.current=!0,S(!1),M(!0),(async()=>{try{const e=await l.capture(),r=e.filePath.startsWith("file://")?e.filePath:`file://${e.filePath}`,a=await Ne({uri:r,width:e.width,height:e.height},D);f.current?.mark(E.captureComplete),f.current?.mark(E.filesReady),q?.(ge.CameraManualCapture),V?.([{kind:k.Document,captureIndex:0,url:a}],fe()),g("captured")}catch(e){C.captureException(e instanceof Error?e:new Error(String(e))),g("failed"),o.current=!1,M(!1),S(!0)}})())},[V,q,D,g]),ct=x(()=>{o.current||(H.current=!0,S(!1),M(!0),(async()=>{try{if(!(await qe.requestMediaLibraryPermissionsAsync()).granted){M(!1),o.current||S(!0);return}const r=await qe.launchImageLibraryAsync({mediaTypes:["images"],allowsMultipleSelection:!1,quality:1}),a=r.canceled?void 0:r.assets[0];if(a===void 0){M(!1),o.current||S(!0);return}if(a.width!==void 0&&a.height!==void 0&&Math.min(a.width,a.height)<It.minGalleryDocumentDimensionPx){oe("si_document_gallery_image_too_small"),M(!1),o.current||S(!0);return}if(o.current)return;o.current=!0;const T=await Ne({uri:a.uri,width:a.width,height:a.height},D);f.current?.mark(E.filesReady),q?.(ge.Gallery),V?.([{kind:k.Document,captureIndex:0,url:T}],fe()),g("gallery")}catch(e){C.captureException(e instanceof Error?e:new Error(String(e))),g("failed"),oe("si_document_gallery_image_failed"),o.current=!1,M(!1),S(!0)}})())},[V,q,D,g]),Pe=i(!1),Me=i(de);Me.current=de,p(()=>{!s||Pe.current||o.current||(Pe.current=!0,Me.current())},[s]);const it=x(async()=>{const e=y.current,r=[...e.filter(n=>n.kind===k.Selfie),...e.filter(n=>n.kind===k.Liveness).sort((n,w)=>n.captureIndex-w.captureIndex)];if(r.length===0)return!1;const a=r.map(n=>n.url),m=(await Et.restartReasons(a))[0];if(m===void 0)return!1;if(!$.current)return!0;if(Ae.current>=Jt)return!1;Ae.current+=1;const N=L.current??Date.now();return y.current=[],o.current=!1,v.current=!0,te({type:P.Initial}),re(!1),Re(!0),me?.({kind:"selfieSessionRestarted",reason:m.startsWith(Se.uploadCheckPrefix)?m:Se.fallback,framesDiscarded:a.length,durationMs:Date.now()-N}),!0},[me]),ot=Mt(u?b:null,e=>{const r=ue.current;if(r!==null&&Ht(r,e.state)&&De(n=>n+1),r!==null){const n=qt(r,e.state);n!==null&&zt(n)}if(ue.current=e.state,!s)l.meterExposure(vt(e.output.faces));else{const{documentGuide:n}=e;l.setDocumentGuide(n??null)}v.current||f.current?.mark(E.firstScanResult),W.current===null&&(W.current=Date.now()),L.current??=Date.now();const a=e.state.type===P.Unsatisfied,T=a&&!G.current,m=y.current.filter(n=>n.kind===k.Selfie||n.kind===k.Liveness).length,N=L.current;if(T?le.current+=1:!a&&G.current&&(L.current=Date.now()),G.current=a,e.state.type===P.Unsatisfied)y.current=[];else if(e.capturedEvents.length>0){const w=e.capturedEvents.some(R=>R.kind===k.Selfie)?y.current.filter(R=>{const Be=R.kind===k.Selfie||R.kind===k.Liveness;return Be&&se.current.add(R.url),!Be}):y.current;y.current=[...w,...e.capturedEvents]}if(T&&!s&&me?.({kind:"selfieSessionRestarted",reason:e.state.reason??Se.fallback,framesDiscarded:m,durationMs:Date.now()-N}),te(n=>n.type===e.state.type&&n.feedback===e.state.feedback&&n.reason===e.state.reason&&n.currentDirection===e.state.currentDirection&&(n.poseMatched??!1)===(e.state.poseMatched??!1)&&n.captureStep===e.state.captureStep&&Math.round((n.captureProgress??0)*100)===Math.round((e.state.captureProgress??0)*100)&&Ye(n.turnProgress)===Ye(e.state.turnProgress)?n:e.state),re(!0),e.state.type===P.Finished){if(o.current||v.current)return;o.current=!0,S(!1),f.current?.mark(E.captureComplete),_t(se.current,y.current.map(n=>n.url)),se.current.clear(),U&&!A&&Nt(),b.flush().then(()=>f.current?.mark(E.filesReady)).catch(n=>{C.captureException(n)}).then(()=>$t(b,y.current)).then(async n=>{if(!s&&await it().catch(w=>(C.captureException(w instanceof Error?w:new Error(String(w))),!1))){g("retake");return}$.current&&(g("captured"),q?.(ge.CameraAutoCapture),V?.(y.current,fe(),n,...s||d.width<=0||d.height<=0?[]:[{viewport:d,frameAspect:l.uprightPreviewAspect}]))})}else if(e.state.type===P.Timeout){if(o.current||v.current)return;S(!1),v.current=!0,Te(!0),g("timeout"),rt?.(e.state)}},{onError:e=>{C.captureException(e)}}),I=Qe?Wt(Ee,t.captureType,A):Gt(t.captureType,A,u||ce),be=Vt(I.feedbackKey,Je,I.showsAtOnce),_e=(Ee.type===P.Initial&&!u&&!ce||ne||ae)&&!s,st=!u&&!ce&&!o.current,xe=K,Fe=ot.onFrameResult,Ue=Lt({retainWhenFlat:s}),Le=Ot(),[d,ut]=h({width:0,height:0}),lt=x(e=>{ut(r=>r.width===e.width&&r.height===e.height?r:e)},[]),c=Z(()=>{if(!s||d.width<=0||d.height<=0)return;const e=t.knownIdAspectRatio??kt,r=Ct(e,d.width,d.height);return{left:r.left/d.width,top:r.top/d.height,width:r.width/d.width,height:r.height/d.height,viewportWidthPx:Math.round(d.width),viewportHeightPx:Math.round(d.height)}},[s,d,t.knownIdAspectRatio]);p(()=>{if(!s||c===void 0)return;const e=b;if(typeof e.setCropRegion!="function")return;let r=!1;return e.setCropRegion({cropLeft:c.left,cropTop:c.top,cropWidth:c.width,cropHeight:c.height,cropViewportWidth:c.viewportWidthPx,cropViewportHeight:c.viewportHeightPx}).then(a=>{r||l.setDocumentGuide(a)}).catch(a=>{C.captureException(a instanceof Error?a:new Error(String(a)))}),()=>{r=!0}},[s,c,b,l]),p(()=>{if(!u)return;let e=!1,r=!1;const T=setInterval(()=>{e||r||(e=!0,b.analyzeKameraFrame({rotation:Ue,viewportOrientation:Le,cropLeft:c?c.left:0,cropTop:c?c.top:0,cropWidth:c?c.width:0,cropHeight:c?c.height:0,cropViewportWidth:c?c.viewportWidthPx:0,cropViewportHeight:c?c.viewportHeightPx:0}).then(m=>{r||Fe(m)}).catch(m=>{C.captureException(m instanceof Error?m:new Error(String(m)))}).finally(()=>{e=!1}))},U?Yt:jt);return()=>{r=!0,clearInterval(T)}},[u,b,Fe,Ue,Le,c]);const Oe=s&&(ne||u&&(Q?.kind==="manualCapture"||Q?.kind==="autoCaptureWithManualFallback"&&Ze)),Ke=s&&(u||ne)&&nt,ft=Z(()=>({captureType:t.captureType,overlayState:I.overlayState,enableEnhancedLiveness:A,animationDirection:I.animationDirection,feedbackKey:ae?"si_capture_failed":X??be,showCaptureButton:_e,showManualShutter:Oe,showGalleryUpload:Ke,documentSide:D,...t.knownIdAspectRatio!==void 0?{knownIdAspectRatio:t.knownIdAspectRatio}:{},...t.documentTypeKind!==void 0?{documentTypeKind:t.documentTypeKind}:{}}),[t.captureType,I.overlayState,A,I.animationDirection,be,X,ae,_e,s,Oe,Ke,D,t.knownIdAspectRatio,t.documentTypeKind]),dt=Z(()=>He(mt,{children:[_(Dt,{}),xe?_(Ut,{cameraSystem:l,style:Ve.absoluteFill,testID:"si_capture_camera_preview"}):_(ht,{style:[Ve.absoluteFill,{backgroundColor:F.colors.offBlack}],testID:"si_capture_camera_placeholder"})]}),[xe,l,F.colors.offBlack]);return He(yt,{value:F,children:[_(pt,{translucent:!0,backgroundColor:"transparent",barStyle:"dark-content"}),_(wt,{state:ft,captureProgress:I.captureProgress,turnProgress:I.turnProgress,cameraPreview:dt,onStartCapture:de,startCaptureEnabled:st,onBack:t.onBack,showAttribution:j,startButton:t.startButton,onViewportLayout:lt,...s?{onManualShutter:at,onGalleryUpload:ct,documentControlsEnabled:!tt,...t.onClose?{onClose:t.onClose}:{}}:{},theme:F}),t.captureType===z.selfie?_(At,{frameAspect:l.uprightPreviewAspect,mirrorBoxes:ze(O==="front",Ge.OS==="android")}):s?_(Rt,{streamAspect:l.uprightPreviewAspect,mirrorBoxes:ze(O==="front",Ge.OS==="android")}):null]})}async function $t(t,F){if(t.kind!=="document"||!t.documentBorderFraction)return 0;const j=F.find(U=>U.kind===k.Document);if(!j)return 0;let A;try{return await Promise.race([t.documentBorderFraction(j.url),new Promise(U=>{A=setTimeout(()=>U(0),Xt)})])}catch{return 0}finally{clearTimeout(A)}}const Xt=1e3,Jt=1;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{jsx as t,jsxs as c}from"react/jsx-runtime";import{useState as
|
|
1
|
+
"use strict";import{jsx as t,jsxs as c}from"react/jsx-runtime";import{useState as S}from"react";import{ScrollView as P,StyleSheet as U,Text as a,useColorScheme as k,View as o}from"react-native";import{SafeAreaView as R}from"react-native-safe-area-context";import{useUseSmileIDTheme as N}from"../theme/theme_context";import{ATTRIBUTION_TEST_ID as O,UseSmileIDAttribution as v}from"../components/use_smile_id_attribution";import{UseSmileIDButton as C}from"../components/use_smile_id_button";import{UseSmileIDConsentRow as A}from"../components/use_smile_id_consent_row";import{UseSmileIDExpandableIcon as L}from"../components/use_smile_id_expandable_icon";import{SiConsentBiometric as j,SiConsentContact as z,SiConsentDevice as B,SiConsentPersonal as E,SiPlus as H,SiSmileConsentIcon as M}from"../components/icons";import{UseSmileIDLearnMoreContent as G}from"../components/use_smile_id_learn_more_content";import{UseSmileIDPartnerIcon as I}from"../components/use_smile_id_partner_icon";import{UseSmileIDRoundedCheckbox as V}from"../components/use_smile_id_rounded_checkbox";import{useUseSmileIDLocalizations as W}from"../../l10n/use_smile_id_localizations_provider";import{UseSmileIDLocalizations as Z}from"../../l10n/use_smile_id_localizations";import{GlobalPrivacyControl as F}from"../../core/models/global_privacy_control";import{getDefaultConsentState as X}from"../utils/locale_utils";import{buildConsentRows as K,composeConsentSubtitleParts as $}from"./helpers/consent_screen_helpers";const d=28,q=24,J=30,y=28;function Q(n,s){switch(n){case"contact":return t(z,{tone:s,size:y});case"personal":return t(E,{tone:s,size:y});case"biometric":return t(j,{tone:s,size:y});case"device":return t(B,{tone:s,size:y})}}function Y(){try{const n=Z.localeResolver();return((typeof n=="string"?n.split(/[-_]/)[0]:"")||"en").toUpperCase()}catch{return"EN"}}export function UseSmileIDConsentScreen(n){const s=N(),e=n.theme??s,i=W(),f=n.showAttribution??!0,b=n.globalPrivacyControl??F.OptOut,[m,x]=S(()=>X(b)),[h,_]=S(!1),p=$(n.partnerName,i),w=K(i),D=k()==="dark"?"dark":"light",u=()=>{if(!m)return;const r={granted:!0,grantedAt:new Date().toISOString(),noticeLanguage:Y(),noticePrivacyPolicyUrl:n.partnerPrivacyPolicyUrl??""};n.onAllow?.(r)},g=()=>{n.onDeny?.()};return t(R,{style:[l.root,{backgroundColor:e.colors.background,padding:e.spacing.medium}],testID:"si_consent_screen",children:c(P,{style:l.scroll,contentContainerStyle:l.scrollContent,showsVerticalScrollIndicator:!1,children:[c(o,{style:{rowGap:e.spacing.medium},children:[c(o,{style:l.iconRow,children:[t(I,{partnerName:n.partnerName,theme:e,...n.partnerIcon!==void 0?{icon:n.partnerIcon}:{}}),t(o,{style:{paddingHorizontal:e.spacing.extraSmall},children:t(H,{size:q})}),t(I,{partnerName:n.partnerName,theme:e,accessibilityLabel:i.siCdSmileId,icon:t(M,{size:J})})]}),t(a,{accessibilityRole:"header",style:{...e.typography.sectionHeading,color:e.colors.titleText,textAlign:"center"},testID:"si_consent_title",children:i.siConsentTitle(n.partnerName)}),t(a,{style:{...e.typography.cardSubTitle,color:e.colors.cardText,textAlign:"justify"},testID:"si_consent_subtitle",children:p.map((r,T)=>r.bold?t(a,{style:{...e.typography.cardSubTitle,color:e.colors.cardText,fontWeight:"700"},children:r.text},T):r.text)}),t(a,{style:{...e.typography.cardSubTitle,color:e.colors.cardText,textAlign:"justify"},children:i.siConsentProcessing}),t(o,{children:w.map(r=>t(A,{label:r.label,icon:Q(r.key,D),theme:e,testID:`si_consent_row_${r.key}`},r.key))}),c(o,{style:l.gateRow,children:[t(o,{style:[l.checkboxHitbox,{width:d,height:d}],children:t(V,{checked:m,onChange:x,theme:e,testID:"si_consent_checkbox"})}),t(o,{style:{width:e.spacing.extraSmall}}),t(a,{style:{...e.typography.cardSubTitle,color:e.colors.titleText,flex:1,textAlign:"auto"},children:i.siConsentConsent})]}),c(o,{style:l.learnMoreRow,children:[t(o,{style:[l.checkboxHitbox,{width:d,height:d}],children:t(L,{expanded:h,onChange:_,theme:e,testID:"si_consent_learn_more_toggle"})}),t(o,{style:{width:e.spacing.extraSmall}}),t(a,{style:{...e.typography.cardTitle,color:e.colors.titleText,flex:1},children:i.siConsentLearnMore})]}),h?t(G,{partnerName:n.partnerName,...n.partnerPrivacyPolicyUrl?{partnerPrivacyPolicyUrl:n.partnerPrivacyPolicyUrl}:{},theme:e,testID:"si_consent_learn_more_content"}):null]}),c(o,{style:[l.bottom,{rowGap:e.spacing.small,paddingTop:e.spacing.medium}],children:[n.allowButton?n.allowButton({onClick:u,enabled:m}):t(C,{label:i.siConsentAllow,onClick:u,variant:"primary",enabled:m,theme:e,testID:"si_consent_allow_button"}),n.denyButton?n.denyButton({onClick:g}):t(C,{label:i.siConsentDeny,onClick:g,variant:"secondary",theme:e,testID:"si_consent_deny_button"}),f?t(v,{theme:e,testID:O}):null]})]})})}const l=U.create({root:{flex:1},scroll:{flex:1},scrollContent:{flexGrow:1,justifyContent:"space-between"},iconRow:{flexDirection:"row",alignItems:"center",justifyContent:"center"},gateRow:{flexDirection:"row",alignItems:"flex-start"},learnMoreRow:{flexDirection:"row",alignItems:"center"},checkboxHitbox:{alignItems:"center",justifyContent:"center"},bottom:{alignItems:"stretch"}});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SelfieCaptureGeometry } from "../components/helpers/captured_oval_region";
|
|
1
2
|
import type { UseSmileIDTheme } from "../theme/theme";
|
|
2
3
|
export type UseSmileIDPreviewScreenProps = {
|
|
3
4
|
readonly capturedSelfiePath?: string;
|
|
@@ -6,6 +7,8 @@ export type UseSmileIDPreviewScreenProps = {
|
|
|
6
7
|
readonly onAccept?: () => void;
|
|
7
8
|
readonly onReject?: () => void;
|
|
8
9
|
readonly theme?: UseSmileIDTheme;
|
|
10
|
+
/** @internal What the flow's selfie capture screen laid out, so the oval shows what it showed. */
|
|
11
|
+
readonly selfieCaptureGeometry?: SelfieCaptureGeometry;
|
|
9
12
|
};
|
|
10
13
|
/**
|
|
11
14
|
* Preview screen — confirms the captured selfie before submission.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{jsx as i,jsxs as
|
|
1
|
+
"use strict";import{jsx as i,jsxs as s}from"react/jsx-runtime";import{useState as p}from"react";import{StyleSheet as w,Text as a,View as I}from"react-native";import{SafeAreaView as v}from"react-native-safe-area-context";import{useUseSmileIDTheme as b}from"../theme/theme_context";import{ATTRIBUTION_TEST_ID as D,UseSmileIDAttribution as x}from"../components/use_smile_id_attribution";import{UseSmileIDCapturedImageOval as A}from"../components/use_smile_id_captured_image_oval";import{UseSmileIDCardSurface as T}from"../components/use_smile_id_card_surface";import{UseSmileIDConfirmationButtons as _}from"../components/use_smile_id_confirmation_buttons";import{useUseSmileIDLocalizations as C}from"../../l10n/use_smile_id_localizations_provider";import{resolvePreviewState as P}from"./helpers/preview_screen_helpers";export function UseSmileIDPreviewScreen(t){const m=b(),e=t.theme??m,r=C(),l=t.showAttribution??!0,o=P(t),[c,h]=p({width:0,height:0}),u=f=>{const{width:y,height:S}=f.nativeEvent.layout;h({width:y,height:S})},d=()=>{t.onAccept?.()},g=()=>{o.canRetake&&t.onReject?.()};return s(v,{edges:["left","right","bottom"],style:[n.root,{backgroundColor:e.colors.background}],testID:"si_preview_screen",onLayout:u,children:[i(A,{screenWidth:c.width,screenHeight:c.height,...t.selfieCaptureGeometry?{captureGeometry:t.selfieCaptureGeometry}:{},...o.hasImage&&t.capturedSelfiePath?{uri:t.capturedSelfiePath}:{},imageTestID:"si_preview_image",imageAccessibilityLabel:r.siPreviewAccessibilityImage,showDebugOverlay:!0,theme:e}),s(T,{theme:e,style:n.card,children:[i(a,{accessibilityRole:"header",style:{...e.typography.cardHeading,color:e.colors.primary,textAlign:"center",letterSpacing:-.425},children:r.siPreviewTitle}),i(a,{style:{...e.typography.cardCaption,color:e.colors.cardText,textAlign:"center",marginTop:e.spacing.extraSmall/2},children:r.siPreviewSubtitle}),i(I,{style:{marginTop:e.spacing.medium},children:i(_,{onAccept:d,onReject:g,acceptLabel:r.siPreviewConfirm,rejectLabel:r.siPreviewRetake,theme:e,testID:"si_preview_confirmation_buttons"})})]}),l?i(x,{theme:e,testID:D}):null]})}const n=w.create({root:{flex:1,overflow:"hidden"},card:{alignItems:"center"}});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
+
import type { SelfieCaptureGeometry } from "../components/helpers/captured_oval_region";
|
|
2
3
|
import type { UseSmileIDTheme } from "../theme/theme";
|
|
3
4
|
import type { ProcessingState } from "../state/processing_state";
|
|
4
5
|
export type { ProcessingState } from "../state/processing_state";
|
|
@@ -48,6 +49,8 @@ export type UseSmileIDProcessingScreenProps = {
|
|
|
48
49
|
onClick: () => void;
|
|
49
50
|
}) => ReactNode;
|
|
50
51
|
readonly theme?: UseSmileIDTheme;
|
|
52
|
+
/** @internal What the flow's selfie capture screen laid out, so the oval shows what it showed. */
|
|
53
|
+
readonly selfieCaptureGeometry?: SelfieCaptureGeometry;
|
|
51
54
|
};
|
|
52
55
|
/**
|
|
53
56
|
* Processing screen — submission-status surface holding the captured
|