Skip to content

Commit 9ee6c9d

Browse files
committed
Merge pull request #41 from toolness/autosave
Implement autosave w/ sessionStorage.
2 parents b137509 + 58b3ed6 commit 9ee6c9d

File tree

5 files changed

+65
-12
lines changed

5 files changed

+65
-12
lines changed

lib/app.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import PureComponent from "./pure-component";
44
import Toolbar from "./toolbar";
55
import Editor from "./editor";
66
import Preview from "./preview";
7+
import { Autosaver } from "./autosaver";
78

89
interface ErrorMessage {
910
message: string,
@@ -14,6 +15,7 @@ interface AppProps {
1415
initialContent: string,
1516
previewWidth: number,
1617
p5version: string,
18+
autosaver?: Autosaver,
1719
autoplay?: boolean
1820
}
1921

@@ -51,7 +53,11 @@ export default class App extends PureComponent<AppProps, AppState> {
5153
}
5254

5355
componentDidMount() {
54-
if (this.props.autoplay) {
56+
let autosave = this.props.autosaver && this.props.autosaver.restore();
57+
58+
if (autosave && autosave !== this.state.editorContent) {
59+
this.setState({ editorContent: autosave });
60+
} else if (this.props.autoplay) {
5561
this.handlePlayClick();
5662
}
5763
}
@@ -63,6 +69,7 @@ export default class App extends PureComponent<AppProps, AppState> {
6369
canUndo: canUndo,
6470
canRedo: canRedo
6571
});
72+
this.props.autosaver.save(newValue);
6673
}
6774

6875
handlePreviewError = (message: string, line?: number) => {

lib/autosaver.ts

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
export interface Autosaver {
2+
restore(): string,
3+
save(value: string): void
4+
}
5+
6+
export class SessionStorageAutosaver implements Autosaver {
7+
id: string
8+
9+
constructor(id: string) {
10+
this.id = id;
11+
}
12+
13+
save(value: string) {
14+
try {
15+
window.sessionStorage[this.id] = value;
16+
} catch (e) {
17+
// It's likely that we ran out of storage space or are in
18+
// private browsing mode or something. Regardless, autosave is
19+
// a parachute, so it's regrettable but ultimately not
20+
// catastrophic for us to fail here.
21+
22+
console.log("Autosave for " + this.id + " failed", e);
23+
}
24+
}
25+
26+
restore(): string {
27+
return window.sessionStorage[this.id];
28+
}
29+
}

lib/main.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import ReactDOM = require("react-dom");
44
import url = require("url");
55

66
import * as defaults from "./defaults";
7+
import { SessionStorageAutosaver } from "./autosaver";
78
import App from "./app";
89

910
let defaultSketchJS = require("raw!./default-sketch.js") as string;
@@ -12,6 +13,7 @@ require("../css/style.css");
1213

1314
function start() {
1415
let qs = url.parse(window.location.search, true).query;
16+
let id = document.referrer + '_' + qs['id'];
1517
let autoplay = (qs['autoplay'] === 'on');
1618
let initialContent = qs['sketch'] || defaultSketchJS;
1719
let p5version = qs['p5version'] || defaults.P5_VERSION;
@@ -25,6 +27,7 @@ function start() {
2527

2628
ReactDOM.render(
2729
<App initialContent={initialContent}
30+
autosaver={new SessionStorageAutosaver(id)}
2831
p5version={p5version}
2932
previewWidth={previewWidth}
3033
autoplay={autoplay} />,

lib/p5-widget.ts

+24-11
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const IFRAME_STYLE = [
1111
let myScriptEl = getMyScriptEl() as HTMLScriptElement;
1212
let myBaseURL = getMyBaseURL(myScriptEl.src);
1313
let autoload = !myScriptEl.hasAttribute('data-manual');
14+
let nextId = 1;
1415

1516
function getMyBaseURL(url: string) {
1617
return url.slice(0, -MY_FILENAME.length);
@@ -52,7 +53,10 @@ function replaceScriptWithWidget(el: HTMLScriptElement) {
5253
let p5version = el.getAttribute('data-p5-version');
5354
let autoplay = el.hasAttribute('data-autoplay');
5455
let url;
55-
let qsArgs = ['sketch=' + encodeURIComponent(el.textContent)];
56+
let qsArgs = [
57+
'sketch=' + encodeURIComponent(el.textContent),
58+
'id=' + encodeURIComponent(el.getAttribute('data-id'))
59+
];
5660
let style = IFRAME_STYLE.slice();
5761

5862
if (!isNaN(previewWidth) && previewWidth >= 0) {
@@ -100,19 +104,28 @@ function whenVisible(el: HTMLScriptElement,
100104
maybeMakeVisible();
101105
}
102106

107+
function lazilyReplaceScriptWithWidget(el: HTMLScriptElement) {
108+
let height = getDataHeight(el);
109+
110+
el.style.display = 'block';
111+
el.style.fontSize = '0';
112+
el.style.width = '100%';
113+
el.style.minHeight = height + 'px';
114+
el.style.background = '#f0f0f0';
115+
116+
if (!el.hasAttribute('data-id')) {
117+
el.setAttribute('data-id', nextId.toString());
118+
nextId++;
119+
}
120+
121+
whenVisible(el, replaceScriptWithWidget);
122+
}
123+
103124
function lazilyReplaceAllScriptsWithWidget() {
104125
let scripts = document.querySelectorAll("script[type='text/p5']");
105126

106127
[].slice.call(scripts).forEach((el: HTMLScriptElement) => {
107-
let height = getDataHeight(el);
108-
109-
el.style.display = 'block';
110-
el.style.fontSize = '0';
111-
el.style.width = '100%';
112-
el.style.minHeight = height + 'px';
113-
el.style.background = '#f0f0f0';
114-
115-
whenVisible(el, replaceScriptWithWidget);
128+
lazilyReplaceScriptWithWidget(el);
116129
});
117130
}
118131

@@ -131,7 +144,7 @@ if (autoload) {
131144
window['p5Widget'] = {
132145
baseURL: myBaseURL,
133146
url: myBaseURL + MY_FILENAME,
134-
replaceScript: replaceScriptWithWidget,
147+
replaceScript: lazilyReplaceScriptWithWidget,
135148
replaceAll: lazilyReplaceAllScriptsWithWidget,
136149
defaults: defaults
137150
};

tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"test/test-implicit-sketch.tsx",
1818
"lib/pure-component.tsx",
1919
"lib/defaults.ts",
20+
"lib/autosaver.ts",
2021
"lib/p5-widget.ts",
2122
"lib/preview-frame.ts",
2223
"lib/falafel.ts",

0 commit comments

Comments
 (0)