wikimedia/mediawiki-extensions-MobileFrontend

View on GitHub
src/mobile.startup/promoCampaign/promoCampaign.js

Summary

Maintainability
A
3 hrs
Test Coverage
/**
 * Creates a campaign that makes showing promo drawers, modals, etc that should
 * only be shown once (using localStorage) per action (when page loads, when
 * user clicks on a link, etc) easier. The campaign executes a given callback
 * (e.g. showing a drawer or modal) for a specific action only when it is
 * eligible. A campaign can have multiple arbitrary actions via the supplied
 * `actions` object. An action is either 'eligible' or 'ineligible' at any given
 * time. If `ineligible`, the `showIfEligible` will not execute the `onShow`
 * callback.
 *
 * @ignore
 * @param {Function} onShow A callback intended to show something related to the
 * campaign (drawer, modal, etc) when executed. The callback will only execute
 * after the client calls `showIfEligible` and only if the passed in action is
 * 'eligible'.
 * @param {Object} actions Object of arbitrary actions that are intended to be
 * either "eligible" or "ineligible" at any given time (onPageLoad,
 * onHistoryLinkClick). The `onShow` callback will only be executed when an
 * action is 'eligible'. For each action, the key and value should be the same
 * (e.g. "onLoad":"onLoad") to mimic an enum. The client is responsible for
 * notifying when each action becomes "ineligible" by calling the
 * `makeActionIneligible` method. All actions can be marked as ineligible by
 * calling the `makeAllActionsIneligible` method.
 * @param {string} campaignName Name of campaign. This is only used to form part
 * of the localStorage key for each action.
 * @param {boolean} campaignActive Is campaign active
 * @param {boolean} userEligible Is current user eligible
 * @param {mw.storage} mwStorage Used to mark actions as ineligible
 * into localStorage
 * @return {mobile.startup/AmcOutreach~PromoCampaign}
 */
function createPromoCampaign(
    onShow,
    actions,
    campaignName,
    campaignActive,
    userEligible,
    mwStorage
) {
    // This object maps actions to localStorage keys
    const ACTIONS_TO_STORAGE_KEYS = {};
    for ( const key in actions ) {
        const a = actions[ key ];
        ACTIONS_TO_STORAGE_KEYS[a] = `mobile-frontend-${ campaignName }-ineligible-${ a }`;
    }

    /**
     * @return {boolean}
     */
    function isCampaignActive() {
        return campaignActive;
    }

    /**
     * @param {string} action
     * @throws {Error} Throws an error if action is not valid.
     */
    function validateAction( action ) {
        if ( !( action in actions ) ) {
            throw new Error(
                `Action '${ action }' not found in 'actions' object. Please add this to
                the object when creating a campaign with promoCampaign.js if you believe
                this is a valid action.`
            );
        }
    }

    /**
     * @param {string} action Will check the eligibility of this action. This
     * should be a value in the actions object.
     * @throws {Error} Throws an error if action is not valid.
     * @return {boolean}
     */
    function isActionEligible( action ) {
        validateAction( action );

        return isCampaignActive() &&
            userEligible &&
            mwStorage.get( ACTIONS_TO_STORAGE_KEYS[action] ) === null;
    }

    return {
        /**
         * @ignore
         * @param {string} action Should be one of the values in the
         * actions param
         * @param {...*} [args] Args to pass to the onShow callback
         * @throws {Error} Throws an error if action is not valid.
         * @return {module:mobile.startup/Drawer|null} Returns Drawer if drawer is eligible to be shown and
         * null if not.
         */
        showIfEligible( action, ...args ) {
            if ( !isActionEligible( action ) ) {
                // If not eligible, there is no sense in continuing.
                return null;
            }

            return onShow( action, ...args );
        },
        /**
         * @ignore
         * @param {string} action
         * @throws {Error} Throws an error if action is not valid.
         * @return {boolean} Whether the save operation was successful
         */
        makeActionIneligible( action ) {
            validateAction( action );

            // The value here actually doesn't matter. The only thing that matters is
            // if this key exists in localStorage.
            return mwStorage.set( ACTIONS_TO_STORAGE_KEYS[action], '~' );
        },
        makeAllActionsIneligible() {
            let key, action;
            for ( key in actions ) {
                action = actions[key];
                this.makeActionIneligible( action );
            }
        },
        isCampaignActive
    };
}

module.exports = createPromoCampaign;