react-native-persona
Version:
Launch a mobile native implementation of the Persona inquiry flow from React Native.
670 lines (563 loc) • 17.1 kB
text/typescript
import {
EventSubscription,
NativeEventEmitter,
NativeModules,
} from 'react-native';
import {
callOnCanceledCallback,
callOnCompleteCallback,
callOnErrorCallback,
processThemeValues,
} from './util';
import { Fields, RawInquiryField } from './fields';
import { InquiryEvent } from './InquiryEvent';
import PersonaInquiryView, {
onPersonaInquiryViewEvent,
} from './PersonaInquiryView';
import { Versions } from './versions';
import type {
CollectedData,
ExternalCollectedData,
ExtraData,
OnCanceledCallback,
OnCompleteCallback,
OnErrorCallback,
OnEventCallback,
} from './callbacks';
import {
StepData,
Document,
UiStepData,
SelfieCaptureMethod,
SelfieCapture,
SelfieStepData,
GovernmentIdCaptureFrames,
GovernmentIdCaptureSide,
GovernmentIdCaptureMethod,
GovernmentIdCapture,
GovernmentIdStepData,
DocumentStepData,
} from './StepData';
export { Fields };
export { PersonaInquiryView };
export { Versions };
export {
CollectedData,
ExtraData,
OnCanceledCallback,
OnCompleteCallback,
OnErrorCallback,
OnEventCallback,
};
export {
StepData,
Document,
UiStepData,
SelfieCaptureMethod,
SelfieCapture,
SelfieStepData,
GovernmentIdCaptureFrames,
GovernmentIdCaptureSide,
GovernmentIdCaptureMethod,
GovernmentIdCapture,
GovernmentIdStepData,
DocumentStepData,
};
const { PersonaInquiry2 } = NativeModules;
// Using Opaque types + Smart Constructor enforces validation at
// instantiation time for IDS
declare const Unique: unique symbol;
export type Opaque<T, Tag> = T & { [Unique]: Tag };
type TemplateId = Opaque<string, 'TemplateId'>;
type TemplateVersion = Opaque<string, 'TemplateVersion'>;
type InquiryId = Opaque<string, 'InquiryId'>;
type AccountId = Opaque<string, 'AccountId'>;
export class InvalidTemplateId extends Error {}
export class InvalidTemplateVersion extends Error {}
export class InvalidInquiryId extends Error {}
export class InvalidAccountId extends Error {}
/**
* Run validations that the string is in proper Inquiry token format
* and do a type conversion to InquiryId.
*
* @param candidate
*/
function makeInquiryId(candidate: string): InquiryId {
if (candidate && candidate.startsWith('inq_')) {
return candidate as InquiryId;
}
throw new InvalidInquiryId(
`Valid template IDs start with "inq_". Received: ${candidate} `
);
}
/**
* Run validations that the string is in proper Template token format
* and do a type conversion to TemplateId.
*
* @param candidate
*/
function makeTemplateId(candidate: string): TemplateId {
if (candidate && candidate.startsWith('itmpl_')) {
return candidate as TemplateId;
}
throw new InvalidTemplateId(
`Valid template IDs start with "itmpl_". Received: ${candidate} `
);
}
/**
* Run validations that the string is in proper Template Version token format
* and do a type conversion to TemplateVersion.
*
* @param candidate
*/
function makeTemplateVersion(candidate: string): TemplateVersion {
if (candidate && candidate.startsWith('itmplv_')) {
return candidate as TemplateVersion;
}
throw new InvalidTemplateVersion(
`Valid template versions start with "itmplv_". Received: ${candidate} `
);
}
/**
* Run validations that the string is in proper Template token format
* and do a type conversion to AccountId.
*
* @param candidate
*/
function makeAccountId(candidate: string): AccountId {
if (candidate && candidate.startsWith('act_')) {
return candidate as AccountId;
}
throw new InvalidAccountId(
`Valid account IDs start with "act_". Received: ${candidate} `
);
}
/**
* String enum for environments. These strings will be parsed
* on the native side bridge into Kotlin / Swift enums.
*/
export enum Environment {
SANDBOX = 'sandbox',
PRODUCTION = 'production',
}
/**
* An enum value which determines whether this sdk should use the theme values sent from the server.
*/
export enum ThemeSource {
SERVER = 'server',
/**
* @deprecated Client side theming is deprecated, please configure your theme inside
* the Persona Dashboard and use SERVER as the theme source.
*/
CLIENT = 'client',
}
export interface InquiryOptions {
templateId?: TemplateId;
templateVersion?: TemplateVersion;
inquiryId?: InquiryId;
referenceId?: string;
accountId?: AccountId;
environment?: Environment;
environmentId?: string;
themeSetId?: string;
sessionToken?: string;
returnCollectedData?: boolean;
locale?: String;
disablePresentationAnimation?: boolean;
// Fields
fields?: Fields;
// Callbacks
onComplete?: OnCompleteCallback;
onCanceled?: OnCanceledCallback;
onError?: OnErrorCallback;
// Customization
iosThemeObject?: Object | null;
themeSource?: ThemeSource | null;
}
const eventEmitter = new NativeEventEmitter(PersonaInquiry2);
let onEventCallback: OnEventCallback | null = null;
eventEmitter.addListener(
'onEvent',
(rawEvent: {
event: {
type: string;
};
}) => {
let event = InquiryEvent.fromJson(rawEvent.event);
if (event != null) {
onEventCallback?.(event);
onPersonaInquiryViewEvent(event);
}
}
);
export class Inquiry {
templateId?: TemplateId;
templateVersion?: TemplateVersion;
inquiryId?: InquiryId;
referenceId?: string;
accountId?: AccountId;
environment?: Environment;
environmentId?: string;
themeSetId?: string;
sessionToken?: string;
returnCollectedData?: boolean;
locale?: String;
disablePresentationAnimation?: Boolean;
iosThemeObject?: Object | null;
themeSource?: ThemeSource | null;
fields?: Fields | null;
private readonly onComplete?: OnCompleteCallback;
private readonly onCanceled?: OnCanceledCallback;
private readonly onError?: OnErrorCallback;
private onCompleteListener?: EventSubscription;
private onCanceledListener?: EventSubscription;
private onErrorListener?: EventSubscription;
constructor(options: InquiryOptions) {
this.templateId = options.templateId;
this.templateVersion = options.templateVersion;
this.inquiryId = options.inquiryId;
this.referenceId = options.referenceId;
this.accountId = options.accountId;
this.environment = options.environment;
this.environmentId = options.environmentId;
this.themeSetId = options.themeSetId;
this.sessionToken = options.sessionToken;
this.fields = options.fields;
this.returnCollectedData = options.returnCollectedData;
this.locale = options.locale;
this.disablePresentationAnimation = options.disablePresentationAnimation;
// Callbacks
this.onComplete = options.onComplete;
this.onCanceled = options.onCanceled;
this.onError = options.onError;
// Theme object
this.iosThemeObject = options.iosThemeObject;
this.themeSource = options.themeSource;
}
private clearListeners() {
if (this.onCompleteListener) this.onCompleteListener.remove();
if (this.onCanceledListener) this.onCanceledListener.remove();
if (this.onErrorListener) this.onErrorListener.remove();
}
/**
* Create an Inquiry flow builder based on a template ID.
*
* You can find your template ID on the Dashboard under Inquiries > Templates.
* {@link https://app.withpersona.com/dashboard/inquiry-templates}
*
* @param templateId template ID from your Persona Dashboard
* @return builder for the Inquiry flow
*/
static fromTemplate(templateId: string) {
return new TemplateBuilder(makeTemplateId(templateId), null);
}
/**
* Create an Inquiry flow builder based on a template ID version.
*
* You can find your template ID version on the Dashboard under the
* settings view of a specific template.
* {@link https://app.withpersona.com/dashboard/inquiry-templates}
*
* @param templateVersion template version from your Persona Dashboard
* @return builder for the Inquiry flow
*/
static fromTemplateVersion(templateVersion: string) {
return new TemplateBuilder(null, makeTemplateVersion(templateVersion));
}
/**
* Create an Inquiry flow builder based on an inquiry ID.
*
* You will need to generate the inquiry ID on the server. To try it out, you can create an
* inquiry from the Persona Dashboard under "Inquiries". Click on the "Create Inquiry" button
* and copy the inquiry ID from the URL.
* {@link https://app.withpersona.com/dashboard/inquiries}
*
* @param inquiryId inquiry ID from your server
* @return builder for the Inquiry flow
*/
static fromInquiry(inquiryId: string) {
return new InquiryBuilder(makeInquiryId(inquiryId));
}
static onEvent(callback: OnEventCallback) {
onEventCallback = callback;
}
toOptionsJson() {
return {
templateId: this.templateId,
templateVersion: this.templateVersion,
inquiryId: this.inquiryId,
referenceId: this.referenceId,
accountId: this.accountId,
environment: this.environment,
environmentId: this.environmentId,
themeSetId: this.themeSetId,
sessionToken: this.sessionToken,
fields: this.fields,
returnCollectedData: this.returnCollectedData,
themeSource: this.themeSource,
iosTheme: processThemeValues(this.iosThemeObject || {}),
locale: this.locale,
disablePresentationAnimation: this.disablePresentationAnimation,
};
}
/**
* Launch the Persona Inquiry.
*/
start() {
this.onCompleteListener = eventEmitter.addListener(
'onComplete',
(event: {
inquiryId: string;
status: string;
fields: Record<string, RawInquiryField>;
collectedData: ExternalCollectedData | null;
}) => {
callOnCompleteCallback(event, this.onComplete);
this.clearListeners();
}
);
this.onCanceledListener = eventEmitter.addListener(
'onCanceled',
(event: { inquiryId?: string; sessionToken?: string }) => {
callOnCanceledCallback(event, this.onCanceled);
this.clearListeners();
}
);
this.onErrorListener = eventEmitter.addListener(
'onError',
(event: { debugMessage: string; errorCode?: string }) => {
callOnErrorCallback(event, this.onError);
this.clearListeners();
}
);
PersonaInquiry2.startInquiry(this.toOptionsJson());
}
}
class InquiryBuilder {
private _inquiryId: InquiryId;
private _sessionToken?: string;
// Callbacks
private _onComplete?: OnCompleteCallback;
private _onCanceled?: OnCanceledCallback;
private _onError?: OnErrorCallback;
private _iosThemeObject?: Object;
private _themeSource: ThemeSource = ThemeSource.SERVER;
private _fields?: Fields;
private _locale?: string;
private _disablePresentationAnimation?: boolean;
constructor(inquiryId: InquiryId) {
this._inquiryId = inquiryId;
}
sessionToken(sessionToken: string): InquiryBuilder {
this._sessionToken = sessionToken;
return this;
}
onComplete(callback: OnCompleteCallback): InquiryBuilder {
this._onComplete = callback;
return this;
}
onCanceled(callback: OnCanceledCallback): InquiryBuilder {
this._onCanceled = callback;
return this;
}
onError(callback: OnErrorCallback): InquiryBuilder {
this._onError = callback;
return this;
}
/**
* @deprecated Use iosThemeToUse
*/
iosTheme(themeObject: Object): InquiryBuilder {
this._iosThemeObject = themeObject;
this._themeSource = ThemeSource.CLIENT;
return this;
}
iosThemeToUse(themeObject: Object, themeSource: ThemeSource): InquiryBuilder {
this._iosThemeObject = themeObject;
this._themeSource = themeSource;
return this;
}
locale(locale: string): InquiryBuilder {
this._locale = locale;
return this;
}
disablePresentationAnimation(
disablePresentationAnimation: boolean
): InquiryBuilder {
this._disablePresentationAnimation = disablePresentationAnimation;
return this;
}
build(): Inquiry {
return new Inquiry({
inquiryId: this._inquiryId,
sessionToken: this._sessionToken,
onComplete: this._onComplete,
onCanceled: this._onCanceled,
onError: this._onError,
iosThemeObject: this._iosThemeObject,
themeSource: this._themeSource,
fields: this._fields,
locale: this._locale,
disablePresentationAnimation: this._disablePresentationAnimation,
});
}
}
class TemplateBuilder {
private readonly _templateId?: TemplateId;
private readonly _templateVersion?: TemplateVersion;
private _accountId?: AccountId;
private _referenceId?: string;
private _environment?: Environment;
private _environmentId?: string;
private _themeSetId?: string;
private _fields?: Fields;
private _sessionToken?: string;
private _returnCollectedData?: boolean;
private _locale?: string;
private _disablePresentationAnimation?: boolean;
// Callbacks
private _onComplete?: OnCompleteCallback;
private _onCanceled?: OnCanceledCallback;
private _onError?: OnErrorCallback;
// Customization
private _iosThemeObject?: Object;
private _themeSource: ThemeSource = ThemeSource.SERVER;
constructor(
templateId?: TemplateId | null,
templateVersion?: TemplateVersion | null
) {
if (templateId != null) {
this._templateId = templateId;
} else if (templateVersion != null) {
this._templateVersion = templateVersion;
} else {
throw new InvalidTemplateId(
`Either templateId or templateVersion needs to be set.`
);
}
return this;
}
returnCollectedData(returnCollectedData: boolean) {
this._returnCollectedData = returnCollectedData;
return this;
}
referenceId(referenceId: string): TemplateBuilder {
if (referenceId == null) {
return this;
}
this._accountId = undefined;
this._referenceId = referenceId;
return this;
}
accountId(accountId: string): TemplateBuilder {
if (accountId == null) {
return this;
}
this._referenceId = undefined;
this._accountId = makeAccountId(accountId);
return this;
}
environment(environment: Environment): TemplateBuilder {
this._environment = environment;
return this;
}
environmentId(environmentId: string): TemplateBuilder {
this._environmentId = environmentId;
return this;
}
themeSetId(themeSetId: string): TemplateBuilder {
this._themeSetId = themeSetId;
return this;
}
sessionToken(sessionToken: string): TemplateBuilder {
this._sessionToken = sessionToken;
return this;
}
fields(fields: Fields): TemplateBuilder {
this._fields = fields;
return this;
}
locale(locale: string): TemplateBuilder {
this._locale = locale;
return this;
}
onComplete(callback: OnCompleteCallback): TemplateBuilder {
this._onComplete = callback;
return this;
}
onCanceled(callback: OnCanceledCallback): TemplateBuilder {
this._onCanceled = callback;
return this;
}
onError(callback: OnErrorCallback): TemplateBuilder {
this._onError = callback;
return this;
}
/**
* @deprecated Use iosThemeToUse
*/
iosTheme(themeObject: Object): TemplateBuilder {
this._iosThemeObject = themeObject;
this._themeSource = ThemeSource.CLIENT;
return this;
}
iosThemeToUse(
themeObject: Object,
themeSource: ThemeSource
): TemplateBuilder {
this._iosThemeObject = themeObject;
this._themeSource = themeSource;
return this;
}
disablePresentationAnimation(
disablePresentationAnimation: boolean
): TemplateBuilder {
this._disablePresentationAnimation = disablePresentationAnimation;
return this;
}
build(): Inquiry {
return new Inquiry({
templateId: this._templateId,
templateVersion: this._templateVersion,
accountId: this._accountId,
referenceId: this._referenceId,
environment: this._environment,
environmentId: this._environmentId,
themeSetId: this._themeSetId,
sessionToken: this._sessionToken,
fields: this._fields,
onComplete: this._onComplete,
onCanceled: this._onCanceled,
onError: this._onError,
iosThemeObject: this._iosThemeObject,
themeSource: this._themeSource,
returnCollectedData: this._returnCollectedData,
locale: this._locale,
disablePresentationAnimation: this._disablePresentationAnimation,
});
}
}
/**
* @deprecated Use the `Inquiry` static methods instead
*/
namespace InquiryBuilders {
/**
* @deprecated Use {@link Inquiry#fromInquiry} instead
*/
export function fromInquiry(inquiryId: string) {
return Inquiry.fromInquiry(inquiryId);
}
/**
* @deprecated Use {@link Inquiry#fromTemplate} instead
*/
export function fromTemplate(templateId: string) {
return Inquiry.fromTemplate(templateId);
}
/**
* @deprecated Use {@link Inquiry#fromTemplateVersion} instead
*/
export function fromTemplateVersion(templateVersion: string) {
return Inquiry.fromTemplateVersion(templateVersion);
}
}
export default InquiryBuilders;