Issue #640 - migrate local browser save to indexeddb

This commit is contained in:
Julian Descottes 2017-06-11 17:32:39 +02:00
parent 30ecd41452
commit ed749a747f
7 changed files with 273 additions and 23 deletions

View file

@ -114,6 +114,9 @@
this.canvasBackgroundController = new pskl.controller.CanvasBackgroundController(); this.canvasBackgroundController = new pskl.controller.CanvasBackgroundController();
this.canvasBackgroundController.init(); this.canvasBackgroundController.init();
this.indexedDbStorageService = new pskl.service.storage.IndexedDbStorageService(this.piskelController);
this.indexedDbStorageService.init();
this.localStorageService = new pskl.service.storage.LocalStorageService(this.piskelController); this.localStorageService = new pskl.service.storage.LocalStorageService(this.piskelController);
this.localStorageService.init(); this.localStorageService.init();

View file

@ -10,7 +10,7 @@
this.localStorageItemTemplate_ = pskl.utils.Template.get('local-storage-item-template'); this.localStorageItemTemplate_ = pskl.utils.Template.get('local-storage-item-template');
this.service_ = pskl.app.localStorageService; this.service_ = pskl.app.indexedDbStorageService;
this.piskelList = $('.local-piskel-list'); this.piskelList = $('.local-piskel-list');
this.prevSessionContainer = $('.previous-session'); this.prevSessionContainer = $('.previous-session');
@ -36,24 +36,24 @@
}; };
ns.BrowseLocalController.prototype.fillLocalPiskelsList_ = function () { ns.BrowseLocalController.prototype.fillLocalPiskelsList_ = function () {
var html = ''; this.service_.getKeys().then(function (keys) {
var keys = this.service_.getKeys(); var html = '';
keys.sort(function (k1, k2) {
keys.sort(function (k1, k2) { if (k1.date < k2.date) {return 1;}
if (k1.date < k2.date) {return 1;} if (k1.date > k2.date) {return -1;}
if (k1.date > k2.date) {return -1;} return 0;
return 0;
});
keys.forEach((function (key) {
var date = pskl.utils.DateUtils.format(key.date, '{{Y}}/{{M}}/{{D}} {{H}}:{{m}}');
html += pskl.utils.Template.replace(this.localStorageItemTemplate_, {
name : key.name,
date : date
}); });
}).bind(this));
var tableBody_ = this.piskelList.get(0).tBodies[0]; keys.forEach((function (key) {
tableBody_.innerHTML = html; var date = pskl.utils.DateUtils.format(key.date, '{{Y}}/{{M}}/{{D}} {{H}}:{{m}}');
html += pskl.utils.Template.replace(this.localStorageItemTemplate_, {
name : key.name,
date : date
});
}).bind(this));
var tableBody_ = this.piskelList.get(0).tBodies[0];
tableBody_.innerHTML = html;
}.bind(this));
}; };
})(); })();

View file

