Manage storage and fix the Chrome app

TODO: manage storage IN the Chrome app
main
Isaiah Odhner 2015-10-14 19:30:46 -04:00
parent d044c9be57
commit 4624f13c50
10 changed files with 254 additions and 38 deletions

View File

@ -3,6 +3,7 @@
* Visual
* Warning sign for "Save changes to X?" dialogue
* Error symbol for error message dialogues
* The window close button uses text; font rendering is not consistent
* The menus use text; the arrow character is converted to an icon on some mobile devices
* The progress bar (Rendering GIF) is left native
@ -16,7 +17,6 @@
* Issues
* Address `localStorage` quota limits
* If you open an image it resets the zoom but if you're on the magnification tool it doesn't update the options
* If you zoom in with the magnifier without previously changing the magnification on the toolbar,
then switch back to the magnifier, the toolbar doesn't show any magnification highlighted
@ -243,6 +243,11 @@ Prankily wait for next user input before fullscreening and bluescreening
* Basic things that people would complain about
* Save/manage application state
* On restart, reopen images from storage
* On close / Exit, ask to save, remove image from storage
### Also
* Anything marked `@TODO` or `@FIXME` in the source code

View File

@ -446,4 +446,4 @@
}
.help-window .page:before {
background-position: -32px 0;
}
}

View File

@ -17,19 +17,13 @@
<body>
<script src="lib/jquery.min.js"></script>
<script src="lib/pep.js"></script>
<script>
$.event.props.push("button", "buttons", "clientX", "clientY", "offsetX", "offsetY", "pageX", "pageY", "screenX", "screenY", "toElement");
$.event.props.push("pointerType", "pointerId", "width", "height", "pressure", "tiltX", "tiltY", "hwTimestamp", "isPrimary");
</script>
<script src="lib/canvas.toBlob.js"></script>
<script src="lib/gif.js/gif.js"></script>
<script src="lib/palette.js"></script>
<script src="lib/FileSaver.js"></script>
<script src="lib/font-detective.js"></script>
<script>
FontDetective.swf = "./lib/FontList.swf"
</script>
<script src="src/helpers.js"></script>
<script src="src/storage.js"></script>
<script src="src/$Component.js"></script>
<script src="src/$Window.js"></script>
<script src="src/$ToolBox.js"></script>
@ -46,6 +40,7 @@
<script src="src/menus.js"></script>
<script src="src/app.js"></script>
<script src="src/$menus.js"></script>
<script src="src/manage-storage.js"></script>
<script src="src/canvas-change.js"></script>
<script src="src/multiplayer.js"></script> <!-- @TODO: rename -->
</body>

View File

