ancillary type widget

This commit is contained in:
zadam 2023-02-15 14:32:12 +01:00
parent b1e2b5ba8e
commit 520ffecd36
11 changed files with 134 additions and 9 deletions

View File

@ -146,8 +146,7 @@ class BNoteAncillary extends AbstractBeccaEntity {
isProtected: !!this.isProtected,
contentCheckSum: this.contentCheckSum,
isDeleted: false,
utcDateModified: this.utcDateModified,
content: this.content,
utcDateModified: this.utcDateModified
};
}

View File

@ -166,14 +166,12 @@ class BNoteRevision extends AbstractBeccaEntity {
utcDateLastEdited: this.utcDateLastEdited,
utcDateCreated: this.utcDateCreated,
utcDateModified: this.utcDateModified,
content: this.content,
contentLength: this.contentLength
};
}
getPojoToSave() {
const pojo = this.getPojo();
delete pojo.content; // not getting persisted
delete pojo.contentLength; // not getting persisted
if (pojo.isProtected) {

View File

@ -117,11 +117,19 @@ export default class RootCommandExecutor extends Component {
});
}
async showNoteSourceEvent() {
async showNoteSourceCommand() {
const notePath = appContext.tabManager.getActiveContextNotePath();
if (notePath) {
await appContext.tabManager.openContextWithNote(notePath, { activate: true, viewMode: 'source' });
}
}
async showNoteAncillariesCommand() {
const notePath = appContext.tabManager.getActiveContextNotePath();
if (notePath) {
await appContext.tabManager.openContextWithNote(notePath, { activate: true, viewMode: 'ancillaries' });
}
}
}

View File

