diff --git a/sql/00006.sql b/sql/00006.sql deleted file mode 100644 index 56f2acb..0000000 --- a/sql/00006.sql +++ /dev/null @@ -1,119 +0,0 @@ -CREATE OR REPLACE PROCEDURE public.add_nodes(IN p_user_id integer, IN p_client_uuid uuid, IN p_nodes jsonb) - LANGUAGE plpgsql -AS $procedure$ - -DECLARE - node_data jsonb; - node_updated timestamptz; - db_updated timestamptz; - db_uuid uuid; - db_client uuid; - db_history_uuid uuid; - node_uuid uuid; - node_parent_uuid uuid; - node_history_uuid uuid; - -BEGIN - FOR node_data IN SELECT * FROM jsonb_array_elements(p_nodes) - LOOP - node_uuid = (node_data->>'UUID')::uuid; - node_history_uuid = (node_data->>'HistoryUUID')::uuid; - node_updated = (node_data->>'Updated')::timestamptz; - - - - -- Frontend is using an all-zero UUID to define the root node. - -- Database is using NULL. - IF node_data->>'ParentUUID' = '00000000-0000-0000-0000-000000000000' OR node_data->>'ParentUUID' = '' THEN - node_parent_uuid = NULL; - ELSE - node_parent_uuid = (node_data->>'ParentUUID')::uuid; - END IF; - - - - -- Every jode has a new history UUID to keep the history entry uniquely identifiable - -- across clients. A history entry could potentially be sent again, but should be - -- safe to ignore as every change to a node should have a new history UUID. - -- - -- The current node is also stored as history. - INSERT INTO node_history( - user_id, "uuid", "history_uuid", parents, created, updated, - "name", "content", "content_encrypted", - client - ) - VALUES( - p_user_id, -- combined key - node_uuid, -- combined key - node_history_uuid, -- combined key - (jsonb_populate_record(null::json_ancestor_array, node_data))."Ancestors", - COALESCE((node_data->>'Created')::timestamptz, NOW()), - COALESCE((node_data->>'Updated')::timestamptz, NOW()), - (node_data->>'Name')::varchar, - (node_data->>'Content')::text, - '', /* content_encrypted */ - p_client_uuid - ) - ON CONFLICT ("user_id", "uuid", "history_uuid") - DO NOTHING; - - - - -- Retrieve the current modified timestamp for this node from the database. - SELECT - uuid, updated, client - INTO - db_uuid, db_updated, db_client - FROM public."node" - WHERE - user_id = p_user_id AND - uuid::uuid = node_uuid::uuid; - - - - -- Is the node not in database? It needs to be created. - IF db_uuid IS NULL THEN - RAISE NOTICE '01 New node %', node_uuid; - - INSERT INTO public."node" ( - user_id, "uuid", parent_uuid, created, updated, - "name", "content", "content_encrypted", - client - ) - VALUES( - p_user_id, - node_uuid, - node_parent_uuid, - COALESCE((node_data->>'Created')::timestamptz, NOW()), - COALESCE((node_data->>'Updated')::timestamptz, NOW()), - (node_data->>'Name')::varchar, - (node_data->>'Content')::text, - '', /* content_encrypted */ - p_client_uuid - ); - - CONTINUE; - - END IF; - - - - -- Update the public node as well if it was older than incoming node. - IF node_updated > db_updated THEN - UPDATE public."node" - SET - updated = (node_data->>'Updated')::timestamptz, - updated_seq = nextval('node_updates'), - parent_uuid = (node_data->>'ParentUUID')::uuid, - name = (node_data->>'Name')::varchar, - content = (node_data->>'Content')::text, - client = p_client_uuid - WHERE - user_id = p_user_id AND - uuid::uuid = node_uuid::uuid; - END IF; - - END LOOP; -END -$procedure$ -; diff --git a/static/css/notes2.css b/static/css/notes2.css index 079e3c1..04c9f68 100644 --- a/static/css/notes2.css +++ b/static/css/notes2.css @@ -418,8 +418,6 @@ n2-nodeui { font-size: 1.75em; margin-top: 8px; margin-bottom: 0px; - white-space: nowrap; - width: min-content; } .el-functions { diff --git a/static/images/icon_drag.svg b/static/images/icon_drag.svg deleted file mode 100644 index 02d628e..0000000 --- a/static/images/icon_drag.svg +++ /dev/null @@ -1,71 +0,0 @@ - - - -folder-openfolder-open-outlinenotebook-outlinetext-box-outline diff --git a/static/images/icon_drag_ok.svg b/static/images/icon_drag_ok.svg deleted file mode 100644 index 94ba949..0000000 --- a/static/images/icon_drag_ok.svg +++ /dev/null @@ -1,75 +0,0 @@ - - - -folder-openfolder-open-outlinenotebook-outlinetext-box-outlinetext-box-check-outline diff --git a/static/images/icon_drag_source.svg b/static/images/icon_drag_source.svg deleted file mode 100644 index 6378ed9..0000000 --- a/static/images/icon_drag_source.svg +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - drag-variant - - - diff --git a/static/js/app.mjs b/static/js/app.mjs index 8fc43df..756f486 100644 --- a/static/js/app.mjs +++ b/static/js/app.mjs @@ -12,7 +12,6 @@ export class App { this.crumbs = new N2Crumbs() this.crumbsElement = document.getElementById('crumbs') this.nodeUI = document.getElementById('note') - this.dragIcon = new N2DragIcon() this.sidebar.render().then(sidebar => { document.getElementById('tree').append(sidebar) @@ -69,7 +68,6 @@ export class App { }) document.querySelector('#page-root .create').addEventListener('click', () => this.createNode()) - document.body.append(this.dragIcon) _mbus.dispatch('SHOW_PAGE', { page: 'node' }) @@ -80,13 +78,13 @@ export class App { // There a slight delay to initiate sync seems reasonable. setTimeout(() => window._sync.run(), 1000) }// }}} + keyHandler(event) {//{{{ let handled = true // Most keybindings is Alt+Shift, since the popular browsers at the time (2023) allows to override thees. // Ctrl+S is the exception to using Alt+Shift, since it is overridable and in such widespread use for saving. // Thus, the exception is acceptable to consequent use of alt+shift. - const CTRL = !event.shiftKey && event.ctrlKey && !event.altKey const SHIFT_ALT = event.shiftKey && !event.ctrlKey && event.altKey const SHIFT_CTRL_ALT = event.shiftKey && event.ctrlKey && event.altKey @@ -123,7 +121,7 @@ export class App { break case 'S': - if (!CTRL) { handled = false; break } + if (!SHIFT_ALT) { handled = false; break } this.nodeUI.saveNode() break @@ -152,10 +150,6 @@ export class App { async saveNode() {//{{{ }//}}} - async moveNode(node, targetNodeUUID) {// {{{ - node.moveToParent(targetNodeUUID) - await node.save() - }// }}} async createNode(createUnderUUID) {//{{{ const parentUUID = createUnderUUID ? createUnderUUID : this.currentNode.UUID const p = createUnderUUID ? 'Name for sibling document' : 'Name for sub-document' @@ -244,6 +238,7 @@ class N2Crumbs extends CustomHTMLElement { return this }// }}} } +customElements.define('n2-crumbs', N2Crumbs) class N2Crumb extends CustomHTMLElement { static {// {{{ @@ -274,6 +269,7 @@ class N2Crumb extends CustomHTMLElement { this.elLink.addEventListener('click', () => _mbus.dispatch("GO_TO_NODE", { nodeUUID: this.uuid, dontPush: false, dontExpand: true })) }// }}} } +customElements.define('n2-crumb', N2Crumb) function tmpl(html) {// {{{ const el = document.createElement('template') @@ -347,52 +343,4 @@ class OpSearch extends Op { }// }}} } -class N2DragIcon extends CustomHTMLElement { - static {// {{{ - this.tmpl = document.createElement('template') - this.tmpl.innerHTML = ` - - - ` - }// }}} - constructor() {// {{{ - super(true) - - document.addEventListener('dragover', e => { - this.style.left = `${e.clientX + 8}px` - this.style.top = `${e.clientY}px` - }) - - this.dragTarget = null - }// }}} - start() {// {{{ - this.style.display = 'block' - }// }}} - end() {// {{{ - this.style.display = 'none' - }// }}} - icon(name) {// {{{ - if (name != '') - name = '_' + name - this.elIcon.setAttribute('src', `/images/${_VERSION}/icon_drag${name}.svg`) - }// }}} - setTarget(t) {// {{{ - this.dragTarget = t - }// }}} - getTarget() {// {{{ - return this.dragTarget - }// }}} -} - -customElements.define('n2-crumbs', N2Crumbs) -customElements.define('n2-crumb', N2Crumb) -customElements.define('n2-dragicon', N2DragIcon) - // vim: foldmethod=marker diff --git a/static/js/node_store.mjs b/static/js/node_store.mjs index 324f004..f31a4b6 100644 --- a/static/js/node_store.mjs +++ b/static/js/node_store.mjs @@ -247,7 +247,6 @@ export class NodeStore { nodeStore = t.objectStore('nodes') t.oncomplete = (_event) => { - console.log('complete') resolve() } @@ -359,7 +358,6 @@ class SimpleNodeStore { // Node to be moved is first stored in the new queue. const req = store.put(node.data) req.onsuccess = () => { - console.log('here') resolve() } req.onerror = (event) => { diff --git a/static/js/page_node.mjs b/static/js/page_node.mjs index f65afb9..834e22f 100644 --- a/static/js/page_node.mjs +++ b/static/js/page_node.mjs @@ -422,11 +422,6 @@ export class Node { getParent() {//{{{ return this._parent }//}}} - moveToParent(newParentUUID) {// {{{ - this.ParentUUID = newParentUUID - this.data.ParentUUID = newParentUUID - this._modified = true - }// }}} isLastSibling() {//{{{ return this._sibling_after === null }//}}} @@ -468,10 +463,9 @@ export class Node { // When stored into database and ancestry was changed, // the ancestry path could be interesting. - /* const ancestors = await nodeStore.getNodeAncestry(this) this.data.Ancestors = ancestors.map(a => a.get('Name')).reverse() - */ + /* The node history is a local store for node history. * This could be provisioned from the server or cleared if * deemed unnecessary. @@ -487,17 +481,12 @@ export class Node { const history = nodeStore.nodesHistory.add(this) // Updated node is added to the send queue to be stored on server. - const sendQueue = nodeStore.sendQueue.add(this) // Updated node is saved to the primary node store. const nodeStoreAdding = nodeStore.add([this]) - console.log('waiting') - await Promise.all([history, sendQueue, nodeStoreAdding]) - console.log('waiting done') - - return + return Promise.all([history, sendQueue, nodeStoreAdding]) }//}}} } diff --git a/static/js/sidebar.mjs b/static/js/sidebar.mjs index 561de8d..7d73d6a 100644 --- a/static/js/sidebar.mjs +++ b/static/js/sidebar.mjs @@ -449,20 +449,15 @@ export class N2Sidebar extends CustomHTMLElement { treenode?.scrollIntoView({ block: 'nearest' }) }// }}} } +customElements.define('n2-sidebar', N2Sidebar) export class N2TreeNode extends CustomHTMLElement { - static DRAG_ICON = new Image() - static DRAG_ICON_OK = new Image() - static {// {{{ - N2TreeNode.DRAG_ICON.src = `/images/${_VERSION}/leaf.svg` - N2TreeNode.DRAG_ICON_OK.src = `/images/${_VERSION}/expanded.svg` - this.tmpl = document.createElement('template') this.tmpl.innerHTML = `
- +
@@ -545,7 +490,6 @@ export class N2TreeNode extends CustomHTMLElement { constructor(sidebar, node, parent) {//{{{ super() - this.setAttribute('draggable', 'true') this.classList.add('node') this.sidebar = sidebar @@ -554,7 +498,6 @@ export class N2TreeNode extends CustomHTMLElement { this.children_populated = false this.rendered = false - this.dragNode = null this.elExpandToggle.addEventListener('click', () => this.sidebar.setNodeExpanded(this.node, !this.sidebar.getNodeExpanded(this.node.UUID))) this.elName.addEventListener('click', () => _mbus.dispatch('TREE_NODE_SELECTED', this.node)) @@ -562,70 +505,6 @@ export class N2TreeNode extends CustomHTMLElement { _mbus.subscribe(`NODE_EXPAND_${node.UUID}`, _state => { this.render(true) }) - - // Drag-and-dropping of nodes - this.addEventListener('dragstart', event => this.dragStart(event)) - this.addEventListener('dragend', event => this.dragEnd(event)) - this.addEventListener('dragover', event => this.dragOver(event)) - this.addEventListener('drop', event => this.dragDrop(event)) - this.elName.addEventListener('dragenter', event => this.dragEnter(event)) - this.elName.addEventListener('dragleave', event => this.dragLeave(event)) - }// }}} - dragStart(e) {// {{{ - if (this.node.isModified()) { - alert('Save note before moving it.') - e.stopPropagation() - e.preventDefault() - return - } - - this.classList.add('drag-source') - const blankPixel = new Image() - blankPixel.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' - e.dataTransfer.setDragImage(blankPixel, 0, 0) - e.dataTransfer.allowedEffects = 'none' - e.stopPropagation() - _app.dragIcon.start() - }// }}} - dragEnd(e) {// {{{ - this.classList.remove('drag-source') - _app.dragIcon.end() - e.stopPropagation() - }// }}} - dragOver(e) {// {{{ - e.dataTransfer.dropEffect = 'move' - e.preventDefault() - }// }}} - async dragDrop(e) {// {{{ - e.stopPropagation() - const moveToNode = _app.dragIcon.getTarget() - await _app.moveNode(this.node, moveToNode.node.UUID) - return - - _app.sidebar.setNodeExpanded(moveToNode, true) - await this.render(true, true) - await moveToNode.render(true, true) - - this.dragLeave(e) - }// }}} - dragEnter(e) {// {{{ - const targetNode = e.target.closest('n2-treenode') - if (targetNode.classList.contains('drag-source')) - return - e.stopPropagation() - _app.dragIcon.icon('ok') - this.classList.add('drag-target') - - _app.dragIcon.setTarget(this) - }// }}} - dragLeave(e) {// {{{ - e.stopPropagation() - e.dataTransfer.dropEffect = 'none' - e.dataTransfer.setDragImage(N2TreeNode.DRAG_ICON, -16, 8) - _app.dragIcon.icon('') - this.classList.remove('drag-target') - - _app.dragIcon.setTarget(null) }// }}} async fetchChildren(force_fetch) {//{{{ if (this.children_populated && !force_fetch) @@ -696,8 +575,6 @@ export class N2TreeNode extends CustomHTMLElement { img.setAttribute('src', newSrc) }// }}} } - -customElements.define('n2-sidebar', N2Sidebar) customElements.define('n2-treenode', N2TreeNode) // vim: foldmethod=marker diff --git a/views/pages/notes2.gotmpl b/views/pages/notes2.gotmpl index 381d672..abec2b0 100644 --- a/views/pages/notes2.gotmpl +++ b/views/pages/notes2.gotmpl @@ -1,7 +1,4 @@ {{ define "page" }} - - -
>