UNPKG

@shopify/app-bridge

Version:

**Shopify is doubling our engineering staff in 2021! [Join our team and work on libraries like this one.](https://smrtr.io/5GGrc)**

424 lines (423 loc) 17.4 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __spreadArrays = (this && this.__spreadArrays) || function () { for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; for (var r = Array(s), k = 0, i = 0; i < il; i++) for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.NonSnakeCaseGroup = exports.findMatchInEnum = exports.forEachInEnum = exports.getMergedProps = exports.updateActionFromPayload = exports.ActionSetWithChildren = exports.ActionSet = exports.isValidOptionalString = exports.isValidOptionalNumber = exports.getEventNameSpace = exports.getVersion = exports.actionWrapper = void 0; var types_1 = require("../client/types"); var collection_1 = require("../util/collection"); var Error_1 = require("./Error"); var constants_1 = require("./constants"); var merge_1 = __importDefault(require("./merge")); var types_2 = require("./types"); var uuid_1 = __importDefault(require("./uuid")); // eslint-disable-next-line @typescript-eslint/no-var-requires var packageJson = require('../package.json'); function actionWrapper(action) { return __assign(__assign({}, action), { version: getVersion(), clientInterface: { name: packageJson.name, version: getVersion(), } }); } exports.actionWrapper = actionWrapper; function getVersion() { return packageJson.version; } exports.getVersion = getVersion; /** * Returns full event name with prefix, group, subgroups and type formatted with separators * @internal * */ function getEventNameSpace(group, eventName, component) { if (eventName.startsWith("" + constants_1.PREFIX + constants_1.SEPARATOR)) { return eventName; } var eventNameSpace = groupToEventNameSpace(group); if (component) { var subgroups_1 = component.subgroups, type = component.type; if (subgroups_1 && subgroups_1.length > 0) { eventNameSpace += eventNameSpace.length > 0 ? constants_1.SEPARATOR : ''; subgroups_1.forEach(function (subgroup, index) { eventNameSpace += "" + subgroup.toUpperCase() + (index < subgroups_1.length - 1 ? constants_1.SEPARATOR : ''); }); } if (type !== group && type) { eventNameSpace += "" + (eventNameSpace.length > 0 ? constants_1.SEPARATOR : '') + type.toUpperCase(); } } if (eventNameSpace) { eventNameSpace += "" + (eventNameSpace.length > 0 ? constants_1.SEPARATOR : '') + eventName.toUpperCase(); } return "" + constants_1.PREFIX + constants_1.SEPARATOR + eventNameSpace; } exports.getEventNameSpace = getEventNameSpace; function isValidOptionalNumber(value) { return value === null || value === undefined || typeof value === 'number'; } exports.isValidOptionalNumber = isValidOptionalNumber; function isValidOptionalString(value) { return value === null || value === undefined || typeof value === 'string'; } exports.isValidOptionalString = isValidOptionalString; var ActionSet = /** @class */ (function () { function ActionSet(app, type, group, id) { var _this = this; this.app = app; this.type = type; this.group = group; this.subgroups = []; this.subscriptions = []; if (!app) { Error_1.throwError(Error_1.Action.INVALID_ACTION, 'Missing required `app`'); } this.id = id || uuid_1.default(); this.defaultGroup = group; var defaultSet = this.set; this.set = function () { var _a; var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (!_this.app.hooks) { return defaultSet.apply(_this, args); } return (_a = _this.app.hooks).run.apply(_a, __spreadArrays([types_1.LifecycleHook.UpdateAction, defaultSet, _this], args)); }; } ActionSet.prototype.set = function () { var _ = []; for (var _i = 0; _i < arguments.length; _i++) { _[_i] = arguments[_i]; } }; Object.defineProperty(ActionSet.prototype, "component", { get: function () { return { id: this.id, subgroups: this.subgroups, type: this.type, }; }, enumerable: false, configurable: true }); ActionSet.prototype.updateSubscription = function (subscriptionToRemove, group, subgroups) { var eventType = subscriptionToRemove.eventType, callback = subscriptionToRemove.callback, component = subscriptionToRemove.component; var currentIndex; currentIndex = this.subscriptions.findIndex(function (subscription) { return subscription === subscriptionToRemove; }); if (currentIndex >= 0) { this.subscriptions[currentIndex].unsubscribe(); } else { currentIndex = undefined; } this.group = group; this.subgroups = subgroups; Object.assign(component, { subgroups: this.subgroups }); return this.subscribe(eventType, callback, component, currentIndex); }; ActionSet.prototype.error = function (callback) { var _this = this; var subscriptionIndices = []; forEachInEnum(Error_1.Action, function (eventNameSpace) { // Keep track of subscription index so we can call unsubscribe later // This ensure it will continue to work even when the subscription has been updated subscriptionIndices.push(_this.subscriptions.length); _this.subscribe(eventNameSpace, callback); }); return function () { var subscriptionsToRemove = subscriptionIndices.map(function (index) { return _this.subscriptions[index]; }); subscriptionsToRemove.forEach(function (toRemove) { collection_1.removeFromCollection(_this.subscriptions, toRemove, function (removed) { removed.unsubscribe(); }); }); }; }; ActionSet.prototype.subscribe = function (eventName, callback, component, currentIndex) { var _this = this; var eventComponent = component || this.component; var eventType = eventName.toUpperCase(); var boundedCallback = typeof currentIndex === 'number' ? callback : callback.bind(this); var eventNameSpace; if (Error_1.isErrorEventName(eventName)) { eventNameSpace = getEventNameSpace(types_2.Group.Error, eventName, __assign(__assign({}, eventComponent), { type: '' })); } else { eventNameSpace = getEventNameSpace(this.group, eventName, eventComponent); } var unsubscribe = this.app.subscribe(eventNameSpace, boundedCallback, component ? component.id : this.id); var subscription = { eventType: eventType, unsubscribe: unsubscribe, callback: boundedCallback, component: eventComponent, updateSubscribe: function (group, subgroups) { return _this.updateSubscription(subscription, group, subgroups); }, }; if (typeof currentIndex === 'number' && currentIndex >= 0 && currentIndex < this.subscriptions.length) { this.subscriptions[currentIndex] = subscription; } else { this.subscriptions.push(subscription); } return unsubscribe; }; ActionSet.prototype.unsubscribe = function (resetOnly) { if (resetOnly === void 0) { resetOnly = false; } unsubscribeActions(this.subscriptions, this.defaultGroup, resetOnly); return this; }; return ActionSet; }()); exports.ActionSet = ActionSet; var ActionSetWithChildren = /** @class */ (function (_super) { __extends(ActionSetWithChildren, _super); function ActionSetWithChildren() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.children = []; return _this; } ActionSetWithChildren.prototype.unsubscribe = function (unsubscribeChildren, resetParentOnly) { if (unsubscribeChildren === void 0) { unsubscribeChildren = true; } if (resetParentOnly === void 0) { resetParentOnly = false; } unsubscribeActions(this.subscriptions, this.defaultGroup, resetParentOnly); this.children.forEach(function (child) { if (child instanceof ActionSetWithChildren) { child.unsubscribe(unsubscribeChildren, !unsubscribeChildren); } else { child.unsubscribe(!unsubscribeChildren); } }); return this; }; ActionSetWithChildren.prototype.getChild = function (id) { var childIndex = this.children.findIndex(function (child) { return child.id === id; }); return childIndex >= 0 ? this.children[childIndex] : undefined; }; ActionSetWithChildren.prototype.getChildIndex = function (id) { return this.children.findIndex(function (child) { return child.id === id; }); }; ActionSetWithChildren.prototype.getChildSubscriptions = function (id, eventType) { return this.subscriptions.filter(function (sub) { return sub.component.id === id && (!eventType || eventType === sub.eventType); }); }; ActionSetWithChildren.prototype.addChild = function (child, group, subgroups) { var _this = this; var subscriptions = child.subscriptions; var existingChild = this.getChild(child.id); // Add child if it doesn't already exist if (!existingChild) { this.children.push(child); } if (!subscriptions || (group === child.group && subgroups === child.subgroups)) { return this; } subscriptions.forEach(function (subscription) { var updateSubscribe = subscription.updateSubscribe; updateSubscribe(group, subgroups); }); // Update child's group and subgroups Object.assign(child, { group: group, subgroups: subgroups }); // Update child's children subscriptions if (child instanceof ActionSetWithChildren) { child.children.forEach(function (childIter) { return _this.addChild(childIter, group, subgroups); }); } return this; }; ActionSetWithChildren.prototype.removeChild = function (id) { var _this = this; collection_1.removeFromCollection(this.children, this.getChild(id), function () { var toBeRemoved = _this.subscriptions.filter(function (subs) { return subs.component.id === id; }); toBeRemoved.forEach(function (toRemove) { collection_1.removeFromCollection(_this.subscriptions, toRemove, function (removed) { removed.unsubscribe(); }); }); }); return this; }; ActionSetWithChildren.prototype.subscribeToChild = function (child, eventName, callback) { var _this = this; var boundedCallback = callback.bind(this); if (eventName instanceof Array) { eventName.forEach(function (eventNameIter) { return _this.subscribeToChild(child, eventNameIter, callback); }); return this; } if (typeof eventName !== 'string') { return this; } var eventType = eventName.toUpperCase(); var currentSubscriptions = this.getChildSubscriptions(child.id, eventType); if (currentSubscriptions.length > 0) { // Subscription is already there, simply update it currentSubscriptions.forEach(function (subs) { return subs.updateSubscribe(_this.group, child.subgroups); }); } else { var childComponent = { id: child.id, subgroups: child.subgroups, type: child.type, }; this.subscribe(eventType, boundedCallback, childComponent); } return this; }; ActionSetWithChildren.prototype.getUpdatedChildActions = function (newActions, currentActions) { if (newActions.length === 0) { while (currentActions.length > 0) { var action = currentActions.pop(); if (!action) { break; } this.removeChild(action.id); } return undefined; } // Only allow unique actions var uniqueActions = newActions.filter(function (action, index, actionsArr) { return index === actionsArr.indexOf(action); }); var newActionIds = uniqueActions.map(function (action) { return action.id; }); // Remove unused actions var unusedActions = currentActions.filter(function (action) { return newActionIds.indexOf(action.id) < 0; }); while (unusedActions.length > 0) { var action = unusedActions.pop(); if (!action) { break; } this.removeChild(action.id); } return uniqueActions; }; return ActionSetWithChildren; }(ActionSet)); exports.ActionSetWithChildren = ActionSetWithChildren; function unsubscribeActions(subscriptions, defaultGroup, reassign) { if (reassign === void 0) { reassign = false; } subscriptions.forEach(function (subscription) { if (reassign) { var updateSubscribe = subscription.updateSubscribe; // eslint-disable-next-line no-warning-comments // TODO: Support cases where we don't wipe out group and subgroups to defaults updateSubscribe(defaultGroup, []); } else { var unsubscribe = subscription.unsubscribe; unsubscribe(); } }); if (!reassign) { subscriptions.length = 0; } } function updateActionFromPayload(action, newProps) { var id = action.id; if (id === newProps.id) { // Merge new properties Object.assign(action, getMergedProps(action, newProps)); return true; } return false; } exports.updateActionFromPayload = updateActionFromPayload; function getMergedProps(props, newProps) { var merged = merge_1.default(props, newProps); if (!merged) { // tslint:disable-next-line:prefer-object-spread var cloned = Object.assign(props, newProps); return cloned; } return merged; } exports.getMergedProps = getMergedProps; function forEachInEnum(types, callback) { Object.keys(types).forEach(function (key) { callback(types[key]); }); } exports.forEachInEnum = forEachInEnum; function findMatchInEnum(types, lookup) { var match = Object.keys(types).find(function (key) { return lookup === types[key]; }); return match ? types[match] : undefined; } exports.findMatchInEnum = findMatchInEnum; function camelCaseToSnakeCase(value) { return value.replace(/([A-Z])/g, function (matcher, _val, index) { return "" + (index === 0 ? '' : '_') + matcher[0].toLowerCase(); }); } exports.NonSnakeCaseGroup = [ types_2.Group.AuthCode, types_2.Group.Button, types_2.Group.ButtonGroup, types_2.Group.Cart, types_2.Group.Error, types_2.Group.Features, types_2.Group.Fullscreen, types_2.Group.Link, types_2.Group.Loading, types_2.Group.Menu, types_2.Group.Modal, types_2.Group.Navigation, types_2.Group.Pos, types_2.Group.Print, types_2.Group.ResourcePicker, types_2.Group.Scanner, types_2.Group.SessionToken, types_2.Group.Share, types_2.Group.TitleBar, types_2.Group.Toast, ]; /** * Maps the group name to its event name * @internal * @remarks - This method is necessary for the new pattern of using snake case * which makes it more readable and easier to reconstruct the group from an event name. * Example: `ContextualSaveBar` becomes `CONTEXTUAL_SAVE_BAR` * */ function groupToEventNameSpace(group) { if (exports.NonSnakeCaseGroup.includes(group)) { return group.toUpperCase(); } return camelCaseToSnakeCase(group).toUpperCase(); }