Issue #640 - migrate local browser save to indexeddb
This commit is contained in:
parent
30ecd41452
commit
ed749a747f
7 changed files with 273 additions and 23 deletions
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -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 () {
|
||||||
|
|
171
src/js/service/storage/IndexedDbStorageService.js
Normal file
171
src/js/service/storage/IndexedDbStorageService.js
Normal 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);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})();
|
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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');
|
||||||
|
};
|
||||||
|
|
||||||
|
})();
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in a new issue