diff --git a/app/assets/javascripts/app.ts b/app/assets/javascripts/app.ts index a648f718b..8e796403f 100644 --- a/app/assets/javascripts/app.ts +++ b/app/assets/javascripts/app.ts @@ -36,7 +36,6 @@ import { ChallengeModal, ComponentModal, ComponentView, - ConflictResolutionModal, EditorMenu, InputModal, MenuRow, @@ -99,7 +98,6 @@ angular // ($rootScope, componentManager, desktopManager, $timeout) => // new ComponentView($rootScope, componentManager, desktopManager, $timeout) // ) - .directive('conflictResolutionModal', () => new ConflictResolutionModal()) .directive('editorMenu', () => new EditorMenu()) .directive('inputModal', () => new InputModal()) .directive('menuRow', () => new MenuRow()) diff --git a/app/assets/javascripts/directives/views/index.ts b/app/assets/javascripts/directives/views/index.ts index 781b050e8..f043f106a 100644 --- a/app/assets/javascripts/directives/views/index.ts +++ b/app/assets/javascripts/directives/views/index.ts @@ -3,7 +3,6 @@ export { ActionsMenu } from './actionsMenu'; export { ChallengeModal } from './challengeModal'; export { ComponentModal } from './componentModal'; export { ComponentView } from './componentView'; -export { ConflictResolutionModal } from './conflictResolutionModal'; export { EditorMenu } from './editorMenu'; export { InputModal } from './inputModal'; export { MenuRow } from './menuRow'; diff --git a/app/assets/javascripts/directives/views/permissionsModal.js b/app/assets/javascripts/directives/views/permissionsModal.ts similarity index 77% rename from app/assets/javascripts/directives/views/permissionsModal.js rename to app/assets/javascripts/directives/views/permissionsModal.ts index 1e7a2adda..b547fec05 100644 --- a/app/assets/javascripts/directives/views/permissionsModal.js +++ b/app/assets/javascripts/directives/views/permissionsModal.ts @@ -1,8 +1,13 @@ +import { WebDirective } from './../../types'; import template from '%/directives/permissions-modal.pug'; class PermissionsModalCtrl { + + $element: JQLite + callback!: (success: boolean) => void + /* @ngInject */ - constructor($element) { + constructor($element: JQLite) { this.$element = $element; } @@ -24,8 +29,9 @@ class PermissionsModalCtrl { } } -export class PermissionsModal { +export class PermissionsModal extends WebDirective { constructor() { + super(); this.restrict = 'E'; this.template = template; this.controller = PermissionsModalCtrl; diff --git a/app/assets/javascripts/directives/views/revisionPreviewModal.js b/app/assets/javascripts/directives/views/revisionPreviewModal.js deleted file mode 100644 index c58123280..000000000 --- a/app/assets/javascripts/directives/views/revisionPreviewModal.js +++ /dev/null @@ -1,129 +0,0 @@ -import { - PAYLOAD_SOURCE_REMOTE_ACTION_RETRIEVED, - ContentTypes -} from 'snjs'; -import template from '%/directives/revision-preview-modal.pug'; - -class RevisionPreviewModalCtrl { - /* @ngInject */ - constructor( - $element, - $timeout - ) { - this.$element = $element; - this.$timeout = $timeout; - } - - $onInit() { - this.configure(); - } - - $onDestroy() { - if (this.unregisterComponent) { - this.unregisterComponent(); - this.unregisterComponent = null; - } - } - - async configure() { - this.note = await this.application.createTemplateItem({ - contentType: ContentType.Note, - content: this.content - }); - - /** - * Set UUID so editoForNote can find proper editor, but then generate new uuid - * for note as not to save changes to original, if editor makes changes. - */ - this.note.uuid = this.uuid; - const editorForNote = this.application.componentManager.editorForNote(this.note); - this.note.uuid = await this.application.generateUuid(); - if (editorForNote) { - /** - * Create temporary copy, as a lot of componentManager is uuid based, so might - * interfere with active editor. Be sure to copy only the content, as the top level - * editor object has non-copyable properties like .window, which cannot be transfered - */ - const editorCopy = await this.application.createTemplateItem({ - contentType: ContentType.Component, - content: editorForNote.content - }); - this.application.component.setReadonlyStateForComponent(editorCopy, true, true); - this.unregisterComponent = this.application.componentManager.registerHandler({ - identifier: editorCopy.uuid, - areas: ['editor-editor'], - contextRequestHandler: (component) => { - if (component === this.editor) { - return this.note; - } - }, - componentForSessionKeyHandler: (key) => { - if (key === this.editor.sessionKey) { - return this.editor; - } - } - }); - - this.editor = editorCopy; - } - } - - restore(asCopy) { - const run = async () => { - let item; - if (asCopy) { - const contentCopy = Object.assign({}, this.content); - if (contentCopy.title) { - contentCopy.title += " (copy)"; - } - item = await this.application.createManagedItem({ - contentType: 'Note', - content: contentCopy, - needsSync: true - }); - } else { - const uuid = this.uuid; - item = this.application.findItem({ uuid: uuid }); - item.content = Object.assign({}, this.content); - await this.application.mergeItem({ - item: item, - source: PAYLOAD_SOURCE_REMOTE_ACTION_RETRIEVED - }); - } - this.application.saveItem({ item }); - this.dismiss(); - }; - - if (!asCopy) { - this.application.alertService.confirm({ - text: "Are you sure you want to replace the current note's contents with what you see in this preview?", - destructive: true, - onConfirm: run - }); - } else { - run(); - } - } - - dismiss() { - const elem = this.$element; - const scope = elem.scope(); - scope.$destroy(); - elem.remove(); - } -} - -export class RevisionPreviewModal { - constructor() { - this.restrict = 'E'; - this.template = template; - this.controller = RevisionPreviewModalCtrl; - this.controllerAs = 'ctrl'; - this.bindToController = true; - this.scope = { - uuid: '=', - content: '=', - application: '=' - }; - } -} diff --git a/app/assets/javascripts/directives/views/revisionPreviewModal.ts b/app/assets/javascripts/directives/views/revisionPreviewModal.ts new file mode 100644 index 000000000..05cac5965 --- /dev/null +++ b/app/assets/javascripts/directives/views/revisionPreviewModal.ts @@ -0,0 +1,151 @@ +import { WebApplication } from '@/application'; +import { WebDirective } from './../../types'; +import { + ContentType, + PayloadSource, + SNComponent, + SNNote, + ComponentArea +} from 'snjs'; +import template from '%/directives/revision-preview-modal.pug'; +import { PayloadContent } from '@/../../../../snjs/dist/@types/protocol/payloads/generator'; + +interface RevisionPreviewScope { + uuid: string + content: PayloadContent + application: WebApplication +} + +class RevisionPreviewModalCtrl implements RevisionPreviewScope { + + $element: JQLite + $timeout: ng.ITimeoutService + uuid!: string + content!: PayloadContent + application!: WebApplication + unregisterComponent?: any + note!: SNNote + editor?: SNComponent + + /* @ngInject */ + constructor( + $element: JQLite, + $timeout: ng.ITimeoutService + ) { + this.$element = $element; + this.$timeout = $timeout; + } + + $onInit() { + this.configure(); + } + + $onDestroy() { + if (this.unregisterComponent) { + this.unregisterComponent(); + this.unregisterComponent = undefined; + } + } + + get componentManager() { + return this.application.componentManager!; + } + + async configure() { + this.note = await this.application.createTemplateItem( + ContentType.Note, + this.content + ) as SNNote; + const originalNote = this.application.findItem(this.uuid) as SNNote; + const editorForNote = this.componentManager.editorForNote(originalNote); + if (editorForNote) { + /** + * Create temporary copy, as a lot of componentManager is uuid based, so might + * interfere with active editor. Be sure to copy only the content, as the top level + * editor object has non-copyable properties like .window, which cannot be transfered + */ + const editorCopy = await this.application.createTemplateItem( + ContentType.Component, + editorForNote.safeContent + ) as SNComponent; + this.componentManager.setReadonlyStateForComponent(editorCopy, true, true); + this.unregisterComponent = this.componentManager.registerHandler({ + identifier: editorCopy.uuid, + areas: [ComponentArea.Editor], + contextRequestHandler: (component) => { + if (component === this.editor) { + return this.note; + } + }, + componentForSessionKeyHandler: (key) => { + if (key === this.componentManager.sessionKeyForComponent(this.editor!)) { + return this.editor; + } + } + }); + + this.editor = editorCopy; + } + } + + restore(asCopy: boolean) { + const run = async () => { + let item; + if (asCopy) { + const contentCopy = Object.assign({}, this.content); + if (contentCopy.title) { + contentCopy.title += " (copy)"; + } + item = await this.application.createManagedItem( + ContentType.Note, + contentCopy, + true + ); + } else { + const uuid = this.uuid; + item = this.application.findItem(uuid)!; + this.application.changeAndSaveItem(item.uuid, (mutator) => { + mutator.setContent(this.content); + }, true, PayloadSource.RemoteActionRetrieved); + } + this.dismiss(); + }; + + if (!asCopy) { + this.application.alertService!.confirm( + "Are you sure you want to replace the current note's contents with what you see in this preview?", + undefined, + undefined, + undefined, + run, + undefined, + true, + ); + } else { + run(); + } + } + + dismiss() { + const elem = this.$element; + const scope = elem.scope(); + scope.$destroy(); + elem.remove(); + } +} + +export class RevisionPreviewModal extends WebDirective { + constructor() { + super(); + this.restrict = 'E'; + this.template = template; + this.controller = RevisionPreviewModalCtrl; + this.controllerAs = 'ctrl'; + this.bindToController = true; + this.scope = { + uuid: '=', + content: '=', + application: '=' + }; + } +} diff --git a/app/assets/javascripts/directives/views/sessionHistoryMenu.js b/app/assets/javascripts/directives/views/sessionHistoryMenu.js deleted file mode 100644 index 637619b1c..000000000 --- a/app/assets/javascripts/directives/views/sessionHistoryMenu.js +++ /dev/null @@ -1,113 +0,0 @@ -import template from '%/directives/session-history-menu.pug'; - -class SessionHistoryMenuCtrl { - /* @ngInject */ - constructor( - $timeout - ) { - 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.item.updated_at < b.item.updated_at ? 1 : -1; - }); - this.history = history; - } - - openRevision(revision) { - this.application.presentRevisionPreviewModal( - revision.item.uuid, - revision.item.content - ); - } - - classForRevision(revision) { - const vector = revision.operationVector(); - if (vector === 0) { - return 'default'; - } else if (vector === 1) { - return 'success'; - } else if (vector === -1) { - return 'danger'; - } - } - - clearItemHistory() { - this.application.alertService.confirm({ - text: "Are you sure you want to delete the local session history for this note?", - destructive: true, - onConfirm: () => { - this.application.historyManager.clearHistoryForItem(this.item).then(() => { - this.$timeout(() => { - this.reloadHistory(); - }); - }); - } - }); - } - - clearAllHistory() { - this.application.alertService.confirm({ - text: "Are you sure you want to delete the local session history for all notes?", - destructive: true, - onConfirm: () => { - this.application.historyManager.clearAllHistory().then(() => { - this.$timeout(() => { - this.reloadHistory(); - }); - }); - } - }); - } - - toggleDiskSaving() { - const run = () => { - this.application.historyManager.toggleDiskSaving().then(() => { - this.$timeout(() => { - this.diskEnabled = this.application.historyManager.diskEnabled; - }); - }); - }; - if (!this.application.historyManager.diskEnabled) { - this.application.alertService.confirm({ - 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.`, - destructive: true, - onConfirm: run - }); - } else { - run(); - } - } - - toggleAutoOptimize() { - this.application.historyManager.toggleAutoOptimize().then(() => { - this.$timeout(() => { - this.autoOptimize = this.application.historyManager.autoOptimize; - }); - }); - } -} - -export class SessionHistoryMenu { - constructor() { - this.restrict = 'E'; - this.template = template; - this.controller = SessionHistoryMenuCtrl; - this.controllerAs = 'ctrl'; - this.bindToController = true; - this.scope = { - item: '=', - application: '=' - }; - } -} diff --git a/app/assets/javascripts/directives/views/sessionHistoryMenu.ts b/app/assets/javascripts/directives/views/sessionHistoryMenu.ts new file mode 100644 index 000000000..6f142b32d --- /dev/null +++ b/app/assets/javascripts/directives/views/sessionHistoryMenu.ts @@ -0,0 +1,143 @@ +import { WebDirective } from './../../types'; +import { WebApplication } from '@/application'; +import template from '%/directives/session-history-menu.pug'; +import { SNItem, ItemHistoryEntry, ItemHistory } from '@/../../../../snjs/dist/@types'; + +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'; + } + } + + clearItemHistory() { + this.application.alertService!.confirm( + "Are you sure you want to delete the local session history for this note?", + undefined, + undefined, + undefined, + () => { + this.application.historyManager!.clearHistoryForItem(this.item).then(() => { + this.$timeout(() => { + this.reloadHistory(); + }); + }); + }, + undefined, + true, + ); + } + + clearAllHistory() { + this.application.alertService!.confirm( + "Are you sure you want to delete the local session history for all notes?", + undefined, + undefined, + undefined, + () => { + this.application.historyManager!.clearAllHistory().then(() => { + this.$timeout(() => { + this.reloadHistory(); + }); + }); + }, + undefined, + true, + ); + } + + toggleDiskSaving() { + const run = () => { + this.application.historyManager!.toggleDiskSaving().then(() => { + this.$timeout(() => { + this.diskEnabled = this.application.historyManager!.isDiskEnabled(); + }); + }); + }; + if (!this.application.historyManager!.isDiskEnabled()) { + this.application.alertService!.confirm( + `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.`, + undefined, + undefined, + undefined, + run, + undefined, + true, + ); + } 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: '=' + }; + } +}