mirror of
https://github.com/piskelapp/piskel.git
synced 2023-08-10 21:12:52 +03:00
Fix : add backup service and make undo safer
This commit is contained in:
parent
23fd3c464c
commit
bd7ebc5f7d
|
@ -121,6 +121,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-title {
|
.settings-title {
|
||||||
|
color : gold;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
|
|
@ -99,6 +99,12 @@
|
||||||
this.savedStatusService = new pskl.service.SavedStatusService(this.piskelController);
|
this.savedStatusService = new pskl.service.SavedStatusService(this.piskelController);
|
||||||
this.savedStatusService.init();
|
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) {
|
if (this.isAppEngineVersion) {
|
||||||
this.storageService = new pskl.service.AppEngineStorageService(this.piskelController);
|
this.storageService = new pskl.service.AppEngineStorageService(this.piskelController);
|
||||||
|
|
|
@ -8,9 +8,13 @@
|
||||||
*/
|
*/
|
||||||
ns.LocalStorageController.prototype.init = function() {
|
ns.LocalStorageController.prototype.init = function() {
|
||||||
this.localStorageItemTemplate_ = pskl.utils.Template.get("local-storage-item-template");
|
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.service_ = pskl.app.localStorageService;
|
||||||
this.piskelsList = $('.local-piskels-list');
|
this.piskelsList = $('.local-piskels-list');
|
||||||
|
this.prevSessionContainer = $('.previous-session');
|
||||||
|
|
||||||
|
this.fillRestoreSession_();
|
||||||
this.fillLocalPiskelsList_();
|
this.fillLocalPiskelsList_();
|
||||||
|
|
||||||
this.piskelsList.click(this.onPiskelsListClick_.bind(this));
|
this.piskelsList.click(this.onPiskelsListClick_.bind(this));
|
||||||
|
@ -27,14 +31,32 @@
|
||||||
} else if (action === 'delete') {
|
} else if (action === 'delete') {
|
||||||
if (window.confirm('This will permanently DELETE this piskel from your computer. Continue ?')) {
|
if (window.confirm('This will permanently DELETE this piskel from your computer. Continue ?')) {
|
||||||
this.service_.remove(name);
|
this.service_.remove(name);
|
||||||
$.publish(Events.CLOSE_SETTINGS_DRAWER);
|
this.fillLocalPiskelsList_();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ns.LocalStorageController.prototype.fillLocalPiskelsList_ = function () {
|
ns.LocalStorageController.prototype.fillRestoreSession_ = function () {
|
||||||
var html = "";
|
var previousInfo = pskl.app.backupService.getPreviousPiskelInfo();
|
||||||
var keys = this.service_.getKeys();
|
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) {
|
var pad = function (num) {
|
||||||
if (num < 10) {
|
if (num < 10) {
|
||||||
|
@ -44,6 +66,22 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
keys.sort(function (k1, k2) {
|
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) {
|
keys.forEach((function (key) {
|
||||||
var date = new Date(key.date);
|
var date = this.formatDate_(key.date, "{{Y}}/{{M}}/{{D}} {{H}}:{{m}}");
|
||||||
var formattedDate = pskl.utils.Template.replace("{{Y}}/{{M}}/{{D}} {{H}}:{{m}}", {
|
html += pskl.utils.Template.replace(this.localStorageItemTemplate_, {name : key.name, date : date});
|
||||||
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});
|
|
||||||
}).bind(this));
|
}).bind(this));
|
||||||
|
|
||||||
var tableBody_ = this.piskelsList.get(0).tBodies[0];
|
var tableBody_ = this.piskelsList.get(0).tBodies[0];
|
||||||
|
|
|
@ -46,12 +46,12 @@
|
||||||
} else {
|
} else {
|
||||||
color = window.tinycolor.lighten(pixelColor, step);
|
color = window.tinycolor.lighten(pixelColor, step);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (color) {
|
if (color) {
|
||||||
usedPixels[key] = true;
|
usedPixels[key] = true;
|
||||||
this.superclass.applyToolAt.call(this, col, row, color.toRgbString(), frame, overlay, event);
|
this.superclass.applyToolAt.call(this, col, row, color.toRgbString(), frame, overlay, event);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ns.Lighten.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) {
|
ns.Lighten.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) {
|
||||||
|
|
|
@ -96,4 +96,10 @@
|
||||||
ns.Layer.prototype.length = function () {
|
ns.Layer.prototype.length = function () {
|
||||||
return this.frames.length;
|
return this.frames.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ns.Layer.prototype.getHash = function () {
|
||||||
|
return this.frames.map(function (frame) {
|
||||||
|
return frame.getHash();
|
||||||
|
}).join('-');
|
||||||
|
};
|
||||||
})();
|
})();
|
|
@ -109,4 +109,10 @@
|
||||||
var appEngineEditorHeader = $('.piskel-name').html(this.descriptor.name);
|
var appEngineEditorHeader = $('.piskel-name').html(this.descriptor.name);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ns.Piskel.prototype.getHash = function () {
|
||||||
|
return this.layers.map(function (layer) {
|
||||||
|
return layer.getHash();
|
||||||
|
}).join('-');
|
||||||
|
};
|
||||||
|
|
||||||
})();
|
})();
|
59
src/js/service/BackupService.js
Normal file
59
src/js/service/BackupService.js
Normal 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);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
})();
|
23
src/js/service/BeforeUnloadService.js
Normal file
23
src/js/service/BeforeUnloadService.js
Normal 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
})();
|
|
@ -63,13 +63,18 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
ns.HistoryService.prototype.getPreviousSnapshotIndex_ = function (index) {
|
ns.HistoryService.prototype.getPreviousSnapshotIndex_ = function (index) {
|
||||||
|
var counter = 0;
|
||||||
while (this.stateQueue[index] && !this.stateQueue[index].piskel) {
|
while (this.stateQueue[index] && !this.stateQueue[index].piskel) {
|
||||||
index = index - 1;
|
index = index - 1;
|
||||||
|
if(++counter > 2*SNAPSHOT_PERIOD) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return index;
|
return index;
|
||||||
};
|
};
|
||||||
|
|
||||||
ns.HistoryService.prototype.loadState = function (index) {
|
ns.HistoryService.prototype.loadState = function (index) {
|
||||||
|
try {
|
||||||
if (this.isLoadStateAllowed_(index)) {
|
if (this.isLoadStateAllowed_(index)) {
|
||||||
this.lastLoadState = Date.now();
|
this.lastLoadState = Date.now();
|
||||||
|
|
||||||
|
@ -77,11 +82,21 @@
|
||||||
if (snapshotIndex < 0) {
|
if (snapshotIndex < 0) {
|
||||||
throw 'Could not find previous SNAPSHOT saved in history stateQueue';
|
throw 'Could not find previous SNAPSHOT saved in history stateQueue';
|
||||||
}
|
}
|
||||||
|
|
||||||
var serializedPiskel = this.getSnapshotFromState_(snapshotIndex);
|
var serializedPiskel = this.getSnapshotFromState_(snapshotIndex);
|
||||||
var onPiskelLoadedCb = this.onPiskelLoaded_.bind(this, index, snapshotIndex);
|
var onPiskelLoadedCb = this.onPiskelLoaded_.bind(this, index, snapshotIndex);
|
||||||
pskl.utils.serialization.Deserializer.deserialize(serializedPiskel, onPiskelLoadedCb);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ns.HistoryService.prototype.getSnapshotFromState_ = function (stateIndex) {
|
ns.HistoryService.prototype.getSnapshotFromState_ = function (stateIndex) {
|
||||||
|
|
|
@ -11,8 +11,6 @@
|
||||||
|
|
||||||
ns.LocalStorageService.prototype.init = function() {};
|
ns.LocalStorageService.prototype.init = function() {};
|
||||||
|
|
||||||
// localStorage.setItem('piskel_bkp', pskl.app.piskelController.serialize())
|
|
||||||
|
|
||||||
ns.LocalStorageService.prototype.save = function(name, description, piskel) {
|
ns.LocalStorageService.prototype.save = function(name, description, piskel) {
|
||||||
this.removeFromKeys_(name);
|
this.removeFromKeys_(name);
|
||||||
this.addToKeys_(name, description, Date.now());
|
this.addToKeys_(name, description, Date.now());
|
||||||
|
|
|
@ -10,8 +10,6 @@
|
||||||
$.subscribe(Events.PISKEL_RESET, this.onPiskelReset.bind(this));
|
$.subscribe(Events.PISKEL_RESET, this.onPiskelReset.bind(this));
|
||||||
|
|
||||||
$.subscribe(Events.PISKEL_SAVED, this.onPiskelSaved.bind(this));
|
$.subscribe(Events.PISKEL_SAVED, this.onPiskelSaved.bind(this));
|
||||||
|
|
||||||
window.addEventListener("beforeunload", this.onBeforeUnload.bind(this));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ns.SavedStatusService.prototype.onPiskelReset = function () {
|
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();
|
var piskel = this.piskelController.getPiskel();
|
||||||
if (piskel.isDirty_) {
|
return piskel.isDirty_;
|
||||||
var confirmationMessage = "Your Piskel seems to have unsaved changes";
|
|
||||||
|
|
||||||
(evt || window.event).returnValue = confirmationMessage;
|
|
||||||
return confirmationMessage;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
})();
|
})();
|
|
@ -104,6 +104,8 @@
|
||||||
"js/service/LocalStorageService.js",
|
"js/service/LocalStorageService.js",
|
||||||
"js/service/GithubStorageService.js",
|
"js/service/GithubStorageService.js",
|
||||||
"js/service/AppEngineStorageService.js",
|
"js/service/AppEngineStorageService.js",
|
||||||
|
"js/service/BackupService.js",
|
||||||
|
"js/service/BeforeUnloadService.js",
|
||||||
"js/service/HistoryService.js",
|
"js/service/HistoryService.js",
|
||||||
"js/service/SavedStatusService.js",
|
"js/service/SavedStatusService.js",
|
||||||
"js/service/keyboard/ShortcutService.js",
|
"js/service/keyboard/ShortcutService.js",
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
<div class="settings-section">
|
<div class="settings-section">
|
||||||
|
<div class="settings-title">
|
||||||
|
Restore previous session
|
||||||
|
</div>
|
||||||
|
<div class="settings-item previous-session">
|
||||||
|
</div>
|
||||||
<div class="settings-title">
|
<div class="settings-title">
|
||||||
Browse Local Piskels
|
Browse Local Piskels
|
||||||
</div>
|
</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>
|
<td><a class="local-piskel-delete-link" data-action="delete" data-name="{{name}}" href="javascript:void(0);">x</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</script>
|
</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>
|
</div>
|
Loading…
Reference in New Issue
Block a user