mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-04 13:39:01 +01:00 
			
		
		
		
	attachment improvements
This commit is contained in:
		
							parent
							
								
									a5f0b2a81e
								
							
						
					
					
						commit
						54c0268593
					
				
							
								
								
									
										1
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -5,6 +5,7 @@
 | 
			
		||||
  "requires": true,
 | 
			
		||||
  "packages": {
 | 
			
		||||
    "": {
 | 
			
		||||
      "name": "trilium",
 | 
			
		||||
      "version": "0.59.3",
 | 
			
		||||
      "hasInstallScript": true,
 | 
			
		||||
      "license": "AGPL-3.0-only",
 | 
			
		||||
 | 
			
		||||
@ -39,6 +39,8 @@ class NoteContext extends Component {
 | 
			
		||||
 | 
			
		||||
    async setNote(inputNotePath, opts = {}) {
 | 
			
		||||
        opts.triggerSwitchEvent = opts.triggerSwitchEvent !== undefined ? opts.triggerSwitchEvent : true;
 | 
			
		||||
        opts.viewScope = opts.viewScope || {};
 | 
			
		||||
        opts.viewScope.viewMode = opts.viewScope.viewMode || "default";
 | 
			
		||||
 | 
			
		||||
        const resolvedNotePath = await this.getResolvedNotePath(inputNotePath);
 | 
			
		||||
 | 
			
		||||
@ -46,6 +48,10 @@ class NoteContext extends Component {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.notePath === resolvedNotePath && utils.areObjectsEqual(this.viewScope, opts.viewScope)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await this.triggerEvent('beforeNoteSwitch', {noteContext: this});
 | 
			
		||||
 | 
			
		||||
        utils.closeActiveDialog();
 | 
			
		||||
@ -53,8 +59,7 @@ class NoteContext extends Component {
 | 
			
		||||
        this.notePath = resolvedNotePath;
 | 
			
		||||
        ({noteId: this.noteId, parentNoteId: this.parentNoteId} = treeService.getNoteIdAndParentIdFromNotePath(resolvedNotePath));
 | 
			
		||||
 | 
			
		||||
        this.viewScope = opts.viewScope || {};
 | 
			
		||||
        this.viewScope.viewMode = this.viewScope.viewMode || "default";
 | 
			
		||||
        this.viewScope = opts.viewScope;
 | 
			
		||||
 | 
			
		||||
        this.saveToRecentNotes(resolvedNotePath);
 | 
			
		||||
 | 
			
		||||
@ -137,10 +142,6 @@ class NoteContext extends Component {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (resolvedNotePath === this.notePath) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (await hoistedNoteService.checkNoteAccess(resolvedNotePath, this) === false) {
 | 
			
		||||
            return; // note is outside of hoisted subtree and user chose not to unhoist
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -31,3 +31,5 @@ class FAttachment {
 | 
			
		||||
        return this.froca.notes[this.parentId];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default FAttachment;
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ import options from "../services/options.js";
 | 
			
		||||
import froca from "../services/froca.js";
 | 
			
		||||
import protectedSessionHolder from "../services/protected_session_holder.js";
 | 
			
		||||
import cssClassManager from "../services/css_class_manager.js";
 | 
			
		||||
import FAttachment from "./fattachment.js";
 | 
			
		||||
 | 
			
		||||
const LABEL = 'label';
 | 
			
		||||
const RELATION = 'relation';
 | 
			
		||||
 | 
			
		||||
@ -78,39 +78,39 @@ async function createNoteLink(notePath, options = {}) {
 | 
			
		||||
    return $container;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getNotePathFromLink($link) {
 | 
			
		||||
    const notePathAttr = $link.attr("data-note-path");
 | 
			
		||||
 | 
			
		||||
    if (notePathAttr) {
 | 
			
		||||
        return notePathAttr;
 | 
			
		||||
    }
 | 
			
		||||
function parseNotePathAndScope($link) {
 | 
			
		||||
    let notePath = $link.attr("data-note-path");
 | 
			
		||||
 | 
			
		||||
    if (!notePath) {
 | 
			
		||||
        const url = $link.attr('href');
 | 
			
		||||
 | 
			
		||||
    const notePath = url ? getNotePathFromUrl(url) : null;
 | 
			
		||||
        notePath = url ? getNotePathFromUrl(url) : null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const viewScope = {
 | 
			
		||||
        viewMode: $link.attr('data-view-mode'),
 | 
			
		||||
        viewMode: $link.attr('data-view-mode') || 'default',
 | 
			
		||||
        attachmentId: $link.attr('data-attachment-id'),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        notePath,
 | 
			
		||||
        noteId: treeService.getNoteIdFromNotePath(notePath),
 | 
			
		||||
        viewScope
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function goToLink(evt) {
 | 
			
		||||
    const $link = $(evt.target).closest("a,.block-link");
 | 
			
		||||
    const address = $link.attr('href');
 | 
			
		||||
    const hrefLink = $link.attr('href');
 | 
			
		||||
 | 
			
		||||
    if (address?.startsWith("data:")) {
 | 
			
		||||
    if (hrefLink?.startsWith("data:")) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    evt.preventDefault();
 | 
			
		||||
    evt.stopPropagation();
 | 
			
		||||
 | 
			
		||||
    const {notePath, viewScope} = getNotePathFromLink($link);
 | 
			
		||||
    const { notePath, viewScope } = parseNotePathAndScope($link);
 | 
			
		||||
 | 
			
		||||
    const ctrlKey = utils.isCtrlKey(evt);
 | 
			
		||||
    const isLeftClick = evt.which === 1;
 | 
			
		||||
@ -135,20 +135,19 @@ function goToLink(evt) {
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        if (openInNewTab
 | 
			
		||||
            || $link.hasClass("ck-link-actions__preview") // within edit link dialog single click suffices
 | 
			
		||||
            || $link.closest("[contenteditable]").length === 0 // outside of CKEditor single click suffices
 | 
			
		||||
        ) {
 | 
			
		||||
            if (address) {
 | 
			
		||||
                if (address.toLowerCase().startsWith('http')) {
 | 
			
		||||
                    window.open(address, '_blank');
 | 
			
		||||
    else if (hrefLink) {
 | 
			
		||||
        // this branch handles external links
 | 
			
		||||
        const isWithinCKLinkDialog = $link.hasClass("ck-link-actions__preview");
 | 
			
		||||
        const isOutsideCKEditor = $link.closest("[contenteditable]").length === 0;
 | 
			
		||||
 | 
			
		||||
        if (openInNewTab || isWithinCKLinkDialog || isOutsideCKEditor) {
 | 
			
		||||
            if (hrefLink.toLowerCase().startsWith('http')) {
 | 
			
		||||
                window.open(hrefLink, '_blank');
 | 
			
		||||
            }
 | 
			
		||||
                else if (address.toLowerCase().startsWith('file:') && utils.isElectron()) {
 | 
			
		||||
            else if (hrefLink.toLowerCase().startsWith('file:') && utils.isElectron()) {
 | 
			
		||||
                const electron = utils.dynamicRequire('electron');
 | 
			
		||||
 | 
			
		||||
                    electron.shell.openPath(address);
 | 
			
		||||
                }
 | 
			
		||||
                electron.shell.openPath(hrefLink);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -159,7 +158,7 @@ function goToLink(evt) {
 | 
			
		||||
function linkContextMenu(e) {
 | 
			
		||||
    const $link = $(e.target).closest("a");
 | 
			
		||||
 | 
			
		||||
    const {notePath, viewScope} = getNotePathFromLink($link);
 | 
			
		||||
    const { notePath, viewScope } = parseNotePathAndScope($link);
 | 
			
		||||
 | 
			
		||||
    if (!notePath) {
 | 
			
		||||
        return;
 | 
			
		||||
@ -223,5 +222,6 @@ export default {
 | 
			
		||||
    getNotePathFromUrl,
 | 
			
		||||
    createNoteLink,
 | 
			
		||||
    goToLink,
 | 
			
		||||
    loadReferenceLinkTitle
 | 
			
		||||
    loadReferenceLinkTitle,
 | 
			
		||||
    parseNotePathAndScope
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -31,18 +31,12 @@ async function mouseEnterHandler() {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let notePath = linkService.getNotePathFromUrl($link.attr("href"));
 | 
			
		||||
    const { notePath, noteId, viewScope } = linkService.parseNotePathAndScope($link);
 | 
			
		||||
 | 
			
		||||
    if (!notePath) {
 | 
			
		||||
        notePath = $link.attr("data-note-path");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!notePath) {
 | 
			
		||||
    if (!notePath || viewScope.viewMode !== 'default') {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const noteId = treeService.getNoteIdFromNotePath(notePath);
 | 
			
		||||
 | 
			
		||||
    const note = await froca.getNote(noteId);
 | 
			
		||||
    const content = await renderTooltip(note);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -365,6 +365,121 @@ function escapeRegExp(str) {
 | 
			
		||||
    return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function areObjectsEqual () {
 | 
			
		||||
    var i, l, leftChain, rightChain;
 | 
			
		||||
 | 
			
		||||
    function compare2Objects (x, y) {
 | 
			
		||||
        var p;
 | 
			
		||||
 | 
			
		||||
        // remember that NaN === NaN returns false
 | 
			
		||||
        // and isNaN(undefined) returns true
 | 
			
		||||
        if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Compare primitives and functions.
 | 
			
		||||
        // Check if both arguments link to the same object.
 | 
			
		||||
        // Especially useful on the step where we compare prototypes
 | 
			
		||||
        if (x === y) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Works in case when functions are created in constructor.
 | 
			
		||||
        // Comparing dates is a common scenario. Another built-ins?
 | 
			
		||||
        // We can even handle functions passed across iframes
 | 
			
		||||
        if ((typeof x === 'function' && typeof y === 'function') ||
 | 
			
		||||
            (x instanceof Date && y instanceof Date) ||
 | 
			
		||||
            (x instanceof RegExp && y instanceof RegExp) ||
 | 
			
		||||
            (x instanceof String && y instanceof String) ||
 | 
			
		||||
            (x instanceof Number && y instanceof Number)) {
 | 
			
		||||
            return x.toString() === y.toString();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // At last checking prototypes as good as we can
 | 
			
		||||
        if (!(x instanceof Object && y instanceof Object)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (x.constructor !== y.constructor) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (x.prototype !== y.prototype) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check for infinitive linking loops
 | 
			
		||||
        if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Quick checking of one object being a subset of another.
 | 
			
		||||
        // todo: cache the structure of arguments[0] for performance
 | 
			
		||||
        for (p in y) {
 | 
			
		||||
            if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            else if (typeof y[p] !== typeof x[p]) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (p in x) {
 | 
			
		||||
            if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            else if (typeof y[p] !== typeof x[p]) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            switch (typeof (x[p])) {
 | 
			
		||||
                case 'object':
 | 
			
		||||
                case 'function':
 | 
			
		||||
 | 
			
		||||
                    leftChain.push(x);
 | 
			
		||||
                    rightChain.push(y);
 | 
			
		||||
 | 
			
		||||
                    if (!compare2Objects (x[p], y[p])) {
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    leftChain.pop();
 | 
			
		||||
                    rightChain.pop();
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                default:
 | 
			
		||||
                    if (x[p] !== y[p]) {
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (arguments.length < 1) {
 | 
			
		||||
        return true; //Die silently? Don't know how to handle such case, please help...
 | 
			
		||||
        // throw "Need two or more arguments to compare";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 1, l = arguments.length; i < l; i++) {
 | 
			
		||||
 | 
			
		||||
        leftChain = []; //Todo: this can be cached
 | 
			
		||||
        rightChain = [];
 | 
			
		||||
 | 
			
		||||
        if (!compare2Objects(arguments[0], arguments[i])) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    reloadFrontendApp,
 | 
			
		||||
    parseDate,
 | 
			
		||||
@ -408,5 +523,6 @@ export default {
 | 
			
		||||
    filterAttributeName,
 | 
			
		||||
    isValidAttributeName,
 | 
			
		||||
    sleep,
 | 
			
		||||
    escapeRegExp
 | 
			
		||||
    escapeRegExp,
 | 
			
		||||
    areObjectsEqual
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -20,24 +20,35 @@ const TPL = `
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        .attachment-content pre {
 | 
			
		||||
            max-height: 400px;
 | 
			
		||||
            background: var(--accented-background-color);
 | 
			
		||||
            padding: 10px;
 | 
			
		||||
            margin-top: 10px;
 | 
			
		||||
            margin-bottom: 10px;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        .attachment-detail-wrapper.list-view .attachment-content pre {
 | 
			
		||||
            max-height: 400px;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        .attachment-content img {
 | 
			
		||||
            margin: 10px;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        .attachment-detail-wrapper.list-view .attachment-content img {
 | 
			
		||||
            max-height: 300px; 
 | 
			
		||||
            max-width: 90%; 
 | 
			
		||||
            object-fit: contain;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        .attachment-detail-wrapper.full-detail .attachment-content img {
 | 
			
		||||
            max-width: 90%; 
 | 
			
		||||
            object-fit: contain;
 | 
			
		||||
        }
 | 
			
		||||
    </style>
 | 
			
		||||
 | 
			
		||||
    <div class="attachment-detail-wrapper">
 | 
			
		||||
        <div class="attachment-title-line">
 | 
			
		||||
            <h4 class="attachment-title"><a href="javascript:" data-trigger-command="openAttachmentDetail"></a></h4>                
 | 
			
		||||
            <h4 class="attachment-title"></h4>                
 | 
			
		||||
            <div class="attachment-details"></div>
 | 
			
		||||
            <div style="flex: 1 1;"></div>
 | 
			
		||||
            <div class="attachment-actions-container"></div>
 | 
			
		||||
@ -54,6 +65,7 @@ export default class AttachmentDetailWidget extends BasicWidget {
 | 
			
		||||
        this.contentSized();
 | 
			
		||||
        this.attachment = attachment;
 | 
			
		||||
        this.attachmentActionsWidget = new AttachmentActionsWidget(attachment);
 | 
			
		||||
        this.isFullDetail = true;
 | 
			
		||||
        this.child(this.attachmentActionsWidget);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -73,7 +85,21 @@ export default class AttachmentDetailWidget extends BasicWidget {
 | 
			
		||||
                    .html()
 | 
			
		||||
            );
 | 
			
		||||
        this.$wrapper = this.$widget.find('.attachment-detail-wrapper');
 | 
			
		||||
        this.$wrapper.find('.attachment-title a').text(this.attachment.title);
 | 
			
		||||
        this.$wrapper.addClass(this.isFullDetail ? "full-detail" : "list-view");
 | 
			
		||||
 | 
			
		||||
        if (!this.isFullDetail) {
 | 
			
		||||
            this.$wrapper.find('.attachment-title').append(
 | 
			
		||||
                $('<a href="javascript:">')
 | 
			
		||||
                    .attr("data-note-path", this.attachment.parentId)
 | 
			
		||||
                    .attr("data-view-mode", "attachments")
 | 
			
		||||
                    .attr("data-attachment-id", this.attachment.attachmentId)
 | 
			
		||||
                    .text(this.attachment.title)
 | 
			
		||||
            );
 | 
			
		||||
        } else {
 | 
			
		||||
            this.$wrapper.find('.attachment-title')
 | 
			
		||||
                .text(this.attachment.title);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.$wrapper.find('.attachment-details')
 | 
			
		||||
            .text(`Role: ${this.attachment.role}, Size: ${utils.formatSize(this.attachment.contentLength)}`);
 | 
			
		||||
        this.$wrapper.find('.attachment-actions-container').append(this.attachmentActionsWidget.render());
 | 
			
		||||
 | 
			
		||||
@ -30,18 +30,19 @@ export default class AttachmentDetailTypeWidget extends TypeWidget {
 | 
			
		||||
        this.children = [];
 | 
			
		||||
        this.renderedAttachmentIds = new Set();
 | 
			
		||||
 | 
			
		||||
        const attachment = await server.get(`notes/${this.noteId}/attachments/${this.noteContext.viewScope.attachment.attachmentId}/?includeContent=true`);
 | 
			
		||||
        const attachment = await server.get(`notes/${this.noteId}/attachments/${this.noteContext.viewScope.attachmentId}/?includeContent=true`);
 | 
			
		||||
 | 
			
		||||
        if (!attachment) {
 | 
			
		||||
            this.$list.html("<strong>This attachment has been deleted.</strong>");
 | 
			
		||||
            this.$wrapper.html("<strong>This attachment has been deleted.</strong>");
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const attachmentDetailWidget = new AttachmentDetailWidget(attachment);
 | 
			
		||||
        attachmentDetailWidget.isFullDetail = true;
 | 
			
		||||
        this.child(attachmentDetailWidget);
 | 
			
		||||
 | 
			
		||||
        this.$list.append(attachmentDetailWidget.render());
 | 
			
		||||
        this.$wrapper.append(attachmentDetailWidget.render());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async entitiesReloadedEvent({loadResults}) {
 | 
			
		||||
 | 
			
		||||
@ -40,6 +40,8 @@ export default class AttachmentListTypeWidget extends TypeWidget {
 | 
			
		||||
 | 
			
		||||
        for (const attachment of attachments) {
 | 
			
		||||
            const attachmentDetailWidget = new AttachmentDetailWidget(attachment);
 | 
			
		||||
            attachmentDetailWidget.isFullDetail = false;
 | 
			
		||||
 | 
			
		||||
            this.child(attachmentDetailWidget);
 | 
			
		||||
 | 
			
		||||
            this.renderedAttachmentIds.add(attachment.attachmentId);
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user