@ -34,7 +34,7 @@
this.saveDesktopAsNewButton = document.querySelector('#save-desktop-as-new-button'); this.saveDesktopAsNewButton = document.querySelector('#save-desktop-as-new-button');
this.saveFileDownloadButton = document.querySelector('#save-file-download-button'); this.saveFileDownloadButton = document.querySelector('#save-file-download-button');
this.safeAddEventListener_(this.saveLocalStorageButton, 'click', this.saveToLocalStorage_); this.safeAddEventListener_(this.saveLocalStorageButton, 'click', this.saveToIndexedDb_);
this.safeAddEventListener_(this.saveGalleryButton, 'click', this.saveToGallery_); this.safeAddEventListener_(this.saveGalleryButton, 'click', this.saveToGallery_);
this.safeAddEventListener_(this.saveDesktopButton, 'click', this.saveToDesktop_); this.safeAddEventListener_(this.saveDesktopButton, 'click', this.saveToDesktop_);
this.safeAddEventListener_(this.saveDesktopAsNewButton, 'click', this.saveToDesktopAsNew_); this.safeAddEventListener_(this.saveDesktopAsNewButton, 'click', this.saveToDesktopAsNew_);
@ -99,7 +99,7 @@
if (pskl.app.isLoggedIn()) { if (pskl.app.isLoggedIn()) {
this.saveToGallery_(); this.saveToGallery_();
} else { } else {
this.saveToLocalStorage_(); this.saveToIndexedDb_();
} }
}; };
@ -111,8 +111,8 @@
this.saveTo_('saveToGallery', false); this.saveTo_('saveToGallery', false);
}; };
ns.SaveController.prototype.saveToLocalStorage_ = function () { ns.SaveController.prototype.saveToIndexedDb_ = function () {
this.saveTo_('saveToLocalStorage', false); this.saveTo_('saveToIndexedDb', false);
}; };
ns.SaveController.prototype.saveToDesktop_ = function () { ns.SaveController.prototype.saveToDesktop_ = function () {

View file

@ -0,0 +1,171 @@
(function () {
var ns = $.namespace('pskl.service.storage');
var DB_NAME = 'PiskelDatabase';
var DB_VERSION = 1;
ns.IndexedDbStorageService = function (piskelController) {
this.piskelController = piskelController;
};
ns.IndexedDbStorageService.prototype.init = function () {
var request = window.indexedDB.open(DB_NAME, DB_VERSION);
request.onerror = this.onRequestError_.bind(this);
request.onupgradeneeded = this.onUpgradeNeeded_.bind(this);
request.onsuccess = this.onRequestSuccess_.bind(this);
};
ns.IndexedDbStorageService.prototype.save = function (piskel) {
var name = piskel.getDescriptor().name;
var description = piskel.getDescriptor().description;
var date = Date.now();
var serialized = pskl.utils.serialization.Serializer.serialize(piskel);
return this.save_(name, description, date, serialized);
};
ns.IndexedDbStorageService.prototype.save_ = function (name, description, date, serialized) {
var deferred = Q.defer();
var objectStore = this.db.transaction(['piskels'], 'readwrite').objectStore('piskels');
var getRequest = objectStore.get(name);
getRequest.onsuccess = function (event) {
console.log('get request successful for name: ' + name);
var data = event.target.result;
if (typeof data !== 'undefined') {
data.serialized = serialized;
data.date = date;
data.description = description;
var putRequest = objectStore.put(data);
putRequest.onerror = function(event) {
console.log('put request failed for name: ' + name);
deferred.reject();
};
putRequest.onsuccess = function(event) {
console.log('put request successful for name: ' + name);
deferred.resolve();
};
} else {
var request = objectStore.add({
name: name,
description: description,
serialized: serialized,
date: date
});
request.onerror = function(event) {
console.log('Failed to save a piskel');
deferred.reject();
};
request.onsuccess = function(event) {
console.log('Successfully saved a piskel');
deferred.resolve();
};
}
};
getRequest.onerror = function () {
console.log('get request failed for name: ' + name);
deferred.reject();
};
return deferred.promise;
};
ns.IndexedDbStorageService.prototype.load = function (name) {
var objectStore = this.db.transaction(['piskels'], 'readwrite').objectStore('piskels');
var getRequest = objectStore.get(name);
getRequest.onsuccess = function (event) {
console.log('get request successful for name: ' + name);
var data = event.target.result;
if (typeof data !== 'undefined') {
var serialized = data.serialized;
pskl.utils.serialization.Deserializer.deserialize(
JSON.parse(serialized),
function (piskel) {
pskl.app.piskelController.setPiskel(piskel);
}
);
} else {
console.log('no local browser save found for name: ' + name);
}
};
getRequest.onerror = function () {
console.log('get request failed for name: ' + name);
};
};
ns.IndexedDbStorageService.prototype.remove = function (name) {
var objectStore = this.db.transaction(['piskels'], 'readwrite').objectStore('piskels');
var deleteRequest = objectStore.delete(name);
deleteRequest.onsuccess = function (event) {
console.log('successfully deleted local browser save for name: ' + name);
};
deleteRequest.onerror = function (event) {
console.log('failed to delete local browser save for name: ' + name);
};
};
ns.IndexedDbStorageService.prototype.list = function () {
var deferred = Q.defer();
var piskels = [];
var objectStore = this.db.transaction(['piskels']).objectStore('piskels');
var cursor = objectStore.openCursor();
cursor.onsuccess = function(event) {
var cursor = event.target.result;
if (cursor) {
piskels.push({
name: cursor.value.name,
date: cursor.value.date,
description: cursor.value.description
});
cursor.continue();
} else {
console.log('Cursor consumed all availabled piskels');
deferred.resolve(piskels);
}
};
cursor.onerror = function () {
deferred.reject();
};
return deferred.promise;
};
ns.IndexedDbStorageService.prototype.getKeys = function () {
return this.list();
};
ns.IndexedDbStorageService.prototype.onRequestError_ = function (event) {
console.log('Failed to initialize IndexedDB, local browser saves will be unavailable.');
};
ns.IndexedDbStorageService.prototype.onRequestSuccess_ = function (event) {
this.db = event.target.result;
console.log('Successfully initialized IndexedDB, local browser saves are available.');
};
ns.IndexedDbStorageService.prototype.onUpgradeNeeded_ = function (event) {
// Set this.db early to allow migration scripts to access it in oncomplete.
this.db = event.target.result;
// Create an object store "piskels" with the autoIncrement flag set as true.
var objectStore = this.db.createObjectStore('piskels', { keyPath : 'name' });
objectStore.transaction.oncomplete = function(event) {
// Migrate existing sprites from LocalStorage
pskl.service.storage.migrate.MigrateLocalStorageToIndexedDb.migrate().then(function () {
console.log('Successfully migrated local storage data to indexed db');
}).catch(function (e) {
console.log('Failed to migrate local storage data to indexed db');
console.error(e);
});
};
};
})();

View file

@ -27,10 +27,15 @@
return this.delegateSave_(pskl.app.galleryStorageService, piskel); return this.delegateSave_(pskl.app.galleryStorageService, piskel);
}; };
// @deprecated, use saveToIndexedDb unless indexedDb is not available.
ns.StorageService.prototype.saveToLocalStorage = function (piskel) { ns.StorageService.prototype.saveToLocalStorage = function (piskel) {
return this.delegateSave_(pskl.app.localStorageService, piskel); return this.delegateSave_(pskl.app.localStorageService, piskel);
}; };
ns.StorageService.prototype.saveToIndexedDb = function (piskel) {
return this.delegateSave_(pskl.app.indexedDbStorageService, piskel);
};
ns.StorageService.prototype.saveToFileDownload = function (piskel) { ns.StorageService.prototype.saveToFileDownload = function (piskel) {
return this.delegateSave_(pskl.app.fileDownloadStorageService, piskel); return this.delegateSave_(pskl.app.fileDownloadStorageService, piskel);
}; };
@ -67,7 +72,7 @@
// wrap in timeout in order to start saving only after event.preventDefault // wrap in timeout in order to start saving only after event.preventDefault
// has been done // has been done
window.setTimeout(function () { window.setTimeout(function () {
this.saveToLocalStorage(this.piskelController.getPiskel()); this.saveToIndexedDb(this.piskelController.getPiskel());
}.bind(this), 0); }.bind(this), 0);
} }
}; };

