react-native-adapty
Version:
Adapty React Native SDK
246 lines • 10.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ViewController = void 0;
const tslib_1 = require("tslib");
const view_emitter_1 = require("./view-emitter");
const types_1 = require("./types");
const logger_1 = require("../logger");
const adapty_paywall_1 = require("../coders/adapty-paywall");
const bridge_1 = require("../bridge");
const adapty_ui_dialog_config_1 = require("../coders/adapty-ui-dialog-config");
/**
* Provides methods to control created paywall view
* @public
*/
class ViewController {
/**
* Intended way to create a ViewController instance.
* It prepares a native controller to be presented
* and creates reference between native controller and JS instance
* @internal
*/
static create(paywall, params) {
var _a, _b;
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const ctx = new logger_1.LogContext();
const log = ctx.call({ methodName: 'createPaywallView' });
log.start({ paywall, params });
const view = new ViewController();
const coder = new adapty_paywall_1.AdaptyPaywallCoder();
const methodKey = 'adapty_ui_create_view';
const data = {
method: methodKey,
paywall: coder.encode(paywall),
preload_products: (_a = params.prefetchProducts) !== null && _a !== void 0 ? _a : true,
load_timeout: ((_b = params.loadTimeoutMs) !== null && _b !== void 0 ? _b : 5000) / 1000,
};
if (params.customTags) {
data['custom_tags'] = params.customTags;
}
if (params.customTimers) {
const convertTimerInfo = (timerInfo) => {
const formatDate = (date) => {
const pad = (num, digits = 2) => {
const str = num.toString();
const paddingLength = digits - str.length;
return paddingLength > 0 ? '0'.repeat(paddingLength) + str : str;
};
const year = date.getUTCFullYear();
const month = pad(date.getUTCMonth() + 1);
const day = pad(date.getUTCDate());
const hours = pad(date.getUTCHours());
const minutes = pad(date.getUTCMinutes());
const seconds = pad(date.getUTCSeconds());
const millis = pad(date.getUTCMilliseconds(), 3);
return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${millis}Z`;
};
const result = {};
for (const key in timerInfo) {
if (timerInfo.hasOwnProperty(key)) {
const date = timerInfo[key];
if (date instanceof Date) {
result[key] = formatDate(date);
}
}
}
return result;
};
data['custom_timers'] = convertTimerInfo(params.customTimers);
}
const body = JSON.stringify(data);
const result = yield view.handle(methodKey, body, 'AdaptyUiView', ctx, log);
view.id = result.id;
return view;
});
}
/**
* Since constructors in JS cannot be async, it is not
* preferred to create ViewControllers in direct way.
* Consider using @link{ViewController.create} instead
*
* @remarks
* Creating ViewController this way does not let you
* to make native create request and set _id.
* It is intended to avoid usage
*
* @internal
*/
constructor() {
this.unsubscribeAllListeners = null;
this.id = null;
}
handle(method, params, resultType, ctx, log) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
try {
const result = yield bridge_1.$bridge.request(method, params, resultType, ctx);
log.success(result);
return result;
}
catch (error) {
/*
* Success because error was handled validly
* It is a developer task to define which errors must be logged
*/
log.success({ error });
throw error;
}
});
}
/**
* Presents a paywall view as a full-screen modal
*
* @remarks
* Calling `present` upon already visible paywall view
* would result in an error
*
* @throws {AdaptyError}
*/
present() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const ctx = new logger_1.LogContext();
const log = ctx.call({ methodName: 'present' });
log.start({ _id: this.id });
if (this.id === null) {
log.failed({ error: 'no _id' });
throw this.errNoViewReference();
}
const methodKey = 'adapty_ui_present_view';
const body = JSON.stringify({
method: methodKey,
id: this.id,
});
const result = yield this.handle(methodKey, body, 'Void', ctx, log);
return result;
});
}
/**
* Dismisses a paywall view
*
* @throws {AdaptyError}
*/
dismiss() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const ctx = new logger_1.LogContext();
const log = ctx.call({ methodName: 'dismiss' });
log.start({ _id: this.id });
if (this.id === null) {
log.failed({ error: 'no id' });
throw this.errNoViewReference();
}
const methodKey = 'adapty_ui_dismiss_view';
const body = JSON.stringify({
method: methodKey,
id: this.id,
destroy: false,
});
yield this.handle(methodKey, body, 'Void', ctx, log);
if (this.unsubscribeAllListeners) {
this.unsubscribeAllListeners();
}
});
}
/**
* Presents the dialog
*
* @param {AdaptyUiDialogConfig} config - A config for showing the dialog.
*
* @remarks
* If you provide two actions in the config, be sure `primaryAction` cancels the operation
* and leaves things unchanged.
*
* @returns {Promise<AdaptyUiDialogActionType>} A Promise that resolves to the {@link AdaptyUiDialogActionType} object
*
* @throws {AdaptyError}
*/
showDialog(config) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const ctx = new logger_1.LogContext();
const log = ctx.call({ methodName: 'showDialog' });
log.start({ _id: this.id });
if (this.id === null) {
log.failed({ error: 'no id' });
throw this.errNoViewReference();
}
const coder = new adapty_ui_dialog_config_1.AdaptyUiDialogConfigCoder();
const methodKey = 'adapty_ui_show_dialog';
const body = JSON.stringify({
method: methodKey,
id: this.id,
configuration: coder.encode(config),
});
return yield this.handle(methodKey, body, 'Void', ctx, log);
});
}
/**
* Creates a set of specific view event listeners
*
* @see {@link https://docs.adapty.io/docs/react-native-handling-events | [DOC] Handling View Events}
*
* @remarks
* It registers only requested set of event handlers.
* Your config is assigned into four event listeners {@link DEFAULT_EVENT_HANDLERS},
* that handle default closing behavior.
* - `onCloseButtonPress`
* - `onAndroidSystemBack`
* - `onRestoreCompleted`
* - `onPurchaseCompleted`
*
* If you want to override these listeners, we strongly recommend to return `true` (or `purchaseResult.type !== 'user_cancelled'` in case of `onPurchaseCompleted`)
* from your custom listener to retain default closing behavior.
*
* @param {Partial<EventHandlers> | undefined} [eventHandlers] - set of event handling callbacks
* @returns {() => void} unsubscribe - function to unsubscribe all listeners
*/
registerEventHandlers(eventHandlers = types_1.DEFAULT_EVENT_HANDLERS) {
const ctx = new logger_1.LogContext();
const log = ctx.call({ methodName: 'registerEventHandlers' });
log.start({ _id: this.id });
if (this.id === null) {
throw this.errNoViewReference();
}
const finalEventHandlers = Object.assign(Object.assign({}, types_1.DEFAULT_EVENT_HANDLERS), eventHandlers);
// DIY way to tell TS that original arg should not be used
const deprecateVar = (_target) => true;
if (!deprecateVar(eventHandlers)) {
return () => { };
}
const viewEmitter = new view_emitter_1.ViewEmitter(this.id);
Object.keys(finalEventHandlers).forEach(eventStr => {
const event = eventStr;
if (!finalEventHandlers.hasOwnProperty(event)) {
return;
}
const handler = finalEventHandlers[event];
viewEmitter.addListener(event, handler, () => this.dismiss());
});
const unsubscribe = () => viewEmitter.removeAllListeners();
// expose to class to be able to unsubscribe on dismiss
this.unsubscribeAllListeners = unsubscribe;
return unsubscribe;
}
errNoViewReference() {
throw new Error('View reference not found');
}
}
exports.ViewController = ViewController;
//# sourceMappingURL=view-controller.js.map