mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
opened file change detection now useable on all note types
This commit is contained in:
parent
dcd35b1ea2
commit
ccac46527c
@ -36,6 +36,7 @@ import SearchResultWidget from "../widgets/search_result.js";
|
||||
import SyncStatusWidget from "../widgets/sync_status.js";
|
||||
import ScrollingContainer from "../widgets/containers/scrolling_container.js";
|
||||
import RootContainer from "../widgets/containers/root_container.js";
|
||||
import NoteUpdateStatusWidget from "../widgets/note_update_status.js";
|
||||
|
||||
const RIGHT_PANE_CSS = `
|
||||
<style>
|
||||
@ -177,6 +178,7 @@ export default class DesktopLayout {
|
||||
.child(new InheritedAttributesWidget())
|
||||
)
|
||||
)
|
||||
.child(new NoteUpdateStatusWidget())
|
||||
.child(
|
||||
new TabCachingWidget(() => new ScrollingContainer()
|
||||
.child(new SqlTableSchemasWidget())
|
||||
|
@ -11,6 +11,10 @@ function fileModificationUploaded(noteId) {
|
||||
delete fileModificationStatus[noteId];
|
||||
}
|
||||
|
||||
function ignoreModification(noteId) {
|
||||
delete fileModificationStatus[noteId];
|
||||
}
|
||||
|
||||
ws.subscribeToMessages(async message => {
|
||||
if (message.type !== 'openedFileUpdated') {
|
||||
return;
|
||||
@ -27,5 +31,6 @@ ws.subscribeToMessages(async message => {
|
||||
|
||||
export default {
|
||||
getFileModificationStatus,
|
||||
fileModificationUploaded
|
||||
fileModificationUploaded,
|
||||
ignoreModification
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ async function getRenderedContent(note, options = {}) {
|
||||
const $openButton = $('<button class="file-open btn btn-primary" type="button">Open</button>');
|
||||
|
||||
$downloadButton.on('click', () => openService.downloadFileNote(note.noteId));
|
||||
$openButton.on('click', () => openService.openFileNote(note.noteId));
|
||||
$openButton.on('click', () => openService.openNoteExternally(note.noteId));
|
||||
|
||||
// open doesn't work for protected notes since it works through browser which isn't in protected session
|
||||
$openButton.toggle(!note.isProtected);
|
||||
|
@ -21,9 +21,9 @@ function downloadFileNote(noteId) {
|
||||
download(url);
|
||||
}
|
||||
|
||||
async function openFileNote(noteId) {
|
||||
async function openNoteExternally(noteId) {
|
||||
if (utils.isElectron()) {
|
||||
const resp = await server.post("notes/" + noteId + "/saveToTmpDir");
|
||||
const resp = await server.post("notes/" + noteId + "/save-to-tmp-dir");
|
||||
|
||||
const electron = utils.dynamicRequire('electron');
|
||||
const res = await electron.shell.openPath(resp.tmpFilePath);
|
||||
@ -66,7 +66,7 @@ function getHost() {
|
||||
export default {
|
||||
download,
|
||||
downloadFileNote,
|
||||
openFileNote,
|
||||
openNoteExternally,
|
||||
downloadNoteRevision,
|
||||
getUrlForDownload
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
import TabAwareWidget from "./tab_aware_widget.js";
|
||||
import protectedSessionService from "../services/protected_session.js";
|
||||
import openService from "../services/open.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="dropdown note-actions">
|
||||
<style>
|
||||
.note-actions .dropdown-menu {
|
||||
width: 15em;
|
||||
width: 20em;
|
||||
}
|
||||
|
||||
.note-actions .dropdown-item[disabled], .note-actions .dropdown-item[disabled]:hover {
|
||||
@ -84,6 +85,7 @@ const TPL = `
|
||||
<a data-trigger-command="showNoteRevisions" class="dropdown-item show-note-revisions-button">Revisions</a>
|
||||
<a data-trigger-command="showLinkMap" class="dropdown-item show-link-map-button"><kbd data-command="showLinkMap"></kbd> Link map</a>
|
||||
<a data-trigger-command="showNoteSource" class="dropdown-item show-source-button"><kbd data-command="showNoteSource"></kbd> Note source</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>
|
||||
<a data-trigger-command="printActiveNote" class="dropdown-item print-note-button"><kbd data-command="printActiveNote"></kbd> Print note</a>
|
||||
@ -119,6 +121,9 @@ export default class NoteActionsWidget extends TabAwareWidget {
|
||||
|
||||
this.$widget.on('click', '.dropdown-item',
|
||||
() => this.$widget.find('.dropdown-toggle').dropdown('toggle'));
|
||||
|
||||
this.$openNoteExternallyButton = this.$widget.find(".open-note-externally-button");
|
||||
this.$openNoteExternallyButton.on('click', () => openService.openNoteExternally(this.noteId));
|
||||
}
|
||||
|
||||
refreshWithNote(note) {
|
||||
|
64
src/public/app/widgets/note_update_status.js
Normal file
64
src/public/app/widgets/note_update_status.js
Normal file
@ -0,0 +1,64 @@
|
||||
import TabAwareWidget from "./tab_aware_widget.js";
|
||||
import server from "../services/server.js";
|
||||
import fileWatcher from "../services/file_watcher.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="dropdown note-update-status-widget alert alert-warning">
|
||||
<style>
|
||||
.note-update-status-widget {
|
||||
margin: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<p>File <code class="file-path"></code> has been last modified on <span class="file-last-modified"></span>.</p>
|
||||
|
||||
<div style="display: flex; flex-direction: row; justify-content: space-evenly;">
|
||||
<button class="btn btn-sm file-upload-button">Upload modified file</button>
|
||||
|
||||
<button class="btn btn-sm ignore-this-change-button">Ignore this change</button>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
export default class NoteUpdateStatusWidget extends TabAwareWidget {
|
||||
isEnabled() {
|
||||
return super.isEnabled()
|
||||
&& !!fileWatcher.getFileModificationStatus(this.noteId);
|
||||
}
|
||||
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
this.overflowing();
|
||||
|
||||
this.$filePath = this.$widget.find(".file-path");
|
||||
this.$fileLastModified = this.$widget.find(".file-last-modified");
|
||||
this.$fileUploadButton = this.$widget.find(".file-upload-button");
|
||||
|
||||
this.$fileUploadButton.on("click", async () => {
|
||||
await server.post(`notes/${this.noteId}/upload-modified-file`, {
|
||||
filePath: this.$filePath.text()
|
||||
});
|
||||
|
||||
fileWatcher.fileModificationUploaded(this.noteId);
|
||||
this.refresh();
|
||||
});
|
||||
|
||||
this.$ignoreThisChangeButton = this.$widget.find(".ignore-this-change-button");
|
||||
this.$ignoreThisChangeButton.on('click', () => {
|
||||
fileWatcher.ignoreModification(this.noteId);
|
||||
this.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
refreshWithNote(note) {
|
||||
const status = fileWatcher.getFileModificationStatus(note.noteId);
|
||||
|
||||
this.$filePath.text(status.filePath);
|
||||
this.$fileLastModified.text(dayjs.unix(status.lastModifiedMs / 1000).format("HH:mm:ss"));
|
||||
}
|
||||
|
||||
openedFileUpdatedEvent(data) {
|
||||
if (data.noteId === this.noteId) {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
}
|
@ -82,7 +82,7 @@ export default class FilePropertiesWidget extends TabAwareWidget {
|
||||
this.$uploadNewRevisionInput = this.$widget.find(".file-upload-new-revision-input");
|
||||
|
||||
this.$downloadButton.on('click', () => openService.downloadFileNote(this.noteId));
|
||||
this.$openButton.on('click', () => openService.openFileNote(this.noteId));
|
||||
this.$openButton.on('click', () => openService.openNoteExternally(this.noteId));
|
||||
|
||||
this.$uploadNewRevisionButton.on("click", () => {
|
||||
this.$uploadNewRevisionInput.trigger("click");
|
||||
|
@ -26,6 +26,8 @@ const TPL = `
|
||||
<div class="no-print" style="display: flex; justify-content: space-evenly; margin: 10px;">
|
||||
<button class="image-download btn btn-sm btn-primary" type="button">Download</button>
|
||||
|
||||
<button class="image-open btn btn-sm btn-primary" type="button">Open</button>
|
||||
|
||||
<button class="image-copy-to-clipboard btn btn-sm btn-primary" type="button">Copy to clipboard</button>
|
||||
|
||||
<button class="image-upload-new-revision btn btn-sm btn-primary" type="button">Upload new revision</button>
|
||||
@ -59,6 +61,9 @@ export default class ImagePropertiesWidget extends TabAwareWidget {
|
||||
this.$fileType = this.$widget.find(".image-filetype");
|
||||
this.$fileSize = this.$widget.find(".image-filesize");
|
||||
|
||||
this.$openButton = this.$widget.find(".image-open");
|
||||
this.$openButton.on('click', () => openService.openNoteExternally(this.noteId));
|
||||
|
||||
this.$imageDownloadButton = this.$widget.find(".image-download");
|
||||
this.$imageDownloadButton.on('click', () => openService.downloadFileNote(this.noteId));
|
||||
|
||||
|
@ -24,12 +24,6 @@ const TPL = `
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="file-watcher-wrapper alert alert-warning">
|
||||
<p>File <code class="file-watcher-path"></code> has been last modified on <span class="file-watcher-last-modified"></span>.</p>
|
||||
|
||||
<button class="btn btn-sm file-watcher-upload-button">Upload modified file</button>
|
||||
</div>
|
||||
|
||||
<pre class="file-preview-content"></pre>
|
||||
|
||||
<div class="file-preview-not-available alert alert-info">
|
||||
@ -54,22 +48,6 @@ export default class FileTypeWidget extends TypeWidget {
|
||||
this.$pdfPreview = this.$widget.find(".pdf-preview");
|
||||
this.$videoPreview = this.$widget.find(".video-preview");
|
||||
this.$audioPreview = this.$widget.find(".audio-preview");
|
||||
|
||||
this.$fileWatcherWrapper = this.$widget.find(".file-watcher-wrapper");
|
||||
this.$fileWatcherWrapper.hide();
|
||||
|
||||
this.$fileWatcherPath = this.$widget.find(".file-watcher-path");
|
||||
this.$fileWatcherLastModified = this.$widget.find(".file-watcher-last-modified");
|
||||
this.$fileWatcherUploadButton = this.$widget.find(".file-watcher-upload-button");
|
||||
|
||||
this.$fileWatcherUploadButton.on("click", async () => {
|
||||
await server.post(`notes/${this.noteId}/upload-modified-file`, {
|
||||
filePath: this.$fileWatcherPath.text()
|
||||
});
|
||||
|
||||
fileWatcher.fileModificationUploaded(this.noteId);
|
||||
this.refreshFileWatchingStatus();
|
||||
});
|
||||
}
|
||||
|
||||
async doRefresh(note) {
|
||||
@ -107,22 +85,5 @@ export default class FileTypeWidget extends TypeWidget {
|
||||
else {
|
||||
this.$previewNotAvailable.show();
|
||||
}
|
||||
|
||||
this.refreshFileWatchingStatus();
|
||||
}
|
||||
|
||||
refreshFileWatchingStatus() {
|
||||
const status = fileWatcher.getFileModificationStatus(this.noteId);
|
||||
|
||||
this.$fileWatcherWrapper.toggle(!!status);
|
||||
|
||||
if (status) {
|
||||
this.$fileWatcherPath.text(status.filePath);
|
||||
this.$fileWatcherLastModified.text(dayjs.unix(status.lastModifiedMs / 1000).format("HH:mm:ss"));
|
||||
}
|
||||
}
|
||||
|
||||
openedFileUpdatedEvent(data) {
|
||||
this.refreshFileWatchingStatus();
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
const protectedSessionService = require('../../services/protected_session');
|
||||
const repository = require('../../services/repository');
|
||||
const utils = require('../../services/utils');
|
||||
const log = require('../../services/log');
|
||||
const noteRevisionService = require('../../services/note_revisions');
|
||||
const tmp = require('tmp');
|
||||
const fs = require('fs');
|
||||
@ -122,6 +123,8 @@ function saveToTmpDir(req) {
|
||||
fs.writeSync(tmpObj.fd, note.getContent());
|
||||
fs.closeSync(tmpObj.fd);
|
||||
|
||||
log.info(`Saved temporary file for note ${noteId} into ${tmpObj.name}`);
|
||||
|
||||
if (utils.isElectron()) {
|
||||
chokidar.watch(tmpObj.name).on('change', (path, stats) => {
|
||||
ws.sendMessageToAllClients({
|
||||
@ -130,8 +133,6 @@ function saveToTmpDir(req) {
|
||||
lastModifiedMs: stats.atimeMs,
|
||||
filePath: tmpObj.name
|
||||
});
|
||||
|
||||
console.log(stats, path);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -285,6 +285,8 @@ function uploadModifiedFile(req) {
|
||||
return [404, `Note ${noteId} has not been found`];
|
||||
}
|
||||
|
||||
log.info(`Updating note ${noteId} with content from ${filePath}`);
|
||||
|
||||
noteRevisionService.createNoteRevision(note);
|
||||
|
||||
const fileContent = fs.readFileSync(filePath);
|
||||
|
@ -186,7 +186,7 @@ function register(app) {
|
||||
route(GET, '/api/notes/:noteId/download', [auth.checkApiAuthOrElectron], filesRoute.downloadFile);
|
||||
// this "hacky" path is used for easier referencing of CSS resources
|
||||
route(GET, '/api/notes/download/:noteId', [auth.checkApiAuthOrElectron], filesRoute.downloadFile);
|
||||
apiRoute(POST, '/api/notes/:noteId/saveToTmpDir', filesRoute.saveToTmpDir);
|
||||
apiRoute(POST, '/api/notes/:noteId/save-to-tmp-dir', filesRoute.saveToTmpDir);
|
||||
|
||||
apiRoute(GET, '/api/notes/:noteId/attributes', attributesRoute.getEffectiveNoteAttributes);
|
||||
apiRoute(POST, '/api/notes/:noteId/attributes', attributesRoute.addNoteAttribute);
|
||||
|
@ -352,6 +352,12 @@ const DEFAULT_KEYBOARD_ACTIONS = [
|
||||
defaultShortcuts: [],
|
||||
scope: "window"
|
||||
},
|
||||
{
|
||||
actionName: "openNoteExternally",
|
||||
defaultShortcuts: [],
|
||||
description: "Open note as a file with default application",
|
||||
scope: "window"
|
||||
},
|
||||
{
|
||||
actionName: "renderActiveNote",
|
||||
defaultShortcuts: [],
|
||||
|
Loading…
x
Reference in New Issue
Block a user