Fix : add backup service and make undo safer

This commit is contained in:
jdescottes 2014-06-27 02:08:00 +02:00
parent 23fd3c464c
commit bd7ebc5f7d
13 changed files with 196 additions and 43 deletions

View File

@ -121,6 +121,7 @@
}
.settings-title {
color : gold;
margin-top: 20px;
margin-bottom: 10px;
text-transform: uppercase;

View File

@ -99,6 +99,12 @@
this.savedStatusService = new pskl.service.SavedStatusService(this.piskelController);
this.savedStatusService.init();
this.backupService = new pskl.service.BackupService(this.piskelController);
this.backupService.init();
this.beforeUnloadService = new pskl.service.BeforeUnloadService(this.piskelController);
this.beforeUnloadService.init();
if (this.isAppEngineVersion) {
this.storageService = new pskl.service.AppEngineStorageService(this.piskelController);

View File

@ -8,9 +8,13 @@
*/
ns.LocalStorageController.prototype.init = function() {
this.localStorageItemTemplate_ = pskl.utils.Template.get("local-storage-item-template");
this.previousSessionTemplate_ = pskl.utils.Template.get("previous-session-info-template");
this.service_ = pskl.app.localStorageService;
this.piskelsList = $('.local-piskels-list');
this.prevSessionContainer = $('.previous-session');
this.fillRestoreSession_();
this.fillLocalPiskelsList_();
this.piskelsList.click(this.onPiskelsListClick_.bind(this));
@ -27,24 +31,58 @@
} else if (action === 'delete') {
if (window.confirm('This will permanently DELETE this piskel from your computer. Continue ?')) {
this.service_.remove(name);
$.publish(Events.CLOSE_SETTINGS_DRAWER);
this.fillLocalPiskelsList_();
}
}
};
ns.LocalStorageController.prototype.fillRestoreSession_ = function () {
var previousInfo = pskl.app.backupService.getPreviousPiskelInfo();
if (previousInfo) {
var info = {
name : previousInfo.name,
date : this.formatDate_(previousInfo.date, "{{H}}:{{m}} - {{Y}}/{{M}}/{{D}}")
};
this.prevSessionContainer.html(pskl.utils.Template.replace(this.previousSessionTemplate_, info));
$(".restore-session-button").click(this.onRestorePreviousSessionClick_.bind(this));
} else {
this.prevSessionContainer.html("No piskel backup was found on this browser.");
}
};
ns.LocalStorageController.prototype.onRestorePreviousSessionClick_ = function () {
if (window.confirm('This will erase your current workspace. Continue ?')) {
pskl.app.backupService.load();
$.publish(Events.CLOSE_SETTINGS_DRAWER);
}
};
var pad = function (num) {
if (num < 10) {
return "0" + num;
} else {
return "" + num;
}
};
ns.LocalStorageController.prototype.formatDate_ = function (date, format) {
date = new Date(date);
var formattedDate = pskl.utils.Template.replace(format, {
Y : date.getFullYear(),
M : pad(date.getMonth() + 1),
D : pad(date.getDate()),
H : pad(date.getHours()),
m : pad(date.getMinutes())
});
return formattedDate;
};
ns.LocalStorageController.prototype.fillLocalPiskelsList_ = function () {
var html = "";
var keys = this.service_.getKeys();
var pad = function (num) {
if (num < 10) {
return "0" + num;
} else {
return "" + num;
}
};
keys.sort(function (k1, k2) {
if (k1.date < k2.date) {return 1;}
if (k1.date > k2.date) {return -1;}
@ -52,15 +90,8 @@
});
keys.forEach((function (key) {
var date = new Date(key.date);
var formattedDate = pskl.utils.Template.replace("{{Y}}/{{M}}/{{D}} {{H}}:{{m}}", {
Y : date.getFullYear(),
M : pad(date.getMonth() + 1),
D : pad(date.getDate()),
H : pad(date.getHours()),
m : pad(date.getMinutes())
});
html += pskl.utils.Template.replace(this.localStorageItemTemplate_, {name : key.name, date : formattedDate});
var date = this.formatDate_(key.date, "{{Y}}/{{M}}/{{D}} {{H}}:{{m}}");
html += pskl.utils.Template.replace(this.localStorageItemTemplate_, {name : key.name, date : date});
}).bind(this));
var tableBody_ = this.piskelsList.get(0).tBodies[0];

View File

@ -46,12 +46,12 @@
} else {
color = window.tinycolor.lighten(pixelColor, step);
}
if (color) {
usedPixels[key] = true;
this.superclass.applyToolAt.call(this, col, row, color.toRgbString(), frame, overlay, event);
}
}
if (color) {
usedPixels[key] = true;
this.superclass.applyToolAt.call(this, col, row, color.toRgbString(), frame, overlay, event);
}
};
ns.Lighten.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) {

View File

@ -96,4 +96,10 @@
ns.Layer.prototype.length = function () {
return this.frames.length;
};
ns.Layer.prototype.getHash = function () {
return this.frames.map(function (frame) {
return frame.getHash();
}).join('-');
};
})();