View file

@ -0,0 +1,69 @@
(function () {
var ns = $.namespace('pskl.service.storage.migrate');
// Simple migration helper to move local storage saves to indexed db.
ns.MigrateLocalStorageToIndexedDb = {};
ns.MigrateLocalStorageToIndexedDb.migrate = function () {
var deferred = Q.defer();
var localStorageService = pskl.app.localStorageService;
var indexedDbStorageService = pskl.app.indexedDbStorageService;
var localStorageKeys = localStorageService.getKeys();
var migrationData = localStorageKeys.map(function (key) {
return {
name: key.name,
description: key.description,
date: key.date,
serialized: localStorageService.getPiskel(key.name)
};
});
// Define the sequential migration process.
// Wait for each sprite to be saved before saving the next one.
var success = true;
var migrateSprite = function (index) {
var data = migrationData[index];
if (!data) {
console.log('Data migration from local storage to indexed db finished.');
if (success) {
ns.MigrateLocalStorageToIndexedDb.deleteLocalStoragePiskels();
}
deferred.resolve();
return;
}
indexedDbStorageService.save_(
data.name,
data.description,
data.date,
data.serialized
).then(function () {
migrateSprite(index + 1);
}).catch(function (e) {
var success = false;
console.error('Failed to migrate local storage sprite for name: ' + data.name);
migrateSprite(index + 1);
});
};
// Start the migration.
migrateSprite(0);
return deferred.promise;
};
ns.MigrateLocalStorageToIndexedDb.deleteLocalStoragePiskels = function () {
var localStorageKeys = pskl.app.localStorageService.getKeys();
// Remove all sprites.
localStorageKeys.forEach(function (key) {
window.localStorage.removeItem('piskel.' + key.name);
});
// Remove keys.
window.localStorage.removeItem('piskel.keys');
};
})();

View file

@ -168,8 +168,10 @@
"js/widgets/Wizard.js", "js/widgets/Wizard.js",
// Services // Services
"js/service/storage/migrate/MigrateLocalStorageToIndexedDb.js",
"js/service/storage/StorageService.js", "js/service/storage/StorageService.js",
"js/service/storage/FileDownloadStorageService.js", "js/service/storage/FileDownloadStorageService.js",
"js/service/storage/IndexedDbStorageService.js",
"js/service/storage/LocalStorageService.js", "js/service/storage/LocalStorageService.js",
"js/service/storage/GalleryStorageService.js", "js/service/storage/GalleryStorageService.js",
"js/service/storage/DesktopStorageService.js", "js/service/storage/DesktopStorageService.js",