client/posts: add copying notes to clipboard

Saves some frustration when losing changes due to editing conflict
This commit is contained in:
rr- 2017-08-25 23:53:51 +02:00
parent 674d6c35d7
commit 87735110aa
2 changed files with 71 additions and 0 deletions

View File

@ -66,6 +66,12 @@
<a href class='add'>Add a note</a> <a href class='add'>Add a note</a>
<%= ctx.makeTextarea({disabled: true, text: 'Content (supports Markdown)', rows: '8'}) %> <%= ctx.makeTextarea({disabled: true, text: 'Content (supports Markdown)', rows: '8'}) %>
<a href class='delete inactive'>Delete selected note</a> <a href class='delete inactive'>Delete selected note</a>
<% if (ctx.hasClipboard) { %>
<br/>
<a href class='copy'>Export notes to clipboard</a>
<br/>
<a href class='paste'>Import notes from clipboard</a>
<% } %>
</section> </section>
<% } %> <% } %>

View File

@ -5,6 +5,8 @@ const config = require('../config.js');
const events = require('../events.js'); const events = require('../events.js');
const misc = require('../util/misc.js'); const misc = require('../util/misc.js');
const views = require('../util/views.js'); const views = require('../util/views.js');
const Note = require('../models/note.js');
const Point = require('../models/point.js');
const TagInputControl = require('./tag_input_control.js'); const TagInputControl = require('./tag_input_control.js');
const ExpanderControl = require('../controls/expander_control.js'); const ExpanderControl = require('../controls/expander_control.js');
const FileDropperControl = require('../controls/file_dropper_control.js'); const FileDropperControl = require('../controls/file_dropper_control.js');
@ -25,6 +27,7 @@ class PostEditSidebarControl extends events.EventTarget {
views.replaceContent(this._hostNode, template({ views.replaceContent(this._hostNode, template({
post: this._post, post: this._post,
enableSafety: config.enableSafety, enableSafety: config.enableSafety,
hasClipboard: document.queryCommandSupported('copy'),
canEditPostSafety: api.hasPrivilege('posts:edit:safety'), canEditPostSafety: api.hasPrivilege('posts:edit:safety'),
canEditPostSource: api.hasPrivilege('posts:edit:source'), canEditPostSource: api.hasPrivilege('posts:edit:source'),
canEditPostTags: api.hasPrivilege('posts:edit:tags'), canEditPostTags: api.hasPrivilege('posts:edit:tags'),
@ -107,6 +110,16 @@ class PostEditSidebarControl extends events.EventTarget {
'click', e => this._evtAddNoteClick(e)); 'click', e => this._evtAddNoteClick(e));
} }
if (this._copyNotesLinkNode) {
this._copyNotesLinkNode.addEventListener(
'click', e => this._evtCopyNotesClick(e));
}
if (this._pasteNotesLinkNode) {
this._pasteNotesLinkNode.addEventListener(
'click', e => this._evtPasteNotesClick(e));
}
if (this._deleteNoteLinkNode) { if (this._deleteNoteLinkNode) {
this._deleteNoteLinkNode.addEventListener( this._deleteNoteLinkNode.addEventListener(
'click', e => this._evtDeleteNoteClick(e)); 'click', e => this._evtDeleteNoteClick(e));
@ -252,6 +265,50 @@ class PostEditSidebarControl extends events.EventTarget {
this._postNotesOverlayControl.switchToDrawing(); this._postNotesOverlayControl.switchToDrawing();
} }
_evtCopyNotesClick(e) {
e.preventDefault();
let textarea = document.createElement('textarea');
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
textarea.value = JSON.stringify([...this._post.notes].map(note => ({
polygon: [...note.polygon].map(
point => [point.x, point.y]),
text: note.text,
})));
document.body.appendChild(textarea);
textarea.select();
let success = false;
try {
success = document.execCommand('copy');
} catch (err) {
}
textarea.blur();
document.body.removeChild(textarea);
alert(success
? 'Notes copied to clipboard.'
: 'Failed to copy the text to clipboard. Sorry.');
}
_evtPasteNotesClick(e) {
e.preventDefault();
const text = window.prompt(
'Please enter the exported notes snapshot:');
if (!text) {
return;
}
const notesObj = JSON.parse(text);
this._post.notes.clear();
for (let noteObj of notesObj) {
let note = new Note();
for (let pointObj of noteObj.polygon) {
note.polygon.add(new Point(pointObj[0], pointObj[1]));
}
note.text = noteObj.text;
this._post.notes.add(note);
}
}
_evtDeleteNoteClick(e) { _evtDeleteNoteClick(e) {
e.preventDefault(); e.preventDefault();
if (e.target.classList.contains('inactive')) { if (e.target.classList.contains('inactive')) {
@ -350,6 +407,14 @@ class PostEditSidebarControl extends events.EventTarget {
return this._formNode.querySelector('.notes .add'); return this._formNode.querySelector('.notes .add');
} }
get _copyNotesLinkNode() {
return this._formNode.querySelector('.notes .copy');
}
get _pasteNotesLinkNode() {
return this._formNode.querySelector('.notes .paste');
}
get _deleteNoteLinkNode() { get _deleteNoteLinkNode() {
return this._formNode.querySelector('.notes .delete'); return this._formNode.querySelector('.notes .delete');
} }