View File

@ -109,4 +109,10 @@
var appEngineEditorHeader = $('.piskel-name').html(this.descriptor.name);
};
ns.Piskel.prototype.getHash = function () {
return this.layers.map(function (layer) {
return layer.getHash();
}).join('-');
};
})();

View File

@ -0,0 +1,59 @@
(function () {
var ns = $.namespace('pskl.service');
var BACKUP_INTERVAL = 1000 * 30;
ns.BackupService = function (piskelController) {
this.piskelController = piskelController;
this.lastHash = null;
};
ns.BackupService.prototype.init = function () {
var previousPiskel = window.localStorage.getItem('bkp.next.piskel');
var previousInfo = window.localStorage.getItem('bkp.next.info');
if (previousPiskel && previousInfo) {
window.localStorage.setItem('bkp.prev.piskel', previousPiskel);
window.localStorage.setItem('bkp.prev.info', previousInfo);
}
window.setInterval(this.backup.bind(this), BACKUP_INTERVAL);
};
ns.BackupService.prototype.backup = function () {
var piskel = this.piskelController.getPiskel();
var descriptor = piskel.getDescriptor();
var hash = piskel.getHash();
var info = {
name : descriptor.name,
description : descriptor.info,
date : Date.now(),
hash : hash
};
// Do not save an unchanged piskel
if (hash !== this.lastHash) {
this.lastHash = hash;
window.localStorage.setItem('bkp.next.piskel', this.piskelController.serialize());
window.localStorage.setItem('bkp.next.info', JSON.stringify(info));
}
};
ns.BackupService.prototype.getPreviousPiskelInfo = function () {
var previousInfo = window.localStorage.getItem('bkp.prev.info');
if (previousInfo) {
return JSON.parse(previousInfo);
}
};
ns.BackupService.prototype.load = function() {
var previousPiskel = window.localStorage.getItem('bkp.prev.piskel');
var previousInfo = window.localStorage.getItem('bkp.prev.info');
previousPiskel = JSON.parse(previousPiskel);
previousInfo = JSON.parse(previousInfo);
pskl.utils.serialization.Deserializer.deserialize(previousPiskel, function (piskel) {
piskel.setDescriptor(new pskl.model.piskel.Descriptor(previousInfo.name, previousInfo.description, true));
pskl.app.piskelController.setPiskel(piskel);
});
};
})();

View File

@ -0,0 +1,23 @@
(function () {
var ns = $.namespace('pskl.service');
ns.BeforeUnloadService = function (piskelController) {
this.piskelController = piskelController;
};
ns.BeforeUnloadService.prototype.init = function () {
window.addEventListener("beforeunload", this.onBeforeUnload.bind(this));
};
ns.BeforeUnloadService.prototype.onBeforeUnload = function (evt) {
pskl.app.backupService.backup();
if (pskl.app.savedStatusService.isDirty()) {
var confirmationMessage = "Your Piskel seems to have unsaved changes";
(evt || window.event).returnValue = confirmationMessage;
return confirmationMessage;
}
};
})();

View File

