Skip to content

Commit

Permalink
Issue #640 - migrate local browser save to indexeddb
Browse files Browse the repository at this point in the history
  • Loading branch information
juliandescottes committed Jul 25, 2017
1 parent f3d067f commit 6edd19a
Show file tree
Hide file tree
Showing 7 changed files with 273 additions and 23 deletions.
3 changes: 3 additions & 0 deletions src/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@
this.canvasBackgroundController = new pskl.controller.CanvasBackgroundController();
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.init();

Expand Down
36 changes: 18 additions & 18 deletions src/js/controller/dialogs/BrowseLocalController.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

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.prevSessionContainer = $('.previous-session');

Expand All @@ -36,24 +36,24 @@
};

ns.BrowseLocalController.prototype.fillLocalPiskelsList_ = function () {
var html = '';
var keys = this.service_.getKeys();

keys.sort(function (k1, k2) {
if (k1.date < k2.date) {return 1;}
if (k1.date > k2.date) {return -1;}
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
this.service_.getKeys().then(function (keys) {
var html = '';
keys.sort(function (k1, k2) {
if (k1.date < k2.date) {return 1;}
if (k1.date > k2.date) {return -1;}
return 0;
});
}).bind(this));

var tableBody_ = this.piskelList.get(0).tBodies[0];
tableBody_.innerHTML = html;
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];
tableBody_.innerHTML = html;
}.bind(this));
};
})();
8 changes: 4 additions & 4 deletions src/js/controller/settings/SaveController.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
this.saveDesktopAsNewButton = document.querySelector('#save-desktop-as-new-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.saveDesktopButton, 'click', this.saveToDesktop_);
this.safeAddEventListener_(this.saveDesktopAsNewButton, 'click', this.saveToDesktopAsNew_);
Expand Down Expand Up @@ -99,7 +99,7 @@
if (pskl.app.isLoggedIn()) {
this.saveToGallery_();
} else {
this.saveToLocalStorage_();
this.saveToIndexedDb_();
}
};

Expand All @@ -111,8 +111,8 @@
this.saveTo_('saveToGallery', false);
};

ns.SaveController.prototype.saveToLocalStorage_ = function () {
this.saveTo_('saveToLocalStorage', false);
ns.SaveController.prototype.saveToIndexedDb_ = function () {
this.saveTo_('saveToIndexedDb', false);
};

ns.SaveController.prototype.saveToDesktop_ = function () {
Expand Down
171 changes: 171 additions & 0 deletions src/js/service/storage/IndexedDbStorageService.js
Original file line number Diff line number Diff line change
@@ -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);
});
};
};
})();
7 changes: 6 additions & 1 deletion src/js/service/storage/StorageService.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,15 @@
return this.delegateSave_(pskl.app.galleryStorageService, piskel);
};

// @deprecated, use saveToIndexedDb unless indexedDb is not available.
ns.StorageService.prototype.saveToLocalStorage = function (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) {
return this.delegateSave_(pskl.app.fileDownloadStorageService, piskel);
};
Expand Down Expand Up @@ -67,7 +72,7 @@
// wrap in timeout in order to start saving only after event.preventDefault
// has been done
window.setTimeout(function () {
this.saveToLocalStorage(this.piskelController.getPiskel());
this.saveToIndexedDb(this.piskelController.getPiskel());
}.bind(this), 0);
}
};
Expand Down
69 changes: 69 additions & 0 deletions src/js/service/storage/migrate/MigrateLocalStorageToIndexedDb.js
Original file line number Diff line number Diff line change
@@ -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');
};

})();
2 changes: 2 additions & 0 deletions src/piskel-script-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,10 @@
"js/widgets/Wizard.js",

// Services
"js/service/storage/migrate/MigrateLocalStorageToIndexedDb.js",
"js/service/storage/StorageService.js",
"js/service/storage/FileDownloadStorageService.js",
"js/service/storage/IndexedDbStorageService.js",
"js/service/storage/LocalStorageService.js",
"js/service/storage/GalleryStorageService.js",
"js/service/storage/DesktopStorageService.js",
Expand Down

0 comments on commit 6edd19a

Please sign in to comment.