@ -28,6 +28,7 @@ const TPL = `
<a data-trigger-command="renderActiveNote" class="dropdown-item render-note-button"><kbd data-command="renderActiveNote"></kbd> Re-render note</a>
<a data-trigger-command="findInText" class="dropdown-item find-in-text-button">Search in note <kbd data-command="findInText"></a>
<a data-trigger-command="showNoteSource" class="dropdown-item show-source-button"><kbd data-command="showNoteSource"></kbd> Note source</a>
<a data-trigger-command="showNoteAncillaries" class="dropdown-item show-source-button"><kbd data-command="showNoteAncillaries"></kbd> Note ancillaries</a>
<a data-trigger-command="openNoteExternally" class="dropdown-item open-note-externally-button"><kbd data-command="openNoteExternally"></kbd> Open note externally</a>
<a class="dropdown-item import-files-button">Import files</a>
<a class="dropdown-item export-note-button">Export note</a>

View File

@ -12,7 +12,8 @@ export default class MermaidExportButton extends NoteContextAwareWidget {
isEnabled() {
return super.isEnabled()
&& this.note?.type === 'mermaid'
&& this.note.isContentAvailable();
&& this.note.isContentAvailable()
&& this.noteContext?.viewScope.viewMode === 'default';
}
doRender() {

View File

@ -36,7 +36,8 @@ export default class MermaidWidget extends NoteContextAwareWidget {
isEnabled() {
return super.isEnabled()
&& this.note?.type === 'mermaid'
&& this.note.isContentAvailable();
&& this.note.isContentAvailable()
&& this.noteContext?.viewScope.viewMode === 'default';
}
doRender() {

View File

@ -27,6 +27,7 @@ import NoteMapTypeWidget from "./type_widgets/note_map.js";
import WebViewTypeWidget from "./type_widgets/web_view.js";
import DocTypeWidget from "./type_widgets/doc.js";
import ContentWidgetTypeWidget from "./type_widgets/content_widget.js";
import AncillariesTypeWidget from "./type_widgets/ancillaries.js";
const TPL = `
<div class="note-detail">
@ -61,7 +62,8 @@ const typeWidgetClasses = {
'noteMap': NoteMapTypeWidget,
'webView': WebViewTypeWidget,
'doc': DocTypeWidget,
'contentWidget': ContentWidgetTypeWidget
'contentWidget': ContentWidgetTypeWidget,
'ancillaries': AncillariesTypeWidget
};
export default class NoteDetailWidget extends NoteContextAwareWidget {
@ -189,6 +191,8 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
if (type === 'text' && this.noteContext.viewScope.viewMode === 'source') {
type = 'readOnlyCode';
} else if (this.noteContext.viewScope.viewMode === 'ancillaries') {
type = 'ancillaries';
} else if (type === 'text' && await this.noteContext.isReadOnly()) {
type = 'readOnlyText';
} else if ((type === 'code' || type === 'mermaid') && await this.noteContext.isReadOnly()) {

View File

@ -460,7 +460,9 @@ export default class TabRowWidget extends BasicWidget {
}
updateTitle($tab, title) {
$tab.find('.note-tab-title').text(title);
$tab.attr("title", title)
$tab.find('.note-tab-title')
.text(title);
}
getTabById(ntxId) {

View File

@ -0,0 +1,79 @@
import TypeWidget from "./type_widget.js";
import server from "../../services/server.js";
const TPL = `
<div class="note-ancillaries note-detail-printable">
<style>
.note-ancillaries {
padding: 15px;
}
.ancillary-content {
max-height: 400px;
background: var(--accented-background-color);
padding: 10px;
margin-top: 10px;
margin-bottom: 10px;
}
.ancillary-details th {
padding-left: 10px;
padding-right: 10px;
}
</style>
<div class="alert alert-info" style="margin: 10px 0 10px 0; padding: 20px;">
Note ancillaries are pieces of data attached to a given note, providing ancillary support.
This view is useful for diagnostics.
</div>
<div class="note-ancillary-list"></div>
</div>`;
export default class AncillariesTypeWidget extends TypeWidget {
static getType() { return "ancillaries"; }
doRender() {
this.$widget = $(TPL);
this.$list = this.$widget.find('.note-ancillary-list');
super.doRender();
}
async doRefresh(note) {
this.$list.empty();
const ancillaries = await server.get(`notes/${this.noteId}/ancillaries?includeContent=true`);
if (ancillaries.length === 0) {
this.$list.html("<strong>This note has no ancillaries.</strong>");
return;
}
for (const ancillary of ancillaries) {
this.$list.append(
$('<div class="note-ancillary-wrapper">')
.append(
$('<h4>').append($('<span class="ancillary-name">').text(ancillary.name))
)
.append(
$('<table class="ancillary-details">')
.append(
$('<tr>')
.append($('<th>').text('Length:'))
.append($('<td>').text(ancillary.contentLength))
.append($('<th>').text('MIME:'))
.append($('<td>').text(ancillary.mime))
.append($('<th>').text('Date modified:'))
.append($('<td>').text(ancillary.utcDateModified))
)
)
.append(
$('<pre class="ancillary-content">')
.text(ancillary.content)
)
);
}
}
}

View File

@ -127,6 +127,36 @@ function setNoteTypeMime(req) {
note.save();
}
function getNoteAncillaries(req) {
const includeContent = req.query.includeContent === 'true';
const {noteId} = req.params;
const note = becca.getNote(noteId);
if (!note) {
throw new NotFoundError(`Note '${noteId}' doesn't exist.`);
}
const noteAncillaries = note.getNoteAncillaries();
return noteAncillaries.map(ancillary => {
const pojo = ancillary.getPojo();
if (includeContent && utils.isStringNote(null, ancillary.mime)) {
pojo.content = ancillary.getContent()?.toString();
pojo.contentLength = pojo.content.length;
const MAX_ANCILLARY_LENGTH = 1_000_000;
if (pojo.content.length > MAX_ANCILLARY_LENGTH) {
pojo.content = pojo.content.substring(0, MAX_ANCILLARY_LENGTH);
}
}
return pojo;
});
}
function saveNoteAncillary(req) {
const {noteId, name} = req.params;
const {mime, content} = req.body;
@ -354,5 +384,6 @@ module.exports = {
getDeleteNotesPreview,
uploadModifiedFile,
forceSaveNoteRevision,
getNoteAncillaries,
saveNoteAncillary
};

View File

@ -126,6 +126,7 @@ function register(app) {
apiRoute(PUT, '/api/notes/:noteId/sort-children', notesApiRoute.sortChildNotes);
apiRoute(PUT, '/api/notes/:noteId/protect/:isProtected', notesApiRoute.protectNote);
apiRoute(PUT, '/api/notes/:noteId/type', notesApiRoute.setNoteTypeMime);
apiRoute(GET, '/api/notes/:noteId/ancillaries', notesApiRoute.getNoteAncillaries);
apiRoute(PUT, '/api/notes/:noteId/ancillaries/:name', notesApiRoute.saveNoteAncillary);
apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions);
apiRoute(DELETE, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.eraseAllNoteRevisions);