attachment improvements

This commit is contained in:
zadam 2023-03-24 10:57:32 +01:00
parent fd8a2d4d92
commit 9be524ef89
8 changed files with 187 additions and 49 deletions

View File

@ -114,6 +114,17 @@ function formatLabel(label) {
return str; return str;
} }
function formatSize(size) {
size = Math.max(Math.round(size / 1024), 1);
if (size < 1024) {
return `${size} KiB`;
}
else {
return `${Math.round(size / 102.4) / 10} MiB`;
}
}
function toObject(array, fn) { function toObject(array, fn) {
const obj = {}; const obj = {};
@ -363,6 +374,7 @@ export default {
formatDate, formatDate,
formatDateISO, formatDateISO,
formatDateTime, formatDateTime,
formatSize,
localNowDateTime, localNowDateTime,
now, now,
isElectron, isElectron,

View File

@ -0,0 +1,95 @@
import BasicWidget from "../basic_widget.js";
const TPL = `
<div class="dropdown attachment-actions">
<style>
.attachment-actions {
width: 35px;
height: 35px;
}
.attachment-actions .dropdown-menu {
width: 15em;
}
.attachment-actions .dropdown-item[disabled], .attachment-actions .dropdown-item[disabled]:hover {
color: var(--muted-text-color) !important;
background-color: transparent !important;
pointer-events: none; /* makes it unclickable */
}
</style>
<button type="button" data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false" class="icon-action icon-action-always-border bx bx-dots-vertical-rounded"></button>
<div class="dropdown-menu dropdown-menu-right">
<a data-trigger-command="deleteAttachment" class="dropdown-item delete-attachment-button">Delete attachment</a>
<a data-trigger-command="pullAttachmentIntoNote" class="dropdown-item pull-attachment-into-note-button">Pull attachment into note</a>
<a data-trigger-command="pullAttachmentIntoNote" class="dropdown-item pull-attachment-into-note-button">Copy into clipboard</a>
</div>
</div>`;
export default class AttachmentActionsWidget extends BasicWidget {
constructor(attachment) {
super();
this.attachment = attachment;
}
doRender() {
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) {
// this.toggleDisabled(this.$findInTextButton, ['text', 'code', 'book', 'search'].includes(note.type));
//
// 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

@ -85,6 +85,11 @@ export default class BacklinksWidget extends NoteContextAwareWidget {
async refreshWithNote(note) { async refreshWithNote(note) {
this.clearItems(); this.clearItems();
if (this.noteContext?.viewScope?.viewMode !== 'default') {
this.toggle(false);
return;
}
// can't use froca since that would count only relations from loaded notes // can't use froca since that would count only relations from loaded notes
const resp = await server.get(`note-map/${this.noteId}/backlink-count`); const resp = await server.get(`note-map/${this.noteId}/backlink-count`);

View File

@ -70,10 +70,16 @@ export default class NoteTitleWidget extends NoteContextAwareWidget {
} }
async refreshWithNote(note) { async refreshWithNote(note) {
this.$noteTitle.val(note.title); const viewMode = this.noteContext.viewScope.viewMode;
this.$noteTitle.val(viewMode === 'default'
? note.title
: `${viewMode}: ${note.title}`);
this.$noteTitle.prop("readonly", (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) this.$noteTitle.prop("readonly",
|| ['_lbRoot', '_lbAvailableLaunchers', '_lbVisibleLaunchers'].includes(note.noteId)); (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable())
|| ['_lbRoot', '_lbAvailableLaunchers', '_lbVisibleLaunchers'].includes(note.noteId)
|| viewMode !== 'default'
);
this.setProtectedStatus(note); this.setProtectedStatus(note);
} }

View File

@ -1,5 +1,6 @@
import NoteContextAwareWidget from "../note_context_aware_widget.js"; import NoteContextAwareWidget from "../note_context_aware_widget.js";
import server from "../../services/server.js"; import server from "../../services/server.js";
import utils from "../../services/utils.js";
const TPL = ` const TPL = `
<div class="note-info-widget"> <div class="note-info-widget">
@ -105,12 +106,12 @@ export default class NoteInfoWidget extends NoteContextAwareWidget {
this.$subTreeSize.empty().append($('<span class="bx bx-loader bx-spin"></span>')); this.$subTreeSize.empty().append($('<span class="bx bx-loader bx-spin"></span>'));
const noteSizeResp = await server.get(`stats/note-size/${this.noteId}`); const noteSizeResp = await server.get(`stats/note-size/${this.noteId}`);
this.$noteSize.text(this.formatSize(noteSizeResp.noteSize)); this.$noteSize.text(utils.formatSize(noteSizeResp.noteSize));
const subTreeResp = await server.get(`stats/subtree-size/${this.noteId}`); const subTreeResp = await server.get(`stats/subtree-size/${this.noteId}`);
if (subTreeResp.subTreeNoteCount > 1) { if (subTreeResp.subTreeNoteCount > 1) {
this.$subTreeSize.text(`(subtree size: ${this.formatSize(subTreeResp.subTreeSize)} in ${subTreeResp.subTreeNoteCount} notes)`); this.$subTreeSize.text(`(subtree size: ${utils.formatSize(subTreeResp.subTreeSize)} in ${subTreeResp.subTreeNoteCount} notes)`);
} }
else { else {
this.$subTreeSize.text(""); this.$subTreeSize.text("");
@ -143,17 +144,6 @@ export default class NoteInfoWidget extends NoteContextAwareWidget {
this.$noteSizesWrapper.hide(); this.$noteSizesWrapper.hide();
} }
formatSize(size) {
size = Math.max(Math.round(size / 1024), 1);
if (size < 1024) {
return `${size} KiB`;
}
else {
return `${Math.round(size / 102.4) / 10} MiB`;
}
}
entitiesReloadedEvent({loadResults}) { entitiesReloadedEvent({loadResults}) {
if (loadResults.isNoteReloaded(this.noteId) || loadResults.isNoteContentReloaded(this.noteId)) { if (loadResults.isNoteReloaded(this.noteId) || loadResults.isNoteContentReloaded(this.noteId)) {
this.refresh(); this.refresh();

View File

@ -1,14 +1,29 @@
import TypeWidget from "./type_widget.js"; import TypeWidget from "./type_widget.js";
import server from "../../services/server.js"; import server from "../../services/server.js";
import utils from "../../services/utils.js";
import AttachmentActionsWidget from "../buttons/attachments_actions.js";
const TPL = ` const TPL = `
<div class="note-attachments note-detail-printable"> <div class="attachments note-detail-printable">
<style> <style>
.note-attachments { .attachments {
padding: 15px; padding: 15px;
} }
.attachment-content { .attachment-wrapper {
margin-bottom: 20px;
}
.attachment-title-line {
display: flex;
align-items: baseline;
}
.attachment-details {
margin-left: 10px;
}
.attachment-content pre {
max-height: 400px; max-height: 400px;
background: var(--accented-background-color); background: var(--accented-background-color);
padding: 10px; padding: 10px;
@ -16,18 +31,15 @@ const TPL = `
margin-bottom: 10px; margin-bottom: 10px;
} }
.attachment-details th { .attachment-content img {
padding-left: 10px; margin: 10px;
padding-right: 10px; max-height: 300px;
max-width: 90%;
object-fit: contain;
} }
</style> </style>
<div class="alert alert-info" style="margin: 10px 0 10px 0; padding: 20px;"> <div class="attachment-list"></div>
Note attachments are pieces of data attached to a given note, providing attachment support.
This view is useful for diagnostics.
</div>
<div class="note-attachment-list"></div>
</div>`; </div>`;
export default class AttachmentsTypeWidget extends TypeWidget { export default class AttachmentsTypeWidget extends TypeWidget {
@ -35,13 +47,14 @@ export default class AttachmentsTypeWidget extends TypeWidget {
doRender() { doRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
this.$list = this.$widget.find('.note-attachment-list'); this.$list = this.$widget.find('.attachment-list');
super.doRender(); super.doRender();
} }
async doRefresh(note) { async doRefresh(note) {
this.$list.empty(); this.$list.empty();
this.children = [];
const attachments = await server.get(`notes/${this.noteId}/attachments?includeContent=true`); const attachments = await server.get(`notes/${this.noteId}/attachments?includeContent=true`);
@ -52,28 +65,36 @@ export default class AttachmentsTypeWidget extends TypeWidget {
} }
for (const attachment of attachments) { for (const attachment of attachments) {
const attachmentActionsWidget = new AttachmentActionsWidget();
this.child(attachmentActionsWidget);
this.$list.append( this.$list.append(
$('<div class="note-attachment-wrapper">') $('<div class="attachment-wrapper">')
.append( .append(
$('<h4>').append($('<span class="attachment-name">').text(attachment.name)) $('<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( .append(
$('<table class="attachment-details">') $('<div class="attachment-content">')
.append( .append(this.renderContent(attachment))
$('<tr>')
.append($('<th>').text('Length:'))
.append($('<td>').text(attachment.contentLength))
.append($('<th>').text('MIME:'))
.append($('<td>').text(attachment.mime))
.append($('<th>').text('Date modified:'))
.append($('<td>').text(attachment.utcDateModified))
)
)
.append(
$('<pre class="attachment-content">')
.text(attachment.content)
) )
); );
} }
} }
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

@ -119,6 +119,10 @@ button.close:hover {
border-radius: var(--button-border-radius); border-radius: var(--button-border-radius);
} }
.icon-action-always-border {
border-color: var(--button-border-color);
}
.icon-action:hover:not(.disabled) { .icon-action:hover:not(.disabled) {
text-decoration: none; text-decoration: none;
border-color: var(--button-border-color); border-color: var(--button-border-color);

View File

@ -142,7 +142,8 @@ function getAttachments(req) {
return attachments.map(attachment => { return attachments.map(attachment => {
const pojo = attachment.getPojo(); const pojo = attachment.getPojo();
if (includeContent && utils.isStringNote(null, attachment.mime)) { if (includeContent) {
if (utils.isStringNote(null, attachment.mime)) {
pojo.content = attachment.getContent()?.toString(); pojo.content = attachment.getContent()?.toString();
pojo.contentLength = pojo.content.length; pojo.contentLength = pojo.content.length;
@ -151,6 +152,10 @@ function getAttachments(req) {
if (pojo.content.length > MAX_ATTACHMENT_LENGTH) { if (pojo.content.length > MAX_ATTACHMENT_LENGTH) {
pojo.content = pojo.content.substring(0, MAX_ATTACHMENT_LENGTH); pojo.content = pojo.content.substring(0, MAX_ATTACHMENT_LENGTH);
} }
} else {
const content = attachment.getContent();
pojo.contentLength = content?.length;
}
} }
return pojo; return pojo;