Merge branch '004-server-history-support' into develop

This commit is contained in:
Baptiste Grob
2020-08-14 17:55:25 +02:00
8 changed files with 263 additions and 181 deletions

View File

@@ -45,7 +45,7 @@ import {
PrivilegesAuthModal, PrivilegesAuthModal,
PrivilegesManagementModal, PrivilegesManagementModal,
RevisionPreviewModal, RevisionPreviewModal,
SessionHistoryMenu, HistoryMenu,
SyncResolutionMenu SyncResolutionMenu
} from './directives/views'; } from './directives/views';
@@ -110,7 +110,7 @@ function startApplication(platform: Platform) {
.directive('privilegesAuthModal', () => new PrivilegesAuthModal()) .directive('privilegesAuthModal', () => new PrivilegesAuthModal())
.directive('privilegesManagementModal', () => new PrivilegesManagementModal()) .directive('privilegesManagementModal', () => new PrivilegesManagementModal())
.directive('revisionPreviewModal', () => new RevisionPreviewModal()) .directive('revisionPreviewModal', () => new RevisionPreviewModal())
.directive('sessionHistoryMenu', () => new SessionHistoryMenu()) .directive('historyMenu', () => new HistoryMenu())
.directive('syncResolutionMenu', () => new SyncResolutionMenu()); .directive('syncResolutionMenu', () => new SyncResolutionMenu());
// Filters // Filters

View File

@@ -0,0 +1,191 @@
import { WebDirective } from '../../types';
import { WebApplication } from '@/ui_models/application';
import template from '%/directives/history-menu.pug';
import { SNItem, ItemHistoryEntry } from '@node_modules/snjs/dist/@types';
import { PureViewCtrl } from '@/views';
import { ItemSessionHistory } from 'snjs/dist/@types/services/history/session/item_session_history';
import { RemoteHistoryList, RemoteHistoryListEntry } from 'snjs/dist/@types/services/history/history_manager';
import { confirmDialog } from '@/services/alertService';
type HistoryState = {
fetchingRemoteHistory: boolean
}
interface HistoryScope {
application: WebApplication
item: SNItem
}
class HistoryMenuCtrl extends PureViewCtrl<{}, HistoryState> implements HistoryScope {
diskEnabled = false
autoOptimize = false
application!: WebApplication
item!: SNItem
sessionHistory?: ItemSessionHistory
remoteHistory?: RemoteHistoryList
/* @ngInject */
constructor(
$timeout: ng.ITimeoutService
) {
super($timeout);
this.state = {
fetchingRemoteHistory: false
};
}
$onInit() {
super.$onInit();
this.reloadSessionHistory();
this.fetchRemoteHistory();
this.diskEnabled = this.application.historyManager!.isDiskEnabled();
this.autoOptimize = this.application.historyManager!.isAutoOptimizeEnabled();
}
reloadSessionHistory() {
this.sessionHistory = this.application.historyManager!.sessionHistoryForItem(this.item);
}
get isfetchingRemoteHistory() {
return this.state.fetchingRemoteHistory;
}
set fetchingRemoteHistory(value: boolean) {
this.setState({
fetchingRemoteHistory: value
});
}
async fetchRemoteHistory() {
this.fetchingRemoteHistory = true;
this.remoteHistory = await this.application.historyManager!.remoteHistoryForItem(this.item)
.finally(() => {
this.fetchingRemoteHistory = false;
});
}
async openSessionRevision(revision: ItemHistoryEntry) {
this.application.presentRevisionPreviewModal(
revision.payload.uuid,
revision.payload.content
);
}
async openRemoteRevision(revision: RemoteHistoryListEntry) {
this.fetchingRemoteHistory = true;
const remoteRevision = await this.application.historyManager!.fetchRemoteRevision(this.item.uuid, revision);
this.fetchingRemoteHistory = false;
if (!remoteRevision) {
this.application.alertService!.alert("The remote revision could not be loaded. Please try again later.");
return;
}
this.application.presentRevisionPreviewModal(
remoteRevision.payload.uuid,
remoteRevision.payload.content
);
}
classForSessionRevision(revision: ItemHistoryEntry) {
const vector = revision.operationVector();
if (vector === 0) {
return 'default';
} else if (vector === 1) {
return 'success';
} else if (vector === -1) {
return 'danger';
}
}
clearItemSessionHistory() {
confirmDialog({
text: "Are you sure you want to delete the local session history for this note?",
confirmButtonStyle: "danger"
}).then((confirmed) => {
if (!confirmed) {
return;
}
this.application.historyManager!.clearHistoryForItem(this.item).then(() => {
this.$timeout(() => {
this.reloadSessionHistory();
});
});
});
}
clearAllSessionHistory() {
confirmDialog({
text: "Are you sure you want to delete the local session history for all notes?",
confirmButtonStyle: "danger"
}).then((confirmed) => {
if (!confirmed) {
return;
}
this.application.historyManager!.clearAllHistory().then(() => {
this.$timeout(() => {
this.reloadSessionHistory();
});
});
});
}
get sessionHistoryEntries() {
return this.sessionHistory?.entries;
}
get remoteHistoryEntries() {
return this.remoteHistory;
}
toggleSessionHistoryDiskSaving() {
const run = () => {
this.application.historyManager!.toggleDiskSaving().then(() => {
this.$timeout(() => {
this.diskEnabled = this.application.historyManager!.isDiskEnabled();
});
});
};
if (!this.application.historyManager!.isDiskEnabled()) {
confirmDialog({
text: "Are you sure you want to save history to disk? This will decrease general " +
"performance, especially as you type. You are advised to disable this feature " +
"if you experience any lagging.",
confirmButtonStyle: "danger"
}).then((confirmed) => {
if (confirmed) {
run();
}
});
} else {
run();
}
}
toggleSessionHistoryAutoOptimize() {
this.application.historyManager!.toggleAutoOptimize().then(() => {
this.$timeout(() => {
this.autoOptimize = this.application.historyManager!.autoOptimize;
});
});
}
previewRemoteHistoryTitle(revision: RemoteHistoryListEntry) {
const createdAt = revision.created_at!;
return new Date(createdAt).toLocaleString();
}
}
export class HistoryMenu extends WebDirective {
constructor() {
super();
this.restrict = 'E';
this.template = template;
this.controller = HistoryMenuCtrl;
this.controllerAs = 'ctrl';
this.bindToController = true;
this.scope = {
item: '=',
application: '='
};
}
}

View File

@@ -11,5 +11,5 @@ export { PermissionsModal } from './permissionsModal';
export { PrivilegesAuthModal } from './privilegesAuthModal'; export { PrivilegesAuthModal } from './privilegesAuthModal';
export { PrivilegesManagementModal } from './privilegesManagementModal'; export { PrivilegesManagementModal } from './privilegesManagementModal';
export { RevisionPreviewModal } from './revisionPreviewModal'; export { RevisionPreviewModal } from './revisionPreviewModal';
export { SessionHistoryMenu } from './sessionHistoryMenu'; export { HistoryMenu } from './historyMenu';
export { SyncResolutionMenu } from './syncResolutionMenu'; export { SyncResolutionMenu } from './syncResolutionMenu';

View File

@@ -89,7 +89,7 @@ class RevisionPreviewModalCtrl implements RevisionPreviewScope {
} }
} }
async restore(asCopy: boolean) { restore(asCopy: boolean) {
const run = async () => { const run = async () => {
if (asCopy) { if (asCopy) {
const contentCopy = Object.assign({}, this.content); const contentCopy = Object.assign({}, this.content);
@@ -110,12 +110,14 @@ class RevisionPreviewModalCtrl implements RevisionPreviewScope {
}; };
if (!asCopy) { if (!asCopy) {
if (await confirmDialog({ confirmDialog({
text: "Are you sure you want to replace the current note's contents with what you see in this preview?", text: "Are you sure you want to replace the current note's contents with what you see in this preview?",
confirmButtonStyle: 'danger', confirmButtonStyle: "danger"
})) { }).then((confirmed) => {
run(); if (confirmed) {
} run();
}
});
} else { } else {
run(); run();
} }

View File

@@ -1,131 +0,0 @@
import { WebDirective } from './../../types';
import { WebApplication } from '@/ui_models/application';
import template from '%/directives/session-history-menu.pug';
import { SNItem, ItemHistoryEntry, ItemHistory } from '@node_modules/snjs/dist/@types';
import { confirmDialog } from '@/services/alertService';
interface SessionHistoryScope {
application: WebApplication
item: SNItem
}
class SessionHistoryMenuCtrl implements SessionHistoryScope {
$timeout: ng.ITimeoutService
diskEnabled = false
autoOptimize = false
application!: WebApplication
item!: SNItem
entries!: ItemHistoryEntry[]
history!: ItemHistory
/* @ngInject */
constructor(
$timeout: ng.ITimeoutService
) {
this.$timeout = $timeout;
}
$onInit() {
this.reloadHistory();
this.diskEnabled = this.application.historyManager!.isDiskEnabled();
this.autoOptimize = this.application.historyManager!.isAutoOptimizeEnabled();
}
reloadHistory() {
const history = this.application.historyManager!.historyForItem(this.item);
this.entries = history.entries.slice(0).sort((a, b) => {
return a.payload.updated_at! < b.payload.updated_at! ? 1 : -1;
});
this.history = history;
}
openRevision(revision: ItemHistoryEntry) {
this.application.presentRevisionPreviewModal(
revision.payload.uuid,
revision.payload.content
);
}
classForRevision(revision: ItemHistoryEntry) {
const vector = revision.operationVector();
if (vector === 0) {
return 'default';
} else if (vector === 1) {
return 'success';
} else if (vector === -1) {
return 'danger';
}
}
async clearItemHistory() {
if (await confirmDialog({
text: "Are you sure you want to delete the local session history for this note?",
confirmButtonStyle: 'danger',
})) {
this.application.historyManager!.clearHistoryForItem(this.item).then(() => {
this.$timeout(() => {
this.reloadHistory();
});
});
}
}
clearAllHistory() {
if (confirmDialog({
text: "Are you sure you want to delete the local session history for all notes?",
confirmButtonStyle: 'danger'
})) {
this.application.historyManager!.clearAllHistory().then(() => {
this.$timeout(() => {
this.reloadHistory();
});
});
}
}
async toggleDiskSaving() {
const run = () => {
this.application.historyManager!.toggleDiskSaving().then(() => {
this.$timeout(() => {
this.diskEnabled = this.application.historyManager!.isDiskEnabled();
});
});
};
if (!this.application.historyManager!.isDiskEnabled()) {
if (await confirmDialog({
text: `Are you sure you want to save history to disk? This will decrease general
performance, especially as you type. You are advised to disable this feature
if you experience any lagging.`,
confirmButtonStyle: 'danger',
})) {
run();
}
} else {
run();
}
}
toggleAutoOptimize() {
this.application.historyManager!.toggleAutoOptimize().then(() => {
this.$timeout(() => {
this.autoOptimize = this.application.historyManager!.autoOptimize;
});
});
}
}
export class SessionHistoryMenu extends WebDirective {
constructor() {
super();
this.restrict = 'E';
this.template = template;
this.controller = SessionHistoryMenuCtrl;
this.controllerAs = 'ctrl';
this.bindToController = true;
this.scope = {
item: '=',
application: '='
};
}
}

View File

@@ -183,14 +183,14 @@
application='self.application' application='self.application'
) )
.sk-app-bar-item( .sk-app-bar-item(
click-outside=`self.setMenuState('showSessionHistory', false)`, click-outside=`self.setMenuState('showHistory', false)`,
is-open='self.state.showSessionHistory', is-open='self.state.showHistory',
ng-click="self.toggleMenu('showSessionHistory')" ng-click="self.toggleMenu('showHistory')"
) )
.sk-label Session History .sk-label History
session-history-menu( history-menu(
item='self.note', item='self.note',
ng-if='self.state.showSessionHistory', ng-if='self.state.showHistory',
application='self.application' application='self.application'
) )
#editor-content.editor-content(ng-if='!self.note.errorDecrypting') #editor-content.editor-content(ng-if='!self.note.errorDecrypting')

View File

@@ -0,0 +1,55 @@
#history-menu.sn-component
.sk-menu-panel.dropdown-menu
.sk-menu-panel-header
.sk-menu-panel-header-title Session
.sk-menu-panel-header-subtitle {{ctrl.sessionHistoryEntries.length || 'No'}} revisions
a.sk-a.info.sk-h5(
ng-click='ctrl.showSessionOptions = !ctrl.showSessionOptions; $event.stopPropagation();'
) Options
div(ng-if='ctrl.showSessionOptions')
menu-row(
action='ctrl.clearItemSessionHistory()'
label="'Clear note local history'"
)
menu-row(
action='ctrl.clearAllSessionHistory()'
label="'Clear all local history'"
)
menu-row(
action='ctrl.toggleSessionHistoryAutoOptimize()'
label="(ctrl.autoOptimize ? 'Disable' : 'Enable') + ' auto cleanup'")
.sk-sublabel
| Automatically cleans up small revisions to conserve space.
menu-row(
action='ctrl.toggleSessionHistoryDiskSaving()'
label="(ctrl.diskEnabled ? 'Disable' : 'Enable') + ' saving history to disk'"
)
.sk-sublabel
| Saving to disk is not recommended. Decreases performance and increases app
| loading time and memory footprint.
menu-row(
ng-repeat='revision in ctrl.sessionHistoryEntries track by $index'
action='ctrl.openSessionRevision(revision);'
label='revision.previewTitle()'
)
.sk-sublabel.opaque(ng-class='ctrl.classForSessionRevision(revision)')
| {{revision.previewSubTitle()}}
.sk-menu-panel-header
.sk-menu-panel-header-title Remote
.sk-menu-panel-header-subtitle {{ctrl.remoteHistoryEntries.length || 'No'}} revisions
a.sk-a.info.sk-h5(
ng-click='ctrl.showRemoteOptions = !ctrl.showRemoteOptions; $event.stopPropagation();'
) Options
div(ng-if='ctrl.showRemoteOptions')
menu-row(
action='ctrl.fetchRemoteHistory()'
label="'Refresh'"
disabled="ctrl.isfetchingRemoteHistory"
spinner-class="ctrl.isfetchingRemoteHistory ? 'info' : null")
.sk-sublabel
| Fetch history from server.
menu-row(
ng-repeat='revision in ctrl.remoteHistoryEntries track by $index'
action='ctrl.openRemoteRevision(revision);'
label='ctrl.previewRemoteHistoryTitle(revision);'
)

View File

@@ -1,35 +0,0 @@
#session-history-menu.sn-component
.sk-menu-panel.dropdown-menu
.sk-menu-panel-header
.sk-menu-panel-header-title {{ctrl.history.entries.length || 'No'}} revisions
a.sk-a.info.sk-h5(
ng-click='ctrl.showOptions = !ctrl.showOptions; $event.stopPropagation();'
) Options
div(ng-if='ctrl.showOptions')
menu-row(
action='ctrl.clearItemHistory()'
label="'Clear note local history'"
)
menu-row(
action='ctrl.clearAllHistory()'
label="'Clear all local history'"
)
menu-row(
action='ctrl.toggleAutoOptimize()'
label="(ctrl.autoOptimize ? 'Disable' : 'Enable') + ' auto cleanup'")
.sk-sublabel
| Automatically cleans up small revisions to conserve space.
menu-row(
action='ctrl.toggleDiskSaving()'
label="(ctrl.diskEnabled ? 'Disable' : 'Enable') + ' saving history to disk'"
)
.sk-sublabel
| Saving to disk is not recommended. Decreases performance and increases app
| loading time and memory footprint.
menu-row(
ng-repeat='revision in ctrl.entries'
action='ctrl.openRevision(revision);'
label='revision.previewTitle()'
)
.sk-sublabel.opaque(ng-class='ctrl.classForRevision(revision)')
| {{revision.previewSubTitle()}}