note revision save also saves attachments

This commit is contained in:
zadam 2023-04-11 23:24:39 +02:00
parent 6cf0fe0b73
commit 34ecd77bd4
4 changed files with 60 additions and 29 deletions

View File

@ -119,7 +119,14 @@ class AbstractBeccaEntity {
return this; return this;
} }
/** @protected */ /**
* Hot entities keep stable blobId which is continuously updated and is not shared with other entities.
* Cold entities can still update its blob, but the blobId will change (and new blob will be created).
* Functionally this is the same, it's an optimization to avoid creating a new blob every second with auto saved
* text notes.
*
* @protected
*/
_isHot() { _isHot() {
return false; return false;
} }
@ -128,6 +135,7 @@ class AbstractBeccaEntity {
_setContent(content, opts = {}) { _setContent(content, opts = {}) {
// client code asks to save entity even if blobId didn't change (something else was changed) // client code asks to save entity even if blobId didn't change (something else was changed)
opts.forceSave = !!opts.forceSave; opts.forceSave = !!opts.forceSave;
opts.forceCold = !!opts.forceCold;
if (content === null || content === undefined) { if (content === null || content === undefined) {
throw new Error(`Cannot set null content to ${this.constructor.primaryKeyName} '${this[this.constructor.primaryKeyName]}'`); throw new Error(`Cannot set null content to ${this.constructor.primaryKeyName} '${this[this.constructor.primaryKeyName]}'`);
@ -150,7 +158,7 @@ class AbstractBeccaEntity {
} }
sql.transactional(() => { sql.transactional(() => {
let newBlobId = this._saveBlob(content); let newBlobId = this._saveBlob(content, opts);
if (newBlobId !== this.blobId || opts.forceSave) { if (newBlobId !== this.blobId || opts.forceSave) {
this.blobId = newBlobId; this.blobId = newBlobId;
@ -160,11 +168,11 @@ class AbstractBeccaEntity {
} }
/** @protected */ /** @protected */
_saveBlob(content) { _saveBlob(content, opts) {
let newBlobId; let newBlobId;
let blobNeedsInsert; let blobNeedsInsert;
if (this._isHot()) { if (this._isHot() && !opts.forceCold) {
newBlobId = this.blobId || utils.randomBlobId(); newBlobId = this.blobId || utils.randomBlobId();
blobNeedsInsert = true; blobNeedsInsert = true;
} else { } else {

View File

@ -55,6 +55,19 @@ class BAttachment extends AbstractBeccaEntity {
this.utcDateScheduledForDeletionSince = row.utcDateScheduledForDeletionSince; this.utcDateScheduledForDeletionSince = row.utcDateScheduledForDeletionSince;
} }
/** @returns {BAttachment} */
copy() {
return new BAttachment({
parentId: this.parentId,
role: this.role,
mime: this.mime,
title: this.title,
blobId: this.blobId,
isProtected: this.isProtected,
utcDateScheduledForDeletionSince: this.utcDateScheduledForDeletionSince
});
}
getNote() { getNote() {
return becca.notes[this.parentId]; return becca.notes[this.parentId];
} }
@ -73,6 +86,7 @@ class BAttachment extends AbstractBeccaEntity {
* @param content * @param content
* @param {object} [opts] * @param {object} [opts]
* @param {object} [opts.forceSave=false] - will also save this BAttachment entity * @param {object} [opts.forceSave=false] - will also save this BAttachment entity
* @param {object} [opts.forceCold=false] - blob has to be saved as cold
*/ */
setContent(content, opts) { setContent(content, opts) {
this._setContent(content, opts); this._setContent(content, opts);

View File

@ -241,6 +241,7 @@ class BNote extends AbstractBeccaEntity {
* @param content * @param content
* @param {object} [opts] * @param {object} [opts]
* @param {object} [opts.forceSave=false] - will also save this BNote entity * @param {object} [opts.forceSave=false] - will also save this BNote entity
* @param {object} [opts.forceCold=false] - blob has to be saved as cold
*/ */
setContent(content, opts) { setContent(content, opts) {
this._setContent(content, opts); this._setContent(content, opts);
@ -1521,35 +1522,43 @@ class BNote extends AbstractBeccaEntity {
* @returns {BNoteRevision|null} * @returns {BNoteRevision|null}
*/ */
saveNoteRevision() { saveNoteRevision() {
const content = this.getContent(); return sql.transactional(() => {
const content = this.getContent();
const contentMetadata = this.getContentMetadata();
if (!content || (Buffer.isBuffer(content) && content.byteLength === 0)) { const noteRevision = new BNoteRevision({
return null; noteId: this.noteId,
} // title and text should be decrypted now
title: this.title,
type: this.type,
mime: this.mime,
isProtected: this.isProtected,
utcDateLastEdited: this.utcDateModified > contentMetadata.utcDateModified
? this.utcDateModified
: contentMetadata.utcDateModified,
utcDateCreated: dateUtils.utcNowDateTime(),
utcDateModified: dateUtils.utcNowDateTime(),
dateLastEdited: this.dateModified > contentMetadata.dateModified
? this.dateModified
: contentMetadata.dateModified,
dateCreated: dateUtils.localNowDateTime()
}, true);
const contentMetadata = this.getContentMetadata(); noteRevision.setContent(content, { forceSave: true });
const noteRevision = new BNoteRevision({ for (const noteAttachment of this.getAttachments()) {
noteId: this.noteId, const content = noteAttachment.getContent();
// title and text should be decrypted now
title: this.title,
type: this.type,
mime: this.mime,
isProtected: this.isProtected,
utcDateLastEdited: this.utcDateModified > contentMetadata.utcDateModified
? this.utcDateModified
: contentMetadata.utcDateModified,
utcDateCreated: dateUtils.utcNowDateTime(),
utcDateModified: dateUtils.utcNowDateTime(),
dateLastEdited: this.dateModified > contentMetadata.dateModified
? this.dateModified
: contentMetadata.dateModified,
dateCreated: dateUtils.localNowDateTime()
}, true);
noteRevision.setContent(content, { forceSave: true }); const revisionAttachment = noteAttachment.copy();
revisionAttachment.parentId = noteRevision.noteRevisionId;
revisionAttachment.setContent(content, {
forceSave: true,
forceCold: true
});
}
return noteRevision; return noteRevision;
});
} }
/** /**

View File

@ -48,7 +48,7 @@ export default class AttachmentDetailTypeWidget extends TypeWidget {
async entitiesReloadedEvent({loadResults}) { async entitiesReloadedEvent({loadResults}) {
const attachmentChange = loadResults.getAttachments().find(att => att.attachmentId === this.attachment.attachmentId); const attachmentChange = loadResults.getAttachments().find(att => att.attachmentId === this.attachment.attachmentId);
if (attachmentChange.isDeleted) { if (attachmentChange?.isDeleted) {
this.refresh(); // all other updates are handled within AttachmentDetailWidget this.refresh(); // all other updates are handled within AttachmentDetailWidget
} }
} }