wip attachment widget

This commit is contained in:
zadam 2023-03-30 23:48:26 +02:00
parent 9be524ef89
commit fa406d3ded
7 changed files with 138 additions and 61 deletions

View File

@ -11,7 +11,9 @@ module.exports = () => {
try { try {
const attachment = note.convertToParentAttachment({force: false}); const attachment = note.convertToParentAttachment({force: false});
log.info(`Auto-converted note '${note.noteId}' into attachment '${attachment.attachmentId}'.`) if (attachment) {
log.info(`Auto-converted note '${note.noteId}' into attachment '${attachment.attachmentId}'.`);
}
} }
catch (e) { catch (e) {
log.error(`Cannot convert note '${note.noteId}' to attachment: ${e.message} ${e.stack}`); log.error(`Cannot convert note '${note.noteId}' to attachment: ${e.message} ${e.stack}`);

View File

@ -1086,11 +1086,15 @@ class BNote extends AbstractBeccaEntity {
.map(row => new BAttachment(row)); .map(row => new BAttachment(row));
} }
/** @returns {BAttachment|undefined} */ /** @returns {BAttachment|null} */
getAttachmentByName(name) { getAttachmentById(attachmentId) {
return sql.getRows("SELECT * FROM attachments WHERE parentId = ? AND name = ? AND isDeleted = 0", [this.noteId, name]) return sql.getRows(`
.map(row => new BAttachment(row)) SELECT attachments.*
[0]; FROM attachments
WHERE parentId = ?
AND attachmentId = ?
AND isDeleted = 0`, [this.noteId, attachmentId])
.map(row => new BAttachment(row))[0];
} }
/** /**

View File

@ -0,0 +1,101 @@
import TypeWidget from "./type_widget.js";
import server from "../../services/server.js";
import utils from "../../services/utils.js";
import AttachmentActionsWidget from "../buttons/attachments_actions.js";
import BasicWidget from "./basic_widget.js";
const TPL = `
<div class="attachment-detail">
<style>
.attachment-detail-wrapper {
margin-bottom: 20px;
}
.attachment-title-line {
display: flex;
align-items: baseline;
}
.attachment-details {
margin-left: 10px;
}
.attachment-content pre {
max-height: 400px;
background: var(--accented-background-color);
padding: 10px;
margin-top: 10px;
margin-bottom: 10px;
}
.attachment-content img {
margin: 10px;
max-height: 300px;
max-width: 90%;
object-fit: contain;
}
</style>
<div class="attachment-detail-wrapper"></div>
</div>`;
export default class AttachmentDetailWidget extends BasicWidget {
constructor(attachment) {
super();
this.attachment = attachment;
}
doRender() {
this.$widget = $(TPL);
this.$wrapper = this.$widget.find('.attachment-detail-wrapper');
super.doRender();
}
async doRefresh(note) {
this.$list.empty();
this.children = [];
const attachments = await server.get(`notes/${this.noteId}/attachments?includeContent=true`);
if (attachments.length === 0) {
this.$list.html("<strong>This note has no attachments.</strong>");
return;
}
for (const attachment of attachments) {
const attachmentActionsWidget = new AttachmentActionsWidget(attachment);
this.child(attachmentActionsWidget);
this.$list.append(
$('<div class="attachment-detail-wrapper">')
.append(
$('<div class="attachment-title-line">')
.append($('<h4>').append($('<span class="attachment-title">').text(attachment.title)))
.append(
$('<div class="attachment-details">')
.text(`Role: ${attachment.role}, Size: ${utils.formatSize(attachment.contentLength)}`)
)
.append($('<div style="flex: 1 1;">')) // spacer
.append(attachmentActionsWidget.render())
)
.append(
$('<div class="attachment-content">')
.append(this.renderContent(attachment))
)
);
}
}
renderContent(attachment) {
if (attachment.content) {
return $("<pre>").text(attachment.content);
} else if (attachment.role === 'image') {
return `<img src="api/notes/${attachment.parentId}/images/${attachment.attachmentId}/${encodeURIComponent(attachment.title)}?${attachment.utcDateModified}">`;
} else {
return '';
}
}
}

View File

@ -1,4 +1,5 @@
import BasicWidget from "../basic_widget.js"; import BasicWidget from "../basic_widget.js";
import server from "../../services/server.js";
const TPL = ` const TPL = `
<div class="dropdown attachment-actions"> <div class="dropdown attachment-actions">
@ -38,58 +39,9 @@ export default class AttachmentActionsWidget extends BasicWidget {
doRender() { doRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
// this.$findInTextButton = this.$widget.find('.find-in-text-button');
// this.$printActiveNoteButton = this.$widget.find('.print-active-note-button');
// this.$showSourceButton = this.$widget.find('.show-source-button');
// this.$renderNoteButton = this.$widget.find('.render-note-button');
//
// this.$exportNoteButton = this.$widget.find('.export-note-button');
// this.$exportNoteButton.on("click", () => {
// if (this.$exportNoteButton.hasClass("disabled")) {
// return;
// }
//
// this.triggerCommand("showExportDialog", {
// notePath: this.noteContext.notePath,
// defaultType: "single"
// });
// });
//
// this.$importNoteButton = this.$widget.find('.import-files-button');
// this.$importNoteButton.on("click", () => this.triggerCommand("showImportDialog", {noteId: this.noteId}));
//
// this.$widget.on('click', '.dropdown-item', () => this.$widget.find("[data-toggle='dropdown']").dropdown('toggle'));
//
// this.$openNoteExternallyButton = this.$widget.find(".open-note-externally-button");
//
// this.$deleteNoteButton = this.$widget.find(".delete-note-button");
// this.$deleteNoteButton.on("click", () => {
// if (this.note.noteId === 'root') {
// return;
// }
//
// branchService.deleteNotes([this.note.getParentBranches()[0].branchId], true);
// });
} }
refreshWithNote(note) { async deleteAttachmentCommand() {
// this.toggleDisabled(this.$findInTextButton, ['text', 'code', 'book', 'search'].includes(note.type)); await server.remove(`notes/${this.attachment.parentId}/attachments/${this.attachment.attachmentId}`);
//
// this.toggleDisabled(this.$showSourceButton, ['text', 'relationMap', 'mermaid'].includes(note.type));
//
// this.toggleDisabled(this.$printActiveNoteButton, ['text', 'code'].includes(note.type));
//
// this.$renderNoteButton.toggle(note.type === 'render');
//
// this.$openNoteExternallyButton.toggle(utils.isElectron());
}
toggleDisabled($el, enable) {
if (enable) {
$el.removeAttr('disabled');
} else {
$el.attr('disabled', 'disabled');
}
} }
} }

View File

@ -65,7 +65,7 @@ export default class AttachmentsTypeWidget extends TypeWidget {
} }
for (const attachment of attachments) { for (const attachment of attachments) {
const attachmentActionsWidget = new AttachmentActionsWidget(); const attachmentActionsWidget = new AttachmentActionsWidget(attachment);
this.child(attachmentActionsWidget); this.child(attachmentActionsWidget);
this.$list.append( this.$list.append(

View File

@ -175,6 +175,22 @@ function saveAttachment(req) {
note.saveAttachment({attachmentId, role, mime, title, content}); note.saveAttachment({attachmentId, role, mime, title, content});
} }
function deleteAttachment(req) {
const {noteId, attachmentId} = req.params;
const note = becca.getNote(noteId);
if (!note) {
throw new NotFoundError(`Note '${noteId}' doesn't exist.`);
}
const attachment = note.getAttachmentById(attachmentId);
if (attachment) {
attachment.markAsDeleted();
}
}
function getRelationMap(req) { function getRelationMap(req) {
const {relationMapNoteId, noteIds} = req.body; const {relationMapNoteId, noteIds} = req.body;
@ -390,5 +406,6 @@ module.exports = {
uploadModifiedFile, uploadModifiedFile,
forceSaveNoteRevision, forceSaveNoteRevision,
getAttachments, getAttachments,
saveAttachment saveAttachment,
deleteAttachment
}; };

View File

@ -128,6 +128,7 @@ function register(app) {
apiRoute(PUT, '/api/notes/:noteId/type', notesApiRoute.setNoteTypeMime); apiRoute(PUT, '/api/notes/:noteId/type', notesApiRoute.setNoteTypeMime);
apiRoute(GET, '/api/notes/:noteId/attachments', notesApiRoute.getAttachments); apiRoute(GET, '/api/notes/:noteId/attachments', notesApiRoute.getAttachments);
apiRoute(POST, '/api/notes/:noteId/attachments', notesApiRoute.saveAttachment); apiRoute(POST, '/api/notes/:noteId/attachments', notesApiRoute.saveAttachment);
apiRoute(DELETE, '/api/notes/:noteId/attachments/:attachmentId', notesApiRoute.deleteAttachment);
apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions); apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions);
apiRoute(DELETE, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.eraseAllNoteRevisions); apiRoute(DELETE, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.eraseAllNoteRevisions);
apiRoute(GET, '/api/notes/:noteId/revisions/:noteRevisionId', noteRevisionsApiRoute.getNoteRevision); apiRoute(GET, '/api/notes/:noteId/revisions/:noteRevisionId', noteRevisionsApiRoute.getNoteRevision);