From f8fd8e47a977174e211b992cffd3077055cb93a4 Mon Sep 17 00:00:00 2001 From: zadam Date: Sun, 12 Jun 2022 23:29:11 +0200 Subject: [PATCH] added rename note bulk action --- package-lock.json | 11 ++++ package.json | 1 + src/becca/entities/abstract_entity.js | 7 +++ src/becca/entities/attribute.js | 1 + src/becca/entities/note.js | 43 ++++++++++++-- src/public/app/services/bulk_action.js | 4 +- .../widgets/bulk_actions/note/delete_note.js | 11 ++++ .../widgets/bulk_actions/note/rename_note.js | 56 +++++++++++++++++++ src/public/stylesheets/style.css | 1 + src/services/bulk_actions.js | 13 +++++ 10 files changed, 141 insertions(+), 7 deletions(-) create mode 100644 src/public/app/widgets/bulk_actions/note/rename_note.js diff --git a/package-lock.json b/package-lock.json index 55be6a476..e8e8035b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "cookie-parser": "1.4.6", "csurf": "1.11.0", "dayjs": "1.11.3", + "dayjs-plugin-utc": "^0.1.2", "ejs": "3.1.8", "electron-debug": "3.2.0", "electron-dl": "3.3.1", @@ -3097,6 +3098,11 @@ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.3.tgz", "integrity": "sha512-xxwlswWOlGhzgQ4TKzASQkUhqERI3egRNqgV4ScR8wlANA/A9tZ7miXa44vTTKEq5l7vWoL5G57bG3zA+Kow0A==" }, + "node_modules/dayjs-plugin-utc": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dayjs-plugin-utc/-/dayjs-plugin-utc-0.1.2.tgz", + "integrity": "sha512-ExERH5o3oo6jFOdkvMP3gytTCQ9Ksi5PtylclJWghr7k7m3o2U5QrwtdiJkOxLOH4ghr0EKhpqGefzGz1VvVJg==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -13198,6 +13204,11 @@ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.3.tgz", "integrity": "sha512-xxwlswWOlGhzgQ4TKzASQkUhqERI3egRNqgV4ScR8wlANA/A9tZ7miXa44vTTKEq5l7vWoL5G57bG3zA+Kow0A==" }, + "dayjs-plugin-utc": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dayjs-plugin-utc/-/dayjs-plugin-utc-0.1.2.tgz", + "integrity": "sha512-ExERH5o3oo6jFOdkvMP3gytTCQ9Ksi5PtylclJWghr7k7m3o2U5QrwtdiJkOxLOH4ghr0EKhpqGefzGz1VvVJg==" + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", diff --git a/package.json b/package.json index f000c3f07..968205b66 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "cookie-parser": "1.4.6", "csurf": "1.11.0", "dayjs": "1.11.3", + "dayjs-plugin-utc": "^0.1.2", "ejs": "3.1.8", "electron-debug": "3.2.0", "electron-dl": "3.3.1", diff --git a/src/becca/entities/abstract_entity.js b/src/becca/entities/abstract_entity.js index 574c5bae5..ef634543e 100644 --- a/src/becca/entities/abstract_entity.js +++ b/src/becca/entities/abstract_entity.js @@ -14,16 +14,19 @@ let becca = null; * Base class for all backend entities. */ class AbstractEntity { + /** @protected */ beforeSaving() { this.generateIdIfNecessary(); } + /** @protected */ generateIdIfNecessary() { if (!this[this.constructor.primaryKeyName]) { this[this.constructor.primaryKeyName] = utils.newEntityId(); } } + /** @protected */ generateHash(isDeleted = false) { let contentToHash = ""; @@ -38,10 +41,12 @@ class AbstractEntity { return utils.hash(contentToHash).substr(0, 10); } + /** @protected */ getUtcDateChanged() { return this.utcDateModified || this.utcDateCreated; } + /** @protected */ get becca() { if (!becca) { becca = require('../becca'); @@ -50,6 +55,7 @@ class AbstractEntity { return becca; } + /** @protected */ addEntityChange(isDeleted = false) { entityChangesService.addEntityChange({ entityName: this.constructor.entityName, @@ -61,6 +67,7 @@ class AbstractEntity { }); } + /** @protected */ getPojoToSave() { return this.getPojo(); } diff --git a/src/becca/entities/attribute.js b/src/becca/entities/attribute.js index 4d6cb969d..d71ada463 100644 --- a/src/becca/entities/attribute.js +++ b/src/becca/entities/attribute.js @@ -62,6 +62,7 @@ class Attribute extends AbstractEntity { return this; } + init() { if (this.attributeId) { this.becca.attributes[this.attributeId] = this; diff --git a/src/becca/entities/note.js b/src/becca/entities/note.js index 4b43f07dc..17b5b716f 100644 --- a/src/becca/entities/note.js +++ b/src/becca/entities/note.js @@ -9,6 +9,9 @@ const entityChangesService = require('../../services/entity_changes'); const AbstractEntity = require("./abstract_entity"); const NoteRevision = require("./note_revision"); const TaskContext = require("../../services/task_context"); +const dayjs = require("dayjs"); +const utc = require('dayjs/plugin/utc') +dayjs.extend(utc) const LABEL = 'label'; const RELATION = 'relation'; @@ -84,13 +87,17 @@ class Note extends AbstractEntity { } init() { - /** @type {Branch[]} */ + /** @type {Branch[]} + * @private */ this.parentBranches = []; - /** @type {Note[]} */ + /** @type {Note[]} + * @private */ this.parents = []; - /** @type {Note[]} */ + /** @type {Note[]} + * @private*/ this.children = []; - /** @type {Attribute[]} */ + /** @type {Attribute[]} + * @private */ this.ownedAttributes = []; /** @type {Attribute[]|null} @@ -100,7 +107,8 @@ class Note extends AbstractEntity { * @private*/ this.inheritableAttributeCache = null; - /** @type {Attribute[]} */ + /** @type {Attribute[]} + * @private*/ this.targetRelations = []; this.becca.addNote(this.noteId, this); @@ -114,16 +122,19 @@ class Note extends AbstractEntity { /** * size of the content in bytes * @type {int|null} + * @private */ this.contentSize = null; /** * size of the content and note revision contents in bytes * @type {int|null} + * @private */ this.noteSize = null; /** * number of note revisions for this note * @type {int|null} + * @private */ this.revisionCount = null; } @@ -225,6 +236,22 @@ class Note extends AbstractEntity { WHERE noteId = ?`, [this.noteId]); } + get dateCreatedObj() { + return this.dateCreated === null ? null : dayjs(this.dateCreated); + } + + get utcDateCreatedObj() { + return this.utcDateCreated === null ? null : dayjs.utc(this.utcDateCreated); + } + + get dateModifiedObj() { + return this.dateModified === null ? null : dayjs(this.dateModified); + } + + get utcDateModifiedObj() { + return this.utcDateModified === null ? null : dayjs.utc(this.utcDateModified); + } + /** @returns {*} */ getJsonContent() { const content = this.getContent(); @@ -350,6 +377,7 @@ class Note extends AbstractEntity { } } + /** @private */ __getAttributes(path) { if (path.includes(this.noteId)) { return []; @@ -401,7 +429,10 @@ class Note extends AbstractEntity { return this.__attributeCache; } - /** @returns {Attribute[]} */ + /** + * @private + * @returns {Attribute[]} + */ __getInheritableAttributes(path) { if (path.includes(this.noteId)) { return []; diff --git a/src/public/app/services/bulk_action.js b/src/public/app/services/bulk_action.js index 48ca92ef2..4644309df 100644 --- a/src/public/app/services/bulk_action.js +++ b/src/public/app/services/bulk_action.js @@ -12,6 +12,7 @@ import UpdateRelationTargetBulkAction from "../widgets/bulk_actions/relation/upd import ExecuteScriptBulkAction from "../widgets/bulk_actions/execute_script.js"; import AddLabelBulkAction from "../widgets/bulk_actions/label/add_label.js"; import AddRelationBulkAction from "../widgets/bulk_actions/relation/add_relation.js"; +import RenameNoteBulkAction from "../widgets/bulk_actions/note/rename_note.js"; const ACTION_GROUPS = [ { @@ -24,7 +25,7 @@ const ACTION_GROUPS = [ }, { title: 'Notes', - actions: [DeleteNoteBulkAction, DeleteNoteRevisionsBulkAction, MoveNoteBulkAction], + actions: [RenameNoteBulkAction, MoveNoteBulkAction, DeleteNoteBulkAction, DeleteNoteRevisionsBulkAction], }, { title: 'Other', @@ -33,6 +34,7 @@ const ACTION_GROUPS = [ ]; const ACTION_CLASSES = [ + RenameNoteBulkAction, MoveNoteBulkAction, DeleteNoteBulkAction, DeleteNoteRevisionsBulkAction, diff --git a/src/public/app/widgets/bulk_actions/note/delete_note.js b/src/public/app/widgets/bulk_actions/note/delete_note.js index 1580987f1..77fe41995 100644 --- a/src/public/app/widgets/bulk_actions/note/delete_note.js +++ b/src/public/app/widgets/bulk_actions/note/delete_note.js @@ -9,6 +9,17 @@ const TPL = ` + + `; diff --git a/src/public/app/widgets/bulk_actions/note/rename_note.js b/src/public/app/widgets/bulk_actions/note/rename_note.js new file mode 100644 index 000000000..0d3a2ebf1 --- /dev/null +++ b/src/public/app/widgets/bulk_actions/note/rename_note.js @@ -0,0 +1,56 @@ +import SpacedUpdate from "../../../services/spaced_update.js"; +import AbstractBulkAction from "../abstract_bulk_action.js"; + +const TPL = ` + + +
+
Rename note title to:
+ + +
+ + + + + + +`; + +export default class RenameNoteBulkAction extends AbstractBulkAction { + static get actionName() { return "renameNote"; } + static get actionTitle() { return "Rename note"; } + + doRender() { + const $action = $(TPL); + + const $newTitle = $action.find('.new-title'); + $newTitle.val(this.actionDef.newTitle || ""); + + const spacedUpdate = new SpacedUpdate(async () => { + await this.saveAction({ + newTitle: $newTitle.val(), + }); + }, 1000); + + $newTitle.on('input', () => spacedUpdate.scheduleUpdate()); + + return $action; + } +} diff --git a/src/public/stylesheets/style.css b/src/public/stylesheets/style.css index ecf94e57f..60b6ac1e2 100644 --- a/src/public/stylesheets/style.css +++ b/src/public/stylesheets/style.css @@ -868,6 +868,7 @@ body { box-shadow: 10px 10px 93px -25px black; padding: 10px 15px 10px 15px !important; width: 600px; + white-space: normal; } .help-dropdown .dropdown-menu pre { diff --git a/src/services/bulk_actions.js b/src/services/bulk_actions.js index 21d78bbae..b13513a35 100644 --- a/src/services/bulk_actions.js +++ b/src/services/bulk_actions.js @@ -4,6 +4,8 @@ const becca = require("../becca/becca"); const cloningService = require("./cloning"); const branchService = require("./branches"); const utils = require("./utils"); +const dayjs = require("dayjs"); +const cls = require("./cls.js"); const ACTION_HANDLERS = { addLabel: (action, note) => { @@ -30,6 +32,17 @@ const ACTION_HANDLERS = { relation.markAsDeleted(); } }, + renameNote: (action, note) => { + // "officially" injected value: + // - note + + const newTitle = eval('`' + action.newTitle + '`'); + + if (note.title !== newTitle) { + note.title = newTitle; + note.save(); + } + }, renameLabel: (action, note) => { for (const label of note.getOwnedLabels(action.oldLabelName)) { // attribute name is immutable, renaming means delete old + create new