component => type widget refactoring

This commit is contained in:
zadam 2020-01-19 11:03:34 +01:00
parent a99c016818
commit 746181689f
25 changed files with 114 additions and 126 deletions

View File

@ -141,7 +141,7 @@ async function printActiveNote() {
await libraryLoader.requireLibrary(libraryLoader.PRINT_THIS);
$tabContext.$tabContent.find('.note-detail-component:visible').printThis({
$tabContext.$tabContent.find('.note-detail-printable:visible').printThis({
header: $("<h2>").text($tabContext.note && $tabContext.note.title).prop('outerHTML') ,
importCSS: false,
loadCSS: [

View File

@ -60,6 +60,11 @@ class TabContext extends Component {
bundleService.executeRelationBundles(this.note, 'runOnNoteView', this);
if (this.note.isProtected && protectedSessionHolder.isProtectedSessionAvailable()) {
// FIXME: there are probably more places where this should be done
protectedSessionHolder.touchProtectedSession();
}
this.trigger('tabNoteSwitched', {tabId: this.tabId});
}

View File

@ -16,25 +16,25 @@ const TPL = `
</div>
`;
const componentClasses = {
'empty': "./detail/note_detail_empty.js",
'text': "./detail/note_detail_text.js",
'code': "./detail/note_detail_code.js",
'file': "./detail/note_detail_file.js",
'image': "./detail/note_detail_image.js",
'search': "./detail/note_detail_search.js",
'render': "./detail/note_detail_render.js",
'relation-map': "./detail/note_detail_relation_map.js",
'protected-session': "./detail/note_detail_protected_session.js",
'book': "./detail/note_detail_book.js"
const typeWidgetClasses = {
'empty': "./type_widgets/note_detail_empty.js",
'text': "./type_widgets/text.js",
'code': "./type_widgets/code.js",
'file': "./type_widgets/file.js",
'image': "./type_widgets/note_detail_image.js",
'search': "./type_widgets/note_detail_search.js",
'render': "./type_widgets/note_detail_render.js",
'relation-map': "./type_widgets/note_detail_relation_map.js",
'protected-session': "./type_widgets/note_detail_protected_session.js",
'book': "./type_widgets/note_detail_book.js"
};
export default class NoteDetailWidget extends TabAwareWidget {
constructor(appContext) {
super(appContext);
this.components = {};
this.componentPromises = {};
this.typeWidgets = {};
this.typeWidgetPromises = {};
}
doRender() {
@ -43,18 +43,23 @@ export default class NoteDetailWidget extends TabAwareWidget {
return this.$widget;
}
async noteSwitched() {
await this.initComponent(/**disableAutoBook*/);
async refresh() {
this.type = this.getWidgetType(/*disableAutoBook*/);
for (const componentType in this.components) {
if (componentType !== this.type) {
this.components[componentType].cleanup();
if (!(this.type in this.typeWidgetPromises)) {
this.typeWidgetPromises[this.type] = this.initWidgetType(this.type);
}
await this.typeWidgetPromises[this.type];
for (const typeWidget of Object.values(this.typeWidgets)) {
if (typeWidget.constructor.getType() !== this.type) {
typeWidget.cleanup();
typeWidget.toggle(false);
}
}
this.$widget.find('.note-detail-component').hide();
this.getComponent().show();
this.getTypeWidget().toggle(true);
this.setupClasses();
}
@ -75,36 +80,26 @@ export default class NoteDetailWidget extends TabAwareWidget {
this.$widget.toggleClass("protected", note.isProtected);
}
getComponent() {
if (!this.components[this.type]) {
throw new Error("Could not find component for type: " + this.type);
getTypeWidget() {
if (!this.typeWidgets[this.type]) {
throw new Error("Could not find typeWidget for type: " + this.type);
}
return this.components[this.type];
}
async initComponent(disableAutoBook = false) {
this.type = this.getComponentType(disableAutoBook);
if (!(this.type in this.componentPromises)) {
this.componentPromises[this.type] = this.reallyInitComponent(this.type);
}
await this.componentPromises[this.type];
return this.typeWidgets[this.type];
}
async reallyInitComponent(type) {
const clazz = await import(componentClasses[type]);
async initWidgetType(type) {
const clazz = await import(typeWidgetClasses[type]);
this.components[this.type] = new clazz.default(this.appContext);
this.children.push(this.components[this.type]);
this.typeWidgets[this.type] = new clazz.default(this.appContext);
this.children.push(this.typeWidgets[this.type]);
this.components[this.type].renderTo(this.$widget);
this.typeWidgets[this.type].renderTo(this.$widget);
this.components[this.type].eventReceived('setTabContext', {tabContext: this.tabContext});
this.typeWidgets[this.type].eventReceived('setTabContext', {tabContext: this.tabContext});
}
getComponentType(disableAutoBook) {
getWidgetType(disableAutoBook) {
const note = this.tabContext.note;
if (!note) {
@ -113,20 +108,15 @@ export default class NoteDetailWidget extends TabAwareWidget {
let type = note.type;
if (type === 'text' && !disableAutoBook && utils.isHtmlEmpty(note.content) && note.hasChildren()) {
if (type === 'text' && !disableAutoBook
&& utils.isHtmlEmpty(note.content)
&& note.hasChildren()) {
type = 'book';
}
if (note.isProtected) {
if (protectedSessionHolder.isProtectedSessionAvailable()) {
protectedSessionHolder.touchProtectedSession();
} else {
type = 'protected-session';
// FIXME
// user shouldn't be able to edit note title
//this.$noteTitle.prop("readonly", true);
}
if (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) {
type = 'protected-session';
}
return type;

View File

@ -135,6 +135,10 @@ export default class NoteTitleWidget extends TabAwareWidget {
this.$noteTitle.val(note.title);
if (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) {
this.$noteTitle.prop("readonly", true);
}
this.$protectButton.toggleClass("active", note.isProtected);
this.$protectButton.prop("disabled", note.isProtected);
this.$unprotectButton.toggleClass("active", !note.isProtected);

View File

@ -4,14 +4,16 @@ import toastService from "../../services/toast.js";
import server from "../../services/server.js";
import noteDetailService from "../../services/note_detail.js";
import keyboardActionService from "../../services/keyboard_actions.js";
import TabAwareWidget from "../tab_aware_widget.js";
import TypeWidget from "./type_widget.js";
const TPL = `
<div class="note-detail-code note-detail-component">
<div class="note-detail-code note-detail-printable">
<div class="note-detail-code-editor"></div>
</div>`;
class NoteDetailCode extends TabAwareWidget {
class CodeTypeWidget extends TypeWidget {
static getType() { return "code"; }
doRender() {
this.$widget = $(TPL);
this.$editor = this.$widget.find('.note-detail-code-editor');
@ -58,22 +60,19 @@ class NoteDetailCode extends TabAwareWidget {
//this.onNoteChange(() => this.tabContext.noteChanged());
}
refresh() {
// lazy loading above can take time and tab might have been already switched to another note
if (this.tabContext.note && this.tabContext.note.type === 'code') {
// CodeMirror breaks pretty badly on null so even though it shouldn't happen (guarded by consistency check)
// we provide fallback
this.codeEditor.setValue(this.tabContext.note.content || "");
doRefresh() {
// CodeMirror breaks pretty badly on null so even though it shouldn't happen (guarded by consistency check)
// we provide fallback
this.codeEditor.setValue(this.tabContext.note.content || "");
const info = CodeMirror.findModeByMIME(this.tabContext.note.mime);
const info = CodeMirror.findModeByMIME(this.tabContext.note.mime);
if (info) {
this.codeEditor.setOption("mode", info.mime);
CodeMirror.autoLoadMode(this.codeEditor, info.mode);
}
this.show();
if (info) {
this.codeEditor.setOption("mode", info.mime);
CodeMirror.autoLoadMode(this.codeEditor, info.mode);
}
this.show();
}
show() {
@ -127,4 +126,4 @@ class NoteDetailCode extends TabAwareWidget {
}
}
export default NoteDetailCode;
export default CodeTypeWidget;

View File

@ -2,10 +2,10 @@ import utils from "../../services/utils.js";
import server from "../../services/server.js";
import toastService from "../../services/toast.js";
import noteDetailService from "../../services/note_detail.js";
import TabAwareWidget from "../tab_aware_widget.js";
import TypeWidget from "./type_widget.js";
const TPL = `
<div class="note-detail-file note-detail-component">
<div class="note-detail-file note-detail-printable">
<style>
.note-detail-file {
padding: 10px;
@ -55,7 +55,9 @@ const TPL = `
<input type="file" class="file-upload-new-revision-input" style="display: none">
</div>`;
class NoteDetailFile extends TabAwareWidget{
class FileTypeWidget extends TypeWidget {
static getType() { return "file"; }
doRender() {
this.$widget = $(TPL);
this.$fileNoteId = this.$widget.find(".file-note-id");
@ -115,7 +117,7 @@ class NoteDetailFile extends TabAwareWidget{
return this.$widget;
}
async refresh() {
async doRefresh() {
const attributes = await server.get('notes/' + this.tabContext.note.noteId + '/attributes');
const attributeMap = utils.toObject(attributes, l => [l.name, l.value]);
@ -155,4 +157,4 @@ class NoteDetailFile extends TabAwareWidget{
scrollToTop() {}
}
export default NoteDetailFile;
export default FileTypeWidget;

View File

@ -3,6 +3,7 @@ import treeService from '../../services/tree.js';
import noteAutocompleteService from '../../services/note_autocomplete.js';
import mimeTypesService from '../../services/mime_types.js';
import TabAwareWidget from "../tab_aware_widget.js";
import TypeWidget from "./type_widget.js";
const ENABLE_INSPECTOR = false;
@ -41,7 +42,7 @@ const mentionSetup = {
};
const TPL = `
<div class="note-detail-text note-detail-component">
<div class="note-detail-text note-detail-printable">
<style>
.note-detail-text h1 { font-size: 2.0em; }
.note-detail-text h2 { font-size: 1.8em; }
@ -72,7 +73,9 @@ const TPL = `
</div>
`;
class NoteDetailText extends TabAwareWidget {
class TextTypeWidget extends TypeWidget {
static getType() { return "text"; }
doRender() {
this.$widget = $(TPL);
this.$editor = this.$widget.find('.note-detail-text-editor');
@ -130,15 +133,12 @@ class NoteDetailText extends TabAwareWidget {
}
}
async refresh() {
// lazy loading above can take time and tab might have been already switched to another note
if (this.tabContext.note && this.tabContext.note.type === 'text') {
this.textEditor.isReadOnly = await this.isReadOnly();
async doRefresh() {
this.textEditor.isReadOnly = await this.isReadOnly();
this.$widget.show();
this.$widget.show();
this.textEditor.setData(this.tabContext.note.content);
}
this.textEditor.setData(this.tabContext.note.content);
}
getContent() {
@ -188,4 +188,4 @@ class NoteDetailText extends TabAwareWidget {
}
}
export default NoteDetailText;
export default TextTypeWidget;

View File

@ -0,0 +1,18 @@
import TabAwareWidget from "../tab_aware_widget.js";
export default class TypeWidget extends TabAwareWidget {
// for overriding
static getType() {}
doRefresh() {}
refresh() {
if (!this.tabContext.note || this.tabContext.note.type !== this.constructor.getType()) {
this.toggle(false);
return;
}
this.doRefresh();
}
}

View File

@ -104,15 +104,4 @@ span.fancytree-expander {
margin-top: 4px;
border-width: 2px;
border-style: solid;
}
.note-detail-component-wrapper {
position: relative;
overflow: auto;
flex-direction: column;
/* for some reason detail overflows a little bit so we subtract few pixels */
height: calc(100% - 25px);
/* large left padding is necessary for ckeditor gutter in detail-only (smartphone) layout */
padding-left: 35px;
padding-top: 10px;
}

View File

@ -100,25 +100,6 @@ span.fancytree-node.muted { opacity: 0.6; }
width: 100%;
}
.note-detail-component-wrapper {
flex-grow: 100;
position: relative;
overflow: auto;
flex-basis: content;
height: 100%;
display: flex;
flex-direction: column;
min-height: 500px;
overflow-wrap: break-word; /* otherwise CKEditor fails miserably on super long lines */
font-family: var(--detail-font-family);
font-size: var(--detail-font-size);
}
.note-detail-component {
flex-grow: 100;
display: none;
}
/** we disable shield background when in zen mode because I couldn't get it to stay static
(it kept growing with content) */
#container:not(.zen-mode) .note-tab-content.protected {

View File

@ -1,4 +1,4 @@
<div class="note-detail-book note-detail-component">
<div class="note-detail-book note-detail-printable">
<div class="btn-group floating-button" style="right: 20px; top: 20px;">
<button type="button"
class="expand-children-button btn icon-button bx bx-move-vertical"

View File

@ -1,4 +1,4 @@
<div class="note-detail-empty note-detail-component">
<div class="note-detail-empty note-detail-printable">
<div class="form-group">
<label>Open note by typing note's title into input below or choose a note in the tree.</label>
<div class="input-group">

View File

@ -1,4 +1,4 @@
<div class="note-detail-image note-detail-component">
<div class="note-detail-image note-detail-printable">
<div style="display: flex; justify-content: space-evenly; margin: 10px;">
<button class="image-download btn btn-sm btn-primary" type="button">Download</button>

View File

@ -1,4 +1,4 @@
<div class="protected-session-password-component note-detail-component">
<div class="protected-session-password-component note-detail-printable">
<form class="protected-session-password-form">
<div class="form-group">
<label for="protected-session-password-in-detail">Showing protected note requires entering your password:</label>

View File

@ -1,4 +1,4 @@
<div class="note-detail-relation-map note-detail-component">
<div class="note-detail-relation-map note-detail-printable">
<button class="relation-map-create-child-note btn btn-sm floating-button" type="button"
title="Create new child note and add it into this relation map">
<span class="bx bx-folder-plus"></span>

View File

@ -1,4 +1,4 @@
<div class="note-detail-render note-detail-component">
<div class="note-detail-render note-detail-printable">
<div class="note-detail-render-help alert alert-warning">
<p><strong>This help note is shown because this note of type Render HTML doesn't have required relation to function properly.</strong></p>

View File

@ -1,4 +1,4 @@
<div class="note-detail-search note-detail-component">
<div class="note-detail-search note-detail-printable">
<div style="display: flex; align-items: center; margin-right: 20px; margin-top: 15px;">
<strong>Search string: &nbsp; &nbsp;</strong>
<textarea rows="4" style="width: auto !important; flex-grow: 4" class="search-string form-control"></textarea>

View File

@ -53,12 +53,12 @@
</button>
</div>
<div class="note-detail-component-wrapper">
<div class="note-detail-text note-detail-component" tabindex="10000">
<div class="note-detail-printable-wrapper">
<div class="note-detail-text note-detail-printable" tabindex="10000">
<div class="note-detail-text-editor"></div>
</div>
<div class="note-detail-code note-detail-component">
<div class="note-detail-code note-detail-printable">
<div class="note-detail-code-editor"></div>
</div>