mirror of
https://github.com/valitydev/wallet-utils.git
synced 2024-11-06 08:55:19 +00:00
FE-601: temp app initializer (#3)
This commit is contained in:
parent
c228bb131e
commit
c62825ee8e
37
package-lock.json
generated
37
package-lock.json
generated
@ -10,12 +10,39 @@
|
||||
"integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/ismobilejs": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/ismobilejs/-/ismobilejs-0.4.1.tgz",
|
||||
"integrity": "sha512-h7d/EWxHjRNY/VuIWBsciHahuIRmSVvtK+34cal0m/UBN75wcuEXxzfn91PRr7iyyJqMccW+oiwp6T+T9khamQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/lodash": {
|
||||
"version": "4.14.107",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.107.tgz",
|
||||
"integrity": "sha512-afvjfP2rl3yvtv2qrCRN23zIQcDinF+munMJCoHEw2BXF22QJogTlVfNPTACQ6ieDyA6VnyKT4WLuN/wK368ng==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/lodash-es": {
|
||||
"version": "4.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.0.tgz",
|
||||
"integrity": "sha512-h8lkWQSgT4qjs9PcIhcL2nWubZeXRVzjZxYlRFmcX9BW1PIk5qRc0djtRWZqtM+GDDFhwBt0ztRu72D/YxIcEw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/lodash": "4.14.107"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "9.6.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.5.tgz",
|
||||
"integrity": "sha512-NOLEgsT6UiDTjnWG5Hd2Mg25LRyz/oe8ql3wbjzgSFeRzRROhPmtlsvIrei4B46UjERF0td9SZ1ZXPLOdcrBHg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/ramda": {
|
||||
"version": "0.25.24",
|
||||
"resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.25.24.tgz",
|
||||
"integrity": "sha512-c0TmWA7d4y9KLJJwL/cLPEfSReSgFQK9BtemcCATT48lMeyD7HG8IfGY8bamSuz/Byx1l+13hZV0PCvHsgMB3w==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "16.3.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.3.11.tgz",
|
||||
@ -6152,6 +6179,11 @@
|
||||
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
|
||||
"dev": true
|
||||
},
|
||||
"ismobilejs": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/ismobilejs/-/ismobilejs-0.4.1.tgz",
|
||||
"integrity": "sha1-Gl8SbHD+05yT2jgPpiy65XI+fcI="
|
||||
},
|
||||
"isobject": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
|
||||
@ -8985,6 +9017,11 @@
|
||||
"integrity": "sha1-DPf4T5Rj/wrlHExLFC2VvjdyTZw=",
|
||||
"dev": true
|
||||
},
|
||||
"ramda": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.25.0.tgz",
|
||||
"integrity": "sha512-GXpfrYVPwx3K7RQ6aYT8KPS8XViSXUVJT1ONhoKPE9VAleW42YE+U+8VEyGWt41EnEQW7gwecYJriTI0pKoecQ=="
|
||||
},
|
||||
"randomatic": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz",
|
||||
|
@ -19,6 +19,9 @@
|
||||
},
|
||||
"homepage": "https://github.com/rbkmoney/wallet-utils#readme",
|
||||
"devDependencies": {
|
||||
"@types/ismobilejs": "~0.4.1",
|
||||
"@types/lodash-es": "~4.17.0",
|
||||
"@types/ramda": "~0.25.24",
|
||||
"@types/react": "~16.3.11",
|
||||
"@types/react-dom": "~16.0.5",
|
||||
"cache-loader": "~1.2.2",
|
||||
@ -47,6 +50,9 @@
|
||||
"write-file-webpack-plugin": "~4.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ismobilejs": "~0.4.1",
|
||||
"lodash-es": "~4.17.8",
|
||||
"ramda": "~0.25.0",
|
||||
"react": "~16.3.2",
|
||||
"react-dom": "~16.3.2",
|
||||
"redux-form": "~7.3.0",
|
||||
|
@ -1,13 +1,17 @@
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
|
||||
import { Child } from '../communication';
|
||||
import './styles/main.scss';
|
||||
|
||||
const app = document.getElementById('app');
|
||||
|
||||
/* tslint:disable:no-expression-statement */
|
||||
ReactDOM.render(
|
||||
<div>start</div>,
|
||||
app
|
||||
);
|
||||
/* tslint:enable:no-expression-statement */
|
||||
Child.resolve()
|
||||
.then((transport) => {
|
||||
/* tslint:disable:no-expression-statement */
|
||||
ReactDOM.render(
|
||||
<div>start</div>,
|
||||
app
|
||||
);
|
||||
/* tslint:enable:no-expression-statement */
|
||||
});
|
||||
|
29
src/communication/child.ts
Normal file
29
src/communication/child.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { ContextResolver, RealTransport, StubTransport, Transport, TransportInfo } from '.';
|
||||
import { isInFrame } from '../is-in-iframe';
|
||||
|
||||
export class Child {
|
||||
|
||||
static resolve(): Promise<Transport> {
|
||||
return new Promise((resolve) => {
|
||||
if (ContextResolver.isAvailable() && window.opener) {
|
||||
const target = window.opener;
|
||||
const context = ContextResolver.get();
|
||||
return resolve(new RealTransport(target, context.parentOrigin, window));
|
||||
} else if (isInFrame() && !window.opener) {
|
||||
return resolve(new StubTransport());
|
||||
} else {
|
||||
const shake = (e: MessageEvent) => {
|
||||
if (e && e.data === TransportInfo.parentHandshakeMessageName) {
|
||||
const target = e.source;
|
||||
target.postMessage(TransportInfo.childHandshakeMessageName, e.origin);
|
||||
ContextResolver.set({
|
||||
parentOrigin: e.origin
|
||||
});
|
||||
return resolve(new RealTransport(target, e.origin, window));
|
||||
}
|
||||
};
|
||||
window.addEventListener('message', shake, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
22
src/communication/context-resolver.ts
Normal file
22
src/communication/context-resolver.ts
Normal file
@ -0,0 +1,22 @@
|
||||
export class ContextResolver {
|
||||
|
||||
static set(context: any): void {
|
||||
try {
|
||||
sessionStorage.setItem(this.key, JSON.stringify(context));
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
static get(): any {
|
||||
try {
|
||||
return JSON.parse(sessionStorage.getItem(this.key));
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
static isAvailable(): boolean {
|
||||
try {
|
||||
return !!JSON.parse(sessionStorage.getItem(this.key));
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
private static key = 'wallet-context';
|
||||
}
|
9
src/communication/index.ts
Normal file
9
src/communication/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export * from './child';
|
||||
export * from './context-resolver';
|
||||
export * from './transport';
|
||||
export * from './real-transport';
|
||||
export * from './stub-transport';
|
||||
export * from './model/transport-info';
|
||||
export * from './model/possible-events';
|
||||
export * from './model/transport-message';
|
||||
export * from './parent';
|
4
src/communication/model/action-type.ts
Normal file
4
src/communication/model/action-type.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export enum ActionType {
|
||||
userIdentity = 'userIdentity',
|
||||
createOutput = 'createOutput'
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
import { InitializerData } from '.';
|
||||
import { CreateOutputParams } from '../../initializer/model';
|
||||
|
||||
export interface CreateOutputInitializerData extends InitializerData {
|
||||
params: CreateOutputParams;
|
||||
}
|
7
src/communication/model/index.ts
Normal file
7
src/communication/model/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export * from './possible-events';
|
||||
export * from './transport-info';
|
||||
export * from './transport-message';
|
||||
export * from './action-type';
|
||||
export * from './user-identity-initializer-data';
|
||||
export * from './initializer-data';
|
||||
export * from './create-output-initializer-data';
|
6
src/communication/model/initializer-data.ts
Normal file
6
src/communication/model/initializer-data.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { ActionType } from '.';
|
||||
|
||||
export interface InitializerData {
|
||||
type: ActionType;
|
||||
token: string;
|
||||
}
|
9
src/communication/model/possible-events.ts
Normal file
9
src/communication/model/possible-events.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export enum PossibleEvents {
|
||||
init = 'init',
|
||||
onCancel = 'onCancel',
|
||||
onCompleteIdentityChallenge = 'onCompleteIdentityChallenge',
|
||||
onFailIdentityChallenge = 'onFailIdentityChallenge',
|
||||
onCancelIdentityChallenge = 'onCancelIdentityChallenge',
|
||||
onCreateOutput = 'onCreateOutput',
|
||||
abort = 'abort'
|
||||
}
|
5
src/communication/model/transport-info.ts
Normal file
5
src/communication/model/transport-info.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export enum TransportInfo {
|
||||
transportName = 'rbkmoney-wallet',
|
||||
parentHandshakeMessageName = 'rbkmoney-wallet-parent-handshake',
|
||||
childHandshakeMessageName = 'rbkmoney-wallet-child-handshake'
|
||||
}
|
7
src/communication/model/transport-message.ts
Normal file
7
src/communication/model/transport-message.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { TransportInfo } from '../index';
|
||||
|
||||
export class TransportMessage {
|
||||
data: any;
|
||||
name: string;
|
||||
transport: TransportInfo;
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
import { StartIdentityChallengeParams } from '../../initializer/model';
|
||||
import { InitializerData } from '.';
|
||||
|
||||
export interface UserIdentityInitializerData extends InitializerData {
|
||||
params: StartIdentityChallengeParams;
|
||||
}
|
40
src/communication/parent.ts
Normal file
40
src/communication/parent.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { RealTransport } from './real-transport';
|
||||
import { Transport } from './transport';
|
||||
import { TransportInfo } from './model/transport-info';
|
||||
|
||||
export class Parent {
|
||||
|
||||
private target: Window;
|
||||
private origin: string;
|
||||
private parent: Window;
|
||||
|
||||
constructor(target: Window, origin: string) {
|
||||
this.target = target;
|
||||
this.origin = origin;
|
||||
this.parent = window;
|
||||
}
|
||||
|
||||
sendHandshake(): Promise<Transport> {
|
||||
let interval: any;
|
||||
return new Promise((resolve, reject) => {
|
||||
const reply = (e: any) => {
|
||||
if (e.data === TransportInfo.childHandshakeMessageName) {
|
||||
clearInterval(interval);
|
||||
return resolve(new RealTransport(this.target, this.origin, this.parent));
|
||||
}
|
||||
};
|
||||
this.parent.addEventListener('message', reply, false);
|
||||
let attempt = 0;
|
||||
const maxHandshakeRequests = 20;
|
||||
const doSend = () => {
|
||||
attempt++;
|
||||
this.target.postMessage(TransportInfo.parentHandshakeMessageName, this.origin);
|
||||
if (attempt === maxHandshakeRequests) {
|
||||
clearInterval(interval);
|
||||
return reject('failed handshake');
|
||||
}
|
||||
};
|
||||
interval = setInterval(doSend, 500);
|
||||
});
|
||||
}
|
||||
}
|
44
src/communication/real-transport.ts
Normal file
44
src/communication/real-transport.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { PossibleEvents, Transport, TransportInfo, TransportMessage } from '.';
|
||||
|
||||
export class RealTransport implements Transport {
|
||||
|
||||
private target: Window;
|
||||
private origin: string;
|
||||
private events: any = {};
|
||||
|
||||
constructor(target: Window, origin: string, source: Window) {
|
||||
this.target = target;
|
||||
this.origin = origin;
|
||||
source.addEventListener('message', this.listener.bind(this), false);
|
||||
}
|
||||
|
||||
emit(name: PossibleEvents, data?: any): void {
|
||||
const serialized = JSON.stringify({
|
||||
data,
|
||||
name,
|
||||
transport: TransportInfo.transportName
|
||||
} as TransportMessage);
|
||||
this.target.postMessage(serialized, this.origin);
|
||||
}
|
||||
|
||||
on(eventName: PossibleEvents, callback: (data: any) => any): void {
|
||||
this.events[eventName] = callback;
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
window.removeEventListener('message', this.listener.bind(this), false);
|
||||
}
|
||||
|
||||
private listener(e: MessageEvent): void {
|
||||
let parsed: TransportMessage;
|
||||
try {
|
||||
parsed = JSON.parse(e.data);
|
||||
/* tslint:disable:no-empty */
|
||||
} catch (e) {}
|
||||
if (parsed && (parsed.name in this.events)) {
|
||||
if (parsed.transport === TransportInfo.transportName) {
|
||||
this.events[parsed.name].call(this, parsed.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
17
src/communication/stub-transport.ts
Normal file
17
src/communication/stub-transport.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { PossibleEvents, Transport } from '.';
|
||||
|
||||
export class StubTransport implements Transport {
|
||||
|
||||
emit(name: PossibleEvents, data: any): void {
|
||||
console.info('transport stub emit: ', name, data);
|
||||
}
|
||||
|
||||
on(eventName: PossibleEvents, callback: (data: any) => any): void {
|
||||
callback({});
|
||||
console.info('transport stub on: ', eventName, callback);
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
console.info('transport stub destroy');
|
||||
}
|
||||
}
|
9
src/communication/transport.ts
Normal file
9
src/communication/transport.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { PossibleEvents } from './model/index';
|
||||
|
||||
export interface Transport {
|
||||
emit(name: PossibleEvents, data?: any): void;
|
||||
|
||||
on(eventName: PossibleEvents, callback: (data: any) => any): void;
|
||||
|
||||
destroy(): void;
|
||||
}
|
9
src/get-origin.ts
Normal file
9
src/get-origin.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export const ieCurrentScriptStub = {
|
||||
src: 'https://wallets.rbk.money/wallet-utils.js' // TODO: поправить домен!!!
|
||||
};
|
||||
|
||||
const getCurrentScript = (): HTMLScriptElement =>
|
||||
(document.currentScript || ieCurrentScriptStub) as HTMLScriptElement;
|
||||
|
||||
export const getOrigin = (): string =>
|
||||
new URL(getCurrentScript().src).origin;
|
68
src/initializer/iframe-container.ts
Normal file
68
src/initializer/iframe-container.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import assign from 'lodash-es/assign';
|
||||
|
||||
const s4 = () => Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
|
||||
|
||||
const generateId = () => `${s4()}${s4()}${s4()}${s4()}`;
|
||||
|
||||
const styles = {
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'auto',
|
||||
visibility: 'visible',
|
||||
border: '0 none transparent',
|
||||
display: 'none',
|
||||
margin: '0px',
|
||||
padding: '0px',
|
||||
position: 'fixed',
|
||||
left: '0',
|
||||
top: '0',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
zIndex: 2147483647
|
||||
};
|
||||
|
||||
const create = (origin: string): HTMLIFrameElement => {
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('src', `${origin}/v1/app.html`);
|
||||
iframe.setAttribute('name', `rbkmoney-wallet-${generateId()}`);
|
||||
iframe.setAttribute('class', 'rbkmoney-wallet');
|
||||
iframe.setAttribute('allowtransparency', 'true');
|
||||
iframe.setAttribute('frameborder', '0');
|
||||
assign(iframe.style, styles);
|
||||
return iframe;
|
||||
};
|
||||
|
||||
export class IframeContainer {
|
||||
|
||||
private container: HTMLIFrameElement;
|
||||
|
||||
constructor(origin: string) {
|
||||
this.container = create(origin);
|
||||
this.render();
|
||||
}
|
||||
|
||||
reinitialize(): void {
|
||||
this.hide();
|
||||
this.destroy();
|
||||
this.render();
|
||||
}
|
||||
|
||||
render(): void {
|
||||
document.body.appendChild(this.container);
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
document.body.removeChild(this.container);
|
||||
}
|
||||
|
||||
show(): void {
|
||||
this.container.style.display = 'block';
|
||||
}
|
||||
|
||||
hide(): void {
|
||||
this.container.style.display = 'none';
|
||||
}
|
||||
|
||||
getName(): string {
|
||||
return this.container.getAttribute('name');
|
||||
}
|
||||
}
|
31
src/initializer/iframe-initializer.ts
Normal file
31
src/initializer/iframe-initializer.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { IframeContainer } from './iframe-container';
|
||||
import { Parent, PossibleEvents, Transport } from '../communication';
|
||||
import { Initializer } from './initializer';
|
||||
import { InitializerData } from '../communication/model';
|
||||
|
||||
export class IframeInitializer extends Initializer {
|
||||
|
||||
private container: IframeContainer;
|
||||
|
||||
constructor(protected accessToken: string, protected origin: string) {
|
||||
super(accessToken, origin);
|
||||
this.container = new IframeContainer(origin);
|
||||
}
|
||||
|
||||
open(data: InitializerData): Promise<Transport> {
|
||||
const target = (window.frames as any)[this.container.getName()];
|
||||
this.container.show();
|
||||
const parent = new Parent(target, this.origin);
|
||||
return new Promise((resolve) => {
|
||||
return parent.sendHandshake().then((transport) => {
|
||||
transport.emit(PossibleEvents.init, {data});
|
||||
resolve(transport);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
close(): void {
|
||||
this.container.reinitialize();
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
import * as isMobile from 'ismobilejs';
|
||||
import isString from 'lodash-es/isString';
|
||||
import isFunction from 'lodash-es/isFunction';
|
||||
import { Initializer } from './initializer';
|
||||
import { PopupInitializer } from './popup-initializer';
|
||||
import { IframeInitializer } from './iframe-initializer';
|
||||
import {
|
||||
StartIdentityChallengeParams,
|
||||
CancelEvent,
|
||||
IdentityChallengeEvent,
|
||||
WalletUtilsEvent,
|
||||
CreateOutputEvent,
|
||||
CreateOutputParams
|
||||
} from './model';
|
||||
import { getOrigin } from '../get-origin';
|
||||
import { PossibleEvents, Transport } from '../communication';
|
||||
import { ActionType, UserIdentityInitializerData, CreateOutputInitializerData } from '../communication/model';
|
||||
|
||||
const logPrefix = '[RBKmoney wallet utils]';
|
||||
|
||||
const origin = getOrigin();
|
||||
|
||||
const getInitializer = (accessToken: string): Initializer =>
|
||||
isMobile.any
|
||||
? new PopupInitializer(accessToken, origin)
|
||||
: new IframeInitializer(accessToken, origin);
|
||||
|
||||
const toIdentityInitializerData = (token: string, params: StartIdentityChallengeParams): UserIdentityInitializerData => {
|
||||
if (!params) {
|
||||
throw new Error(`${logPrefix}: Missing StartIdentityChallengeParams`);
|
||||
}
|
||||
if (!isString(params.identityID)) {
|
||||
throw new Error(`${logPrefix}: Wrong identityID`);
|
||||
}
|
||||
return {
|
||||
token,
|
||||
params,
|
||||
type: ActionType.userIdentity
|
||||
};
|
||||
};
|
||||
|
||||
const toOutputInitializerData = (token: string, params: CreateOutputParams): CreateOutputInitializerData => {
|
||||
if (!params) {
|
||||
throw new Error(`${logPrefix}: Missing CreateOutputParams`);
|
||||
}
|
||||
if (!isString(params.identityID)) {
|
||||
throw new Error(`${logPrefix}: Wrong identityID`);
|
||||
}
|
||||
if (!isString(params.name)) {
|
||||
throw new Error(`${logPrefix}: Wrong name`);
|
||||
}
|
||||
return {
|
||||
token,
|
||||
params,
|
||||
type: ActionType.createOutput
|
||||
};
|
||||
};
|
||||
|
||||
export class RbkmoneyWalletUtils {
|
||||
|
||||
onCompleteIdentityChallenge: (event: IdentityChallengeEvent) => void;
|
||||
onFailIdentityChallenge: (event: IdentityChallengeEvent) => void;
|
||||
onCancelIdentityChallenge: (event: IdentityChallengeEvent) => void;
|
||||
onCreateOutput: (event: CreateOutputEvent) => void;
|
||||
onCancel: (event: CancelEvent) => void;
|
||||
private initializer: Initializer;
|
||||
|
||||
constructor(private readonly token: string) {
|
||||
if (!isString(token)) {
|
||||
throw new Error(`${logPrefix}: Wrong param token`);
|
||||
}
|
||||
this.token = token;
|
||||
this.initializer = getInitializer(token);
|
||||
}
|
||||
|
||||
startIdentityChallenge(params: StartIdentityChallengeParams): void {
|
||||
const data = toIdentityInitializerData(this.token, params);
|
||||
this.initializer.open(data)
|
||||
.then((transport: Transport) => {
|
||||
transport.on(PossibleEvents.onCompleteIdentityChallenge, (e) =>
|
||||
this.provideCallback(this.onCompleteIdentityChallenge, {
|
||||
data: e.data
|
||||
}));
|
||||
transport.on(PossibleEvents.onFailIdentityChallenge, (e) =>
|
||||
this.provideCallback(this.onFailIdentityChallenge, {
|
||||
data: e.data
|
||||
}));
|
||||
transport.on(PossibleEvents.onCancelIdentityChallenge, (e) =>
|
||||
this.provideCallback(this.onCancelIdentityChallenge, {
|
||||
data: e.data
|
||||
}));
|
||||
transport.on(PossibleEvents.onCancel, () => {
|
||||
this.provideCallback(this.onCancel, {});
|
||||
});
|
||||
})
|
||||
.catch((e) => this.provideCallback(this.onCancel, {error: e}));
|
||||
}
|
||||
|
||||
createOutput(params: CreateOutputParams): void {
|
||||
const data = toOutputInitializerData(this.token, params);
|
||||
this.initializer.open(data)
|
||||
.then((transport: Transport) => {
|
||||
transport.on(PossibleEvents.onCreateOutput, (e) =>
|
||||
this.provideCallback(this.onCreateOutput, {
|
||||
data: e.data
|
||||
}));
|
||||
})
|
||||
.catch((e) => this.provideCallback(this.onCancel, {error: e}));
|
||||
}
|
||||
|
||||
private provideCallback(callback: (data: WalletUtilsEvent) => void, data: any): void {
|
||||
if (isFunction(callback)) {
|
||||
callback({
|
||||
target: this,
|
||||
...data
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* tslint:disable */
|
||||
(window as any).RbkmoneyWalletUtils = RbkmoneyWalletUtils;
|
||||
/* tslint:enable */
|
11
src/initializer/initializer.ts
Normal file
11
src/initializer/initializer.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { Transport } from '../communication';
|
||||
import { InitializerData } from '../communication/model';
|
||||
|
||||
export abstract class Initializer {
|
||||
|
||||
protected constructor(protected readonly token: string, protected readonly origin: string) {}
|
||||
|
||||
abstract open(data: InitializerData): Promise<Transport>;
|
||||
|
||||
abstract close(): void;
|
||||
}
|
4
src/initializer/model/cancel-event-type.ts
Normal file
4
src/initializer/model/cancel-event-type.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export enum CancelEventType {
|
||||
uiDismissing = 'uiDismissing',
|
||||
error = 'error'
|
||||
}
|
9
src/initializer/model/cancel-event.ts
Normal file
9
src/initializer/model/cancel-event.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { WalletUtilsEvent } from './wallet-utils-event';
|
||||
|
||||
export interface CancelEvent extends WalletUtilsEvent {
|
||||
type: CancelEventType;
|
||||
}
|
||||
|
||||
export enum CancelEventType {
|
||||
cancel = 'cancel'
|
||||
}
|
5
src/initializer/model/create-output-event.ts
Normal file
5
src/initializer/model/create-output-event.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { Output, WalletUtilsEvent } from '.';
|
||||
|
||||
export interface CreateOutputEvent extends WalletUtilsEvent {
|
||||
output: Output;
|
||||
}
|
4
src/initializer/model/create-output-params.ts
Normal file
4
src/initializer/model/create-output-params.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export interface CreateOutputParams {
|
||||
identityID: string;
|
||||
name: string;
|
||||
}
|
5
src/initializer/model/error-cancel-event.ts
Normal file
5
src/initializer/model/error-cancel-event.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { LogicError, CancelEvent } from '.';
|
||||
|
||||
export interface ErrorCancelEvent extends CancelEvent {
|
||||
error: LogicError;
|
||||
}
|
6
src/initializer/model/identity-challenge-event.ts
Normal file
6
src/initializer/model/identity-challenge-event.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { WalletUtilsEvent } from './wallet-utils-event';
|
||||
import { IdentityChallenge } from './identity-challenge';
|
||||
|
||||
export interface IdentityChallengeEvent extends WalletUtilsEvent {
|
||||
identityChallenge: IdentityChallenge;
|
||||
}
|
1
src/initializer/model/identity-challenge.ts
Normal file
1
src/initializer/model/identity-challenge.ts
Normal file
@ -0,0 +1 @@
|
||||
export type IdentityChallenge = any;
|
10
src/initializer/model/index.ts
Normal file
10
src/initializer/model/index.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export * from './cancel-event';
|
||||
export * from './create-output-event';
|
||||
export * from './create-output-params';
|
||||
export * from './identity-challenge';
|
||||
export * from './identity-challenge-event';
|
||||
export * from './start-identity-challenge-params';
|
||||
export * from './wallet-utils-event';
|
||||
export * from './output';
|
||||
export * from './error-cancel-event';
|
||||
export * from './logic-error';
|
4
src/initializer/model/logic-error.ts
Normal file
4
src/initializer/model/logic-error.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export interface LogicError {
|
||||
code: string;
|
||||
message: string;
|
||||
}
|
1
src/initializer/model/output.ts
Normal file
1
src/initializer/model/output.ts
Normal file
@ -0,0 +1 @@
|
||||
export type Output = any;
|
8
src/initializer/model/start-identity-challenge-params.ts
Normal file
8
src/initializer/model/start-identity-challenge-params.ts
Normal file
@ -0,0 +1,8 @@
|
||||
declare enum IdentityLevel {
|
||||
partial = 'partial'
|
||||
}
|
||||
|
||||
export interface StartIdentityChallengeParams {
|
||||
readonly identityID: string;
|
||||
readonly level?: IdentityLevel;
|
||||
}
|
5
src/initializer/model/wallet-utils-event.ts
Normal file
5
src/initializer/model/wallet-utils-event.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { RbkmoneyWalletUtils } from '../index';
|
||||
|
||||
export interface WalletUtilsEvent {
|
||||
target: RbkmoneyWalletUtils;
|
||||
}
|
40
src/initializer/popup-initializer.ts
Normal file
40
src/initializer/popup-initializer.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { Parent, Transport } from '../communication';
|
||||
import { Initializer } from './initializer';
|
||||
import { InitializerData } from '../communication/model';
|
||||
|
||||
const serialize = (params: any): string => {
|
||||
let urlParams = '';
|
||||
for (const prop in params) {
|
||||
if (params.hasOwnProperty(prop)) {
|
||||
const value = params[prop];
|
||||
if ((typeof value === 'function') || (value === undefined) || (value === null)) {
|
||||
continue;
|
||||
}
|
||||
if (urlParams !== '') {
|
||||
urlParams += '&';
|
||||
}
|
||||
urlParams += `${prop}=${encodeURIComponent(value)}`;
|
||||
}
|
||||
}
|
||||
return urlParams;
|
||||
};
|
||||
|
||||
export class PopupInitializer extends Initializer {
|
||||
|
||||
constructor(protected accessToken: string, protected origin: string) {
|
||||
super(accessToken, origin);
|
||||
}
|
||||
|
||||
open(data: InitializerData): Promise<Transport> {
|
||||
const url = `${this.origin}/v1/app.html?${serialize(data)}`;
|
||||
const target = window.open(url);
|
||||
const parent = new Parent(target, this.origin);
|
||||
return new Promise((resolve) => {
|
||||
return parent.sendHandshake().then((transport) => {
|
||||
resolve(transport);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
close(): void {}
|
||||
}
|
7
src/is-in-iframe.ts
Normal file
7
src/is-in-iframe.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export const isInFrame = (): boolean => {
|
||||
try {
|
||||
return window.self === window.top;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
17
tslint.json
17
tslint.json
@ -10,22 +10,23 @@
|
||||
"object-literal-sort-keys": false,
|
||||
"interface-name": false,
|
||||
"no-namespace": false,
|
||||
"no-empty": false,
|
||||
|
||||
"no-var-keyword": true,
|
||||
"no-parameter-reassignment": true,
|
||||
"typedef": [true, "call-signature"],
|
||||
|
||||
"readonly-keyword": true,
|
||||
"readonly-keyword": false,
|
||||
"readonly-array": true,
|
||||
"no-let": true,
|
||||
"no-object-mutation": true,
|
||||
"no-let": false,
|
||||
"no-object-mutation": false,
|
||||
"no-delete": true,
|
||||
"no-method-signature": true,
|
||||
"no-method-signature": false,
|
||||
|
||||
"no-this": true,
|
||||
"no-class": true,
|
||||
"no-this": false,
|
||||
"no-class": false,
|
||||
"no-mixed-interface": true,
|
||||
"no-expression-statement": true,
|
||||
"no-if-statement": true
|
||||
"no-expression-statement": false,
|
||||
"no-if-statement": false
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user