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;
}
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) {
const obj = {};
@ -363,6 +374,7 @@ export default {
formatDate,
formatDateISO,
formatDateTime,
formatSize,
localNowDateTime,
now,
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) {
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
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) {
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())
|| ['_lbRoot', '_lbAvailableLaunchers', '_lbVisibleLaunchers'].includes(note.noteId));
this.$noteTitle.prop("readonly",
(note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable())
|| ['_lbRoot', '_lbAvailableLaunchers', '_lbVisibleLaunchers'].includes(note.noteId)
|| viewMode !== 'default'
);
this.setProtectedStatus(note);
}

View File

@ -1,5 +1,6 @@
import NoteContextAwareWidget from "../note_context_aware_widget.js";
import server from "../../services/server.js";
import utils from "../../services/utils.js";
const TPL = `
<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>'));
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}`);
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 {
this.$subTreeSize.text("");
@ -143,17 +144,6 @@ export default class NoteInfoWidget extends NoteContextAwareWidget {
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}) {
if (loadResults.isNoteReloaded(this.noteId) || loadResults.isNoteContentReloaded(this.noteId)) {
this.refresh();

View File

@ -1,14 +1,29 @@
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";
const TPL = `
<div class="note-attachments note-detail-printable">
<div class="attachments note-detail-printable">
<style>
.note-attachments {
.attachments {
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;
background: var(--accented-background-color);
padding: 10px;
@ -16,18 +31,15 @@ const TPL = `
margin-bottom: 10px;
}
.attachment-details th {
padding-left: 10px;
padding-right: 10px;
.attachment-content img {
margin: 10px;
max-height: 300px;
max-width: 90%;
object-fit: contain;
}
</style>
<div class="alert alert-info" style="margin: 10px 0 10px 0; padding: 20px;">
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 class="attachment-list"></div>
</div>`;
export default class AttachmentsTypeWidget extends TypeWidget {
@ -35,13 +47,14 @@ export default class AttachmentsTypeWidget extends TypeWidget {
doRender() {
this.$widget = $(TPL);
this.$list = this.$widget.find('.note-attachment-list');
this.$list = this.$widget.find('.attachment-list');
super.doRender();
}
async doRefresh(note) {
this.$list.empty();
this.children = [];
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) {
const attachmentActionsWidget = new AttachmentActionsWidget();
this.child(attachmentActionsWidget);
this.$list.append(
$('<div class="note-attachment-wrapper">')
$('<div class="attachment-wrapper">')
.append(
$('<h4>').append($('<span class="attachment-name">').text(attachment.name))
)
.append(
$('<table class="attachment-details">')
$('<div class="attachment-title-line">')
.append($('<h4>').append($('<span class="attachment-title">').text(attachment.title)))
.append(
$('<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))
$('<div class="attachment-details">')
.text(`Role: ${attachment.role}, Size: ${utils.formatSize(attachment.contentLength)}`)
)
.append($('<div style="flex: 1 1;">')) // spacer
.append(attachmentActionsWidget.render())
)
.append(
$('<pre class="attachment-content">')
.text(attachment.content)
$('<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

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

View File

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