Skip to content

Commit 0a066d4

Browse files
authored
Load doodles (#11)
* janky but functional saving * added a test * switch to save icon * 2 controllers * refactor into one controller * janky but functional saving * added a test * switch to save icon * 2 controllers * refactor into one controller * canvas clearing bug found * fix canvas rendering issues
1 parent 14dcb56 commit 0a066d4

File tree

14 files changed

+194
-35
lines changed

14 files changed

+194
-35
lines changed

src/annotator/components/toolbar.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ export default function Toolbar({
196196
/>
197197
<ToolbarButton
198198
label="Save"
199-
icon="erase"
199+
icon="save"
200200
onClick={() => saveDoodle()}
201201
/>
202202
</div>

src/annotator/guest.js

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ export default class Guest extends Delegator {
306306
});
307307

308308
this.subscribe('annotationsLoaded', annotations => {
309+
this.loadDoodles(annotations.filter(this.isDoodleAnnotation));
309310
annotations.map(annotation => this.anchor(annotation));
310311
});
311312
}
@@ -364,6 +365,10 @@ export default class Guest extends Delegator {
364365
crossframe.on('saveCurrentDoodle', () => {
365366
this.saveCurrentDoodle();
366367
});
368+
369+
crossframe.on('clearDoodleCanvas', () => {
370+
this.clearDoodleCanvas();
371+
});
367372
}
368373

369374
destroy() {
@@ -773,9 +778,64 @@ export default class Guest extends Delegator {
773778
if (this.doodleCanvasController) {
774779
this.createAnnotation({
775780
$doodle: true,
776-
doodleLines: this.doodleCanvasController.lines,
781+
doodleLines: this.doodleCanvasController.newLines,
777782
});
778-
this.doodleCanvasController.lines = [];
783+
// removed clearing lines from here because of bug when you try to save unsuccessfully (b/c not logged in) clearing your doodle
784+
}
785+
}
786+
787+
/**
788+
* Clear the doodle canvas
789+
*/
790+
clearDoodleCanvas() {
791+
if (this.doodleCanvasController) {
792+
this.doodleCanvasController.saveLines();
793+
}
794+
}
795+
796+
/**
797+
*
798+
* @param {*} annotation
799+
* @returns true if the annotation is a doodleAnnotation
800+
*/
801+
802+
isDoodleAnnotation(annotation) {
803+
// If any of the targets have a DoodleSelector, this is a doodle annotation. Otherwise, it is not.
804+
if (annotation.target) {
805+
for (let targ of annotation.target) {
806+
// not all targets have selectors at this point
807+
if (targ.selector) {
808+
for (let selector of targ.selector) {
809+
if (selector.type === 'DoodleSelector') {
810+
return true;
811+
}
812+
}
813+
}
814+
}
815+
}
816+
return false;
817+
}
818+
819+
loadDoodles(doodleAnnotations) {
820+
// First, make sure there are doodleAnnotations and a Controller
821+
if (!doodleAnnotations.length || !this.doodleCanvasController) {
822+
return;
823+
}
824+
825+
// Then, load the lines into our doodleCanvasController.
826+
let newLines = [];
827+
for (let doodle of doodleAnnotations) {
828+
for (let targ of doodle.target) {
829+
for (let sel of targ.selector) {
830+
if (sel.type === 'DoodleSelector') {
831+
newLines = [...newLines, sel.line];
832+
}
833+
}
834+
}
779835
}
836+
this.doodleCanvasController.savedLines = [
837+
...this.doodleCanvasController.savedLines,
838+
...newLines,
839+
];
780840
}
781841
}

src/annotator/icons.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ export default {
1919
close: require('../images/icons/close.svg'),
2020
pen: require('../images/icons/pen.svg'),
2121
erase: require('../images/icons/erase.svg'),
22+
save: require('../images/icons/save.svg'),
2223
};

src/annotator/sidebar.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ export default class Sidebar extends Guest {
141141
});
142142
this.toolbar.useMinimalControls = config.theme === 'clean';
143143