@ -235,8 +235,9 @@ html, body, .jspaint {
flex-flow: column;
}
.jspaint-window-content .jspaint-button-group > button {
width: 80px;
min-width: 80px;
padding: 3px 5px;
white-space: nowrap;
}
.jspaint-window-content > form {
display: flex;

View File

@ -13,6 +13,7 @@
}
},
"permissions": [
"storage",
"fullscreen",
"clipboardRead",
"clipboardWrite",

View File

@ -9,14 +9,6 @@ var default_canvas_height = 384;
var my_canvas_width = default_canvas_width;
var my_canvas_height = default_canvas_height;
try{
// @TODO support for chrome app where only chrome.storage is available
if(localStorage){
my_canvas_width = Number(localStorage.width) || default_canvas_width;
my_canvas_height = Number(localStorage.height) || default_canvas_height;
}
}catch(e){}
var palette = [
"#000000","#787878","#790300","#757A01","#007902","#007778","#0A0078","#7B0077","#767A38","#003637","#286FFE","#083178","#4C00FE","#783B00",
"#FFFFFF","#BBBBBB","#FF0E00","#FAFF08","#00FF0B","#00FEFF","#3400FE","#FF00FE","#FBFF7A","#00FF7B","#76FEFF","#8270FE","#FF0677","#FF7D36",
@ -101,9 +93,7 @@ $canvas.on("user-resized", function(e, _x, _y, width, height){
undoable(0, function(){
canvas.width = Math.max(1, width);
canvas.height = Math.max(1, height);
if(transparency){
ctx.clearRect(0, 0, canvas.width, canvas.height);
}else{
if(!transparency){
ctx.fillStyle = colors.background;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
@ -115,15 +105,31 @@ $canvas.on("user-resized", function(e, _x, _y, width, height){
$canvas.trigger("update"); // update handles
try{
localStorage.width = canvas.width;
localStorage.height = canvas.height;
}catch(e){
storage.set({
width: canvas.width,
height: canvas.height,
}, function(err){
// oh well
}
})
});
});
storage.get({
width: default_canvas_width,
height: default_canvas_height,
}, function(err, values){
if(err){return;}
my_canvas_width = values.width;
my_canvas_height = values.height;
canvas.width = Math.max(1, my_canvas_width);
canvas.height = Math.max(1, my_canvas_height);
if(!transparency){
ctx.fillStyle = colors.background;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
$canvas.trigger("update"); // update handles
});
$("body").on("dragover dragenter", function(e){
if(
e.target instanceof HTMLInputElement ||

View File

@ -1,4 +1,12 @@
// make jQuery play well with PEP
$.event.props.push("button", "buttons", "clientX", "clientY", "offsetX", "offsetY", "pageX", "pageY", "screenX", "screenY", "toElement");
$.event.props.push("pointerType", "pointerId", "width", "height", "pressure", "tiltX", "tiltY", "hwTimestamp", "isPrimary");
// configure Font Detective
FontDetective.swf = "./lib/FontList.swf";
var TAU = //////|//////
///// | /////
/// tau ///

72
src/manage-storage.js Normal file
View File

@ -0,0 +1,72 @@
var $storage_manager;
var $quota_exceeded_window;
var ignoring_quota_exceeded = false;
function storage_quota_exceeded(){
if($quota_exceeded_window){
$quota_exceeded_window.close();
$quota_exceeded_window = null;
}
if(ignoring_quota_exceeded){
return;
}
var $w = $FormWindow().title("Storage Error").addClass("jspaint-dialogue-window");
$w.$main.html(
"<p>JS Paint stores images as you work on them so that if you " +
"close your browser or tab or reload the page " +
"your images are usually safe.</p>" +
"<p>However, it has run out of space to do so.</p>" +
"<p>You can still save the current image with <b>File > Save</b>. " +
"You should save frequently, or free up enough space to keep the image safe.</p>"
);
$w.$Button("View and manage storage", function(){
$w.close();
ignoring_quota_exceeded = false;
manage_storage();
});
$w.$Button("Ignore", function(){
$w.close();
ignoring_quota_exceeded = true;
});
$w.width(500);
$w.center();
$quota_exceeded_window = $w;
}
function manage_storage(){
if($storage_manager){
$storage_manager.close();
}
$storage_manager = $Window().title("Manage Storage");
$storage_manager.$content.html(
"<p>Any images you've saved to your computer with <b>File > Save</b> will not be affected.</p>"
);
$table = $(E("table")).appendTo($storage_manager.$content);
var addRowFor = function(k, imgSrc){
$tr = $(E("tr")).appendTo($table);
$img = $(E("img")).attr({src: imgSrc});
$img.css({maxWidth: "50px", maxHeight: "50px"});
$remove = $(E("button")).addClass("jspaint-button jspaint-dialogue-button").text("Remove");
$(E("td")).text(k).appendTo($tr);
$(E("td")).append($img).appendTo($tr);
$(E("td")).append($remove).appendTo($tr);
$remove.click(function(){
localStorage.removeItem(k);
$tr.remove();
});
};
for(k in localStorage){
if(k.match(/^image#/)){
var v = localStorage[k];
addRowFor(k, v[0] === '"' ? JSON.parse(v) : v);
}
}
$storage_manager.width(500);
$storage_manager.center();
}

View File

@ -2,7 +2,7 @@
(function(){
var debug = function(text){
if(console){
if(typeof console !== "undefined"){
console.log(text);
}
};
@ -14,23 +14,37 @@
var LocalSession = function(session_id){
var lsid = "image#" + session_id;
debug("localStorage id: "+lsid);
debug("local storage id: " + lsid);
var uri = localStorage[lsid];
if(uri){
open_from_URI(uri, function(){
saved = false; // it's safe, sure, but you haven't "Saved" it
});
}
storage.get(lsid, function(err, uri){
if(err){
$w = $FormWindow().title("Error").addClass("jspaint-dialogue-window");
$w.$main.text("Error retrieving image from localStorage:");
$(E("pre")).text(err.toString()).appendTo($w.$main);
$w.$Button("OK", function(){
$w.close();
});
}else if(uri){
open_from_URI(uri, function(){
saved = false; // it's safe, sure, but you haven't "Saved" it
});
}
});
$canvas.on("change.session-hook", function(){
localStorage[lsid] = canvas.toDataURL("image/png");
storage.set(lsid, canvas.toDataURL("image/png"), function(err){
if(err){
if(err.quotaExceeded){
storage_quota_exceeded();
}else{
// e.g. localStorage is disabled
// (or there's some other error?)
}
}
});
});
};
LocalSession.prototype.end = function(){
//?
// @TODO: clean up localStorage sessions (when?)
// Remove session-related hooks
$app.find("*").off(".session-hook");
$G.off(".session-hook");

114
src/storage.js Normal file
View File

@ -0,0 +1,114 @@
// Generated by CoffeeScript 1.9.2
(function() {
var chromeStore, isArray, localStore, ref, ref1;
isArray = (ref = Array.isArray) != null ? ref : function(a) {
return ("" + a) !== a && {}.toString.call(a) === "[object Array]";
};
localStore = {
get: function(key, callback) {
var defaultValue, err, i, item, len, obj, ref1, ref2;
try {
if (typeof key === "string") {
item = localStorage.getItem(key);
if (item) {
obj = JSON.parse(item);
}
} else {
obj = {};
if (isArray(arguments[0])) {
ref1 = arguments[0];
for (i = 0, len = ref1.length; i < len; i++) {
key = ref1[i];
item = localStorage.getItem(key);
if (item) {
obj[key] = JSON.parse(item);
}
}
} else {
ref2 = arguments[0];
for (key in ref2) {
defaultValue = ref2[key];
item = localStorage.getItem(key);
if (item) {
obj[key] = JSON.parse(item);
} else {
obj[key] = defaultValue;
}
}
}
}
} catch (_error) {
err = _error;
callback(err);
return;
}
callback(null, obj);
},
set: function(key, value, callback) {
var err, obj1, to_set;
to_set = {};
if (typeof arguments[0] === "string") {
to_set = (
obj1 = {},
obj1["" + key] = value,
obj1
);
} else if (isArray(arguments[0])) {
throw new TypeError("Cannot set an array of keys (to what?)");
} else {
to_set = arguments[0], callback = arguments[1];
}
for (key in to_set) {
value = to_set[key];
try {
localStorage.setItem(key, JSON.stringify(value));
} catch (_error) {
err = _error;
err.quotaExceeded = err.code === 22 || err.name === "NS_ERROR_DOM_QUOTA_REACHED" || err.number === -2147024882;
callback(err);
return;
}
}
return callback(null);
}
};
chromeStore = {
get: function(key, callback) {
console.log('GET', key);
return chrome.storage.local.get(key, function(values) {
if (chrome.runtime.lastError) {
callback(chrome.runtime.lastError);
return;
}
console.log('GOT', key, values);
if (typeof key === "string") {
return callback(null, values[key]);
} else {
return callback(null, values);
}
});
},
set: function(key, value, callback) {
var obj1, to_set;
if (arguments.length < 3) {
to_set = arguments[0], callback = arguments[1];
} else {
to_set = (
obj1 = {},
obj1["" + key] = value,
obj1
);
}
console.log('SET', to_set);
return chrome.storage.local.set(to_set, function() {
return callback(chrome.runtime.lastError);
});
}
};
window.storage = ((ref1 = window.chrome) != null ? ref1.storage : void 0) != null ? chromeStore : localStore;
}).call(this);