@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
JavaScript
"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();
}