144+
// Set up the canvases for displaying and creating doodles
144145
this.doodleCanvasController = new DoodleController(
145146
document.getElementById('main-content'),
146147
{

src/annotator/test/guest-test.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ describe('Guest', () => {
184184

185185
beforeEach(() => {
186186
guest = createGuest();
187+
guest.loadDoodles = sinon.stub();
187188
options = CrossFrame.lastCall.args[1];
188189
});
189190

@@ -205,6 +206,36 @@ describe('Guest', () => {
205206
assert.calledWith(guest.anchor, ann2);
206207
});
207208

209+
it('calls loadDoodles with only doodle annotations on "annotationsLoaded"', () => {
210+
const ann1 = {
211+
id: 1,
212+
$tag: 'tag1',
213+
target: [
214+
{
215+
selector: [
216+
{
217+
type: 'DoodleSelector',
218+
line: {
219+
points: [
220+
[1, 1],
221+
[2, 2],
222+
],
223+
size: 5,
224+
color: '#FF0000',
225+
tool: 'pen',
226+
},
227+
},
228+
],
229+
},
230+
],
231+
};
232+
const ann2 = { id: 2, $tag: 'tag2' };
233+
sandbox.stub(guest, 'anchor');
234+
options.emit('annotationsLoaded', [ann1, ann2]);
235+
assert.calledOnce(guest.loadDoodles);
236+
assert.calledWith(guest.loadDoodles, [ann1]);
237+
});
238+
208239
it('proxies all other events into the annotator event system', () => {
209240
const fooHandler = sandbox.stub();
210241
const barHandler = sandbox.stub();

src/doodle/canvas.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const Canvas = ({
4242
useEffect(() => {
4343
const canvas = canvasRef.current;
4444
const ctx = canvas.getContext('2d');
45+
ctx.clearRect(0, 0, canvas.width, canvas.height);
4546

4647
// Draw all of the lines (reverse order so that erasing works)
4748
for (let i = lines.length - 1; i >= 0; i--) {

src/doodle/displayCanvas.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { createElement } from 'preact';
2+
import { Canvas } from './canvas';
3+
import propTypes from 'prop-types';
4+
5+
const DisplayCanvas = ({ container, lines }) => {
6+
const boundingRect = container.getBoundingClientRect();
7+
return (
8+
<div
9+
style={{
10+
position: 'absolute',
11+
top: boundingRect.top + window.scrollY,
12+
left: boundingRect.left + window.scrollX,
13+
zIndex: 9998,
14+
pointerEvents: 'none',
15+
}}
16+
>
17+
<Canvas
18+
width={boundingRect.width}
19+
height={
20+
Math.min(
21+
boundingRect.height,
22+
10000
23+
) /*Canvas starts to lag over 10k, doesnt work over 32k*/
24+
}
25+
handleMouseDown={() => {}}
26+
handleMouseUp={() => {}}
27+
handleMouseLeave={() => {}}
28+
handleMouseMove={() => {}}
29+
lines={lines}
30+
/>
31+
</div>
32+
);
33+
};
34+
35+
DisplayCanvas.propTypes = {
36+
lines: propTypes.array.isRequired,
37+
container: propTypes.object.isRequired,
38+
};
39+
40+
export { DisplayCanvas };

src/doodle/doodleCanvas.js

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,6 @@ const DoodleCanvas = ({
3131
setLines,
3232
}) => {
3333
const [isDrawing, setIsDrawing] = useState(false);
34-
const [everActive, setEverActive] = useState(false);
35-
36-
if (active && !everActive) {
37-
setEverActive(true);
38-
}
3934

4035
useEffect(() => {
4136
if (lines.length === 0) {
@@ -96,10 +91,6 @@ const DoodleCanvas = ({
9691
setLines([newLine, ...rest]);
9792
};
9893

99-
if (!everActive) {
100-
return null;
101-
}
102-
10394
return (
10495
<div
10596
style={{

src/doodle/doodleController.js

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { render, createElement } from 'preact';
1+
import { render, createElement, Fragment } from 'preact';
2+
import { DisplayCanvas } from './displayCanvas';
23
import { DoodleCanvas } from './doodleCanvas';
34

45
export class DoodleController {
@@ -9,13 +10,18 @@ export class DoodleController {
910
constructor(container, options) {
1011
const { tool, size } = options;
1112
this._lines = [];
13+
this._savedLines = [];
14+
this._newLines = [];
1215

1316
this._container = container === null ? document.body : container;
1417
this._tool = tool;
1518
this._size = size;
1619

1720
this._doodleable = false;
1821

22+
// create a new element to render into, to avoid overwriting the main page content.
23+
this.target = document.body.appendChild(document.createElement('div'));
24+
1925
this.render();
2026
}
2127

@@ -34,20 +40,24 @@ export class DoodleController {
3440
/**
3541
* Update the lines and re-render on change
3642
*/
37-
set lines(newLines) {
38-
this._lines = newLines;
43+
set savedLines(lines) {
44+
this._savedLines = lines;
3945
this.render();
4046
}
4147

42-
get lines() {
43-
return this._lines;
48+
get savedLines() {
49+
return this._savedLines;
50+
}
51+
52+
set newLines(lines) {
53+
this._newLines = lines;
54+
this.render();
55+
}
56+
57+
get newLines() {
58+
return this._newLines;
4459
}
4560

46-
/**
47-
* Update the toolbar to reflect whether the "Create annotation" button will
48-
* create a page note (if there is no selection) or an annotation (if there is
49-
* a selection).
50-
*/
5161
set size(newSize) {
5262
this._size = newSize;
5363
this.render();
@@ -70,20 +80,29 @@ export class DoodleController {
7080
return this._doodleable;
7181
}
7282

83+
saveLines() {
84+
this._savedLines = [...this._newLines, ...this._savedLines];
85+
this._newLines = [];
86+
this.render();
87+
}
88+
7389
render() {
74-
const setLines = newLines => {
75-
this.lines = newLines;
90+
const setLines = lines => {
91+
this.newLines = lines;
7692
};
7793
render(
78-
<DoodleCanvas
79-
attachedElement={this._container}
80-
size={this._size}
81-
tool={this._tool}
82-
active={this._doodleable}
83-
lines={this._lines}
84-
setLines={setLines}
85-
/>,
86-
document.body
94+
<Fragment>
95+
<DoodleCanvas
96+
attachedElement={this._container}
97+
size={this._size}
98+
tool={this._tool}
99+
active={this._doodleable}
100+
lines={this.newLines}
101+
setLines={setLines}
102+
/>
103+
<DisplayCanvas lines={this.savedLines} container={this._container} />
104+
</Fragment>,
105+
this.target
87106
);
88107
}
89108
}

src/images/icons/save.svg

Lines changed: 1 addition & 0 deletions
Loading

0 commit comments

Comments
 (0)