Storage.js

/* global platypus, window */
import DataMap from './DataMap.js';
import {UserData} from 'springroll';

/**
 * This class is used to create the Platypus storage system accessible at `platypus.storage`. It uses Springroll UserData if available, with a fallback to local storage if not.
 *
 * @memberof platypus
 * @class Storage
 * @param {*} springroll 
 * @param {*} options 
 * @return {Data} Returns the new Storage object.
 */
class Storage {
    constructor (springroll, options) {
        const
            gameId = options.name,
            storageKey = gameId + '-data',
            unconnectedData = window.localStorage.getItem(storageKey),
            keys = options.storageKeys || null,
            handleData = (resp) => {
                const
                    data = resp && resp.data ? resp.data : resp;

                if (data) {
                    for (const key in data) {
                        if (data.hasOwnProperty(key)) {
                            if (this.map.has(key)) {
                                this.map.set(key, data[key]);
                            } else {
                                this.addKey(key, data[key]);
                            }
                        }
                    }
                }
            };

        this.map = DataMap.setUp();

        /**
         * The storage key being used to store data.
         *
         * @property storageKey
         * @type String
         * @default options.name+'-data'
         */
        this.storageKey = storageKey;

        /**
         * Whether Springroll is connected to a hosting page.
         *
         * @property connected
         * @type Boolean
         * @default false
         */
        this.connected = false;

        if (keys) {
            for (let i = 0; i < keys.length; i++) {
                this.addKey(keys[i], null);
            }
        }

        try { // May throw if data is not parseable. If so, we'll just ignore it.
            handleData(JSON.parse(unconnectedData));
        } catch (e) {}

        springroll.container.on('connected', () => {
            this.connected = true;
            UserData.read(this.storageKey).then(handleData).catch((e) => {
                platypus.debug.warn('Storage: connected but received an error', e);
            });
        });
    }

    /**
     * Adds a storage key to the game's storage.
     *
     * @param {String} key The key to add.
     * @param {*} value The data to store at this defined key.
     */
    addKey (key, value) {
        this.map.set(key, value);
        Object.defineProperty(this, key, {
            get: function () {
                return this.map.get(key);
            },
            set: function (value) {
                this.map.set(key, value);

                this.save();
            },
            enumerable: true
        });
    }

    /**
     * Gets a value from storage for the provided storage key.
     *
     * @param {String} key The key for the data to return
     * @return {*}
     */
    get (key) {
        if (!this.map.has(key)) {
            this.addKey(key, null);
        }

        return this[key];
    }

    /**
     * Takes the current game storage and saves it to local storage or Springroll UserData
     *
     */
    save () {
        const save = this.map.toJSON();
        
        if (this.connected) {
            UserData.write(this.storageKey, save).catch((e) => {
                platypus.debug.warn('Storage: tried to save but received an error', e);
            });
        } else {
            window.localStorage.setItem(this.storageKey, JSON.stringify(save));
        }
    }

    /**
     * Updates a storage key's data. Creates the key if it does not exist.
     *
     * @param {String} key The key to update.
     * @param {*} value The data to store at this key.
     */
    set (key, value) {
        if (!this.map.has(key)) {
            this.addKey(key, value);
            this.save();
        } else {
            this[key] = value;
        }
    }
};

export default Storage;