@ -63,24 +63,39 @@
};
ns.HistoryService.prototype.getPreviousSnapshotIndex_ = function (index) {
var counter = 0;
while (this.stateQueue[index] && !this.stateQueue[index].piskel) {
index = index - 1;
if(++counter > 2*SNAPSHOT_PERIOD) {
break;
}
}
return index;
};
ns.HistoryService.prototype.loadState = function (index) {
if (this.isLoadStateAllowed_(index)) {
this.lastLoadState = Date.now();
try {
if (this.isLoadStateAllowed_(index)) {
this.lastLoadState = Date.now();
var snapshotIndex = this.getPreviousSnapshotIndex_(index);
if (snapshotIndex < 0) {
throw 'Could not find previous SNAPSHOT saved in history stateQueue';
var snapshotIndex = this.getPreviousSnapshotIndex_(index);
if (snapshotIndex < 0) {
throw 'Could not find previous SNAPSHOT saved in history stateQueue';
}
var serializedPiskel = this.getSnapshotFromState_(snapshotIndex);
var onPiskelLoadedCb = this.onPiskelLoaded_.bind(this, index, snapshotIndex);
pskl.utils.serialization.Deserializer.deserialize(serializedPiskel, onPiskelLoadedCb);
}
} catch (e) {
window.console.error("[CRITICAL ERROR] : Unable to load a history state.");
window.console.error("Can you open an issue on http://github.com/juliandescottes/piskel or contact @piskelapp on twitter ? Thanks !");
window.console.error("Thanks !");
if (typeof e === "string") {
window.console.error(e);
} else {
window.console.error(e.message);
window.console.error(e.stack);
}
var serializedPiskel = this.getSnapshotFromState_(snapshotIndex);
var onPiskelLoadedCb = this.onPiskelLoaded_.bind(this, index, snapshotIndex);
pskl.utils.serialization.Deserializer.deserialize(serializedPiskel, onPiskelLoadedCb);
}
};

View File

@ -11,8 +11,6 @@
ns.LocalStorageService.prototype.init = function() {};
// localStorage.setItem('piskel_bkp', pskl.app.piskelController.serialize())
ns.LocalStorageService.prototype.save = function(name, description, piskel) {
this.removeFromKeys_(name);
this.addToKeys_(name, description, Date.now());

View File

@ -10,8 +10,6 @@
$.subscribe(Events.PISKEL_RESET, this.onPiskelReset.bind(this));
$.subscribe(Events.PISKEL_SAVED, this.onPiskelSaved.bind(this));
window.addEventListener("beforeunload", this.onBeforeUnload.bind(this));
};
ns.SavedStatusService.prototype.onPiskelReset = function () {
@ -52,13 +50,8 @@
}
};
ns.SavedStatusService.prototype.onBeforeUnload = function (evt) {
ns.SavedStatusService.prototype.isDirty = function (evt) {
var piskel = this.piskelController.getPiskel();
if (piskel.isDirty_) {
var confirmationMessage = "Your Piskel seems to have unsaved changes";
(evt || window.event).returnValue = confirmationMessage;
return confirmationMessage;
}
return piskel.isDirty_;
};
})();

View File

@ -104,6 +104,8 @@
"js/service/LocalStorageService.js",
"js/service/GithubStorageService.js",
"js/service/AppEngineStorageService.js",
"js/service/BackupService.js",
"js/service/BeforeUnloadService.js",
"js/service/HistoryService.js",
"js/service/SavedStatusService.js",
"js/service/keyboard/ShortcutService.js",

View File

@ -1,4 +1,9 @@
<div class="settings-section">
<div class="settings-title">
Restore previous session
</div>
<div class="settings-item previous-session">
</div>
<div class="settings-title">
Browse Local Piskels
</div>
@ -15,4 +20,12 @@
<td><a class="local-piskel-delete-link" data-action="delete" data-name="{{name}}" href="javascript:void(0);">x</a></td>
</tr>
</script>
<script type="text/template" id="previous-session-info-template">
<div>
Restore a backup of <span style="color:gold">{{name}}</span>, saved at <span style="color:white">{{date}}</span> ?
<div style="margin-top:10px;">
<button type="button" class="button button-primary restore-session-button">Restore</button>
</div>
</div>
</script>
</div>