export/autosave/AutoSave.js

/*
 * Copyright 2020 WICKLETS LLC
 *
 * This file is part of Wick Engine.
 *
 * Wick Engine is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Wick Engine is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Wick Engine.  If not, see <https://www.gnu.org/licenses/>.
 */

/**
 * Utility class for autosaving projects.
 */
Wick.AutoSave = class {
    /**
     * The key used to store the list of autosaved projects.
     * @type {string}
     */
    static get AUTOSAVES_LIST_KEY () {
        return 'autosaveList';
    }

    /**
     * The prefix to use for keys to save project autosave data.
     * @type {string}
     */
    static get AUTOSAVE_DATA_PREFIX () {
        return 'autosave_';
    }

    /**
     * Saves a given project to localforage.
     * @param {Wick.Project} project - the project to store in the AutoSave system.
     */
    static save (project, callback) {
        if(Wick.AutoSave.ENABLE_PERF_TIMERS) console.time('serialize step')
        var autosaveData = this.generateAutosaveData(project);
        if(Wick.AutoSave.ENABLE_PERF_TIMERS) console.timeEnd('serialize step')

        if(Wick.AutoSave.ENABLE_PERF_TIMERS) console.time('localforage step')
        this.addAutosaveToList(autosaveData, () => {
            this.writeAutosaveData(autosaveData, () => {
                if(Wick.AutoSave.ENABLE_PERF_TIMERS) console.timeEnd('localforage step')
                callback();
            })
        });
    }

    /**
     * Loads a given project from localforage.
     * @param {string} uuid - the UUID of the project to load from the AutoSave system.
     * @param {function} callback
     */
    static load (uuid, callback) {
        this.readAutosaveData(uuid, autosaveData => {
            this.generateProjectFromAutosaveData(autosaveData, project => {
                callback(project);
            })
        });
    }

    /**
     * Deletes a project with a given UUID in the autosaves.
     * @param {string} uuid - uuid of project ot delete.
     * @param {function} callback
     */
    static delete (uuid, callback) {
        this.removeAutosaveFromList(uuid, () => {
            this.deleteAutosaveData(uuid, () => {
                callback();
            });
        });
    }

    /**
     * Generates an object that is writable to localforage from a project.
     * @param {Wick.Project} project - The project to generate data for.
     */
    static generateAutosaveData (project) {
        if(Wick.AutoSave.ENABLE_PERF_TIMERS) console.time('generate objects list')
        var objects = Wick.ObjectCache.getActiveObjects(project);
        if(Wick.AutoSave.ENABLE_PERF_TIMERS) console.timeEnd('generate objects list')

        if(Wick.AutoSave.ENABLE_PERF_TIMERS) console.time('serialize objects list')
        var projectData = project.serialize();
        var objectsData = objects.map(object => {
            return object.serialize();
        });
        if(Wick.AutoSave.ENABLE_PERF_TIMERS) console.timeEnd('serialize objects list')
        var lastModified = projectData.metadata.lastModified;

        return {
            projectData: projectData,
            objectsData: objectsData,
            lastModified: lastModified,
        };
    }

    /**
     * Creates a project from data loaded from the autosave system
     * @param {object} autosaveData - An autosave data object, use generateAutosaveData/readAutosaveData to get this object
     */
    static generateProjectFromAutosaveData (autosaveData, callback) {
        // Deserialize all objects in the project so they are added to the ObjectCache
        autosaveData.objectsData.forEach(objectData => {
            var object = Wick.Base.fromData(objectData);
        });

        // Deserialize the project itself
        var project = Wick.Base.fromData(autosaveData.projectData);

        // Load source files for assets from localforage
        Wick.FileCache.loadFilesFromLocalforage(project, () => {
            project.loadAssets(() => {
                callback(project);
            });
        });
    }

    /**
     * Adds autosaved project data to the list of autosaved projects.
     * @param {Object} projectData -
     */
    static addAutosaveToList (autosaveData, callback) {
        this.getAutosavesList((list) => {
            list.push({
                uuid: autosaveData.projectData.uuid,
                lastModified: autosaveData.lastModified,
            });
            this.updateAutosavesList(list, () => {
                callback();
            })
        });
    }

    /**
     * Removes autosaved project data to the list of autosaved projects.
     * @param {string} uuid -
     */
    static removeAutosaveFromList (uuid, callback) {
        this.getAutosavesList((list) => {
            list = list.filter(item => {
                return item.uuid !== uuid;
            })
            this.updateAutosavesList(list, () => {
                callback();
            })
        });
    }

    /**
     * Get the list of autosaved projects currently in the AutoSave system.
     * @param {function} callback - function to be passed object containing all autosaved projects.
     */
    static getAutosavesList (callback) {
        localforage.getItem(this.AUTOSAVES_LIST_KEY).then(result => {
            var projectList = result || [];

            // Sort by lastModified
            projectList.sort((a,b) => {
                return b.lastModified - a.lastModified;
            });

            callback(projectList);
        });
    }

    /**
     * Updates the list of autosaved projects currently in the AutoSave system.
     * @param {Object} autosaveList - the list of projects
     * @param {function} callback - called when saving is finished
     */
    static updateAutosavesList (autosaveList, callback) {
        localforage.setItem(this.AUTOSAVES_LIST_KEY, autosaveList).then(result => {
            callback();
        });
    }

    /**
     * Save project data into the autosave system.
     * @param {Object} autosaveData - Autosave data of a project, use generateAutosaveData to create this object
     */
    static writeAutosaveData (autosaveData, callback) {
        localforage.setItem(this.AUTOSAVE_DATA_PREFIX + autosaveData.projectData.uuid, autosaveData).then(() => {
            callback();
        });
    }

    /**
     * Load project data from the autosave system.
     * @param {string} uuid - the UUID of the project to load
     */
    static readAutosaveData (uuid, callback) {
        localforage.getItem(this.AUTOSAVE_DATA_PREFIX + uuid).then(result => {
            if(!result) {
                console.error('Could not load autosaveData for project: ' + uuid);
            }
            callback(result);
        });
    }

    /**
     * Deletes project data from the autosave system.
     * @param {string} uuid - the UUID of the project to delete
     */
    static deleteAutosaveData (uuid, callback) {
        localforage.removeItem(this.AUTOSAVE_DATA_PREFIX + uuid).then(() => {
            callback();
        });
    }
}

Wick.AutoSave.ENABLE_PERF_TIMERS = false;