diff --git a/app/assets/javascripts/app.ts b/app/assets/javascripts/app.ts index 8f1aab4d7..f93fd2414 100644 --- a/app/assets/javascripts/app.ts +++ b/app/assets/javascripts/app.ts @@ -43,7 +43,6 @@ import { MenuRow, PanelResizer, PasswordWizard, - PermissionsModal, RevisionPreviewModal, HistoryMenu, SyncResolutionMenu, @@ -57,7 +56,7 @@ import { StartApplication } from './startApplication'; import { Bridge } from './services/bridge'; import { SessionsModalDirective } from './components/SessionsModal'; import { NoAccountWarningDirective } from './components/NoAccountWarning'; - +import { NoProtectionsdNoteWarningDirective } from './components/NoProtectionsNoteWarning'; function reloadHiddenFirefoxTab(): boolean { /** @@ -67,14 +66,15 @@ function reloadHiddenFirefoxTab(): boolean { */ if ( document.hidden && - navigator.userAgent.toLowerCase().includes('firefox') && - !localStorage.getItem('reloading') + navigator.userAgent.toLowerCase().includes('firefox') ) { - localStorage.setItem('reloading', 'true'); - location.reload(); + document.addEventListener('visibilitychange', () => { + if (!document.hidden) { + location.reload(); + } + }); return true; } else { - localStorage.removeItem('reloading'); return false; } } @@ -138,12 +138,12 @@ const startApplication: StartApplication = async function startApplication( .directive('menuRow', () => new MenuRow()) .directive('panelResizer', () => new PanelResizer()) .directive('passwordWizard', () => new PasswordWizard()) - .directive('permissionsModal', () => new PermissionsModal()) .directive('revisionPreviewModal', () => new RevisionPreviewModal()) .directive('historyMenu', () => new HistoryMenu()) .directive('syncResolutionMenu', () => new SyncResolutionMenu()) .directive('sessionsModal', SessionsModalDirective) - .directive('noAccountWarning', NoAccountWarningDirective); + .directive('noAccountWarning', NoAccountWarningDirective) + .directive('protectedNotePanel', NoProtectionsdNoteWarningDirective); // Filters angular.module('app').filter('trusted', ['$sce', trusted]); @@ -170,7 +170,10 @@ const startApplication: StartApplication = async function startApplication( }; if (__WEB__) { - startApplication((window as any)._default_sync_server, new BrowserBridge(__VERSION__)); + startApplication( + (window as any)._default_sync_server, + new BrowserBridge(__VERSION__) + ); } else { (window as any).startApplication = startApplication; } diff --git a/app/assets/javascripts/components/NoAccountWarning.tsx b/app/assets/javascripts/components/NoAccountWarning.tsx index 83b7dc9c4..f9f8ea719 100644 --- a/app/assets/javascripts/components/NoAccountWarning.tsx +++ b/app/assets/javascripts/components/NoAccountWarning.tsx @@ -2,7 +2,9 @@ import { toDirective, useAutorunValue } from './utils'; import Close from '../../icons/ic_close.svg'; import { AppState } from '@/ui_models/app_state'; -function NoAccountWarning({ appState }: { appState: AppState }) { +type Props = { appState: AppState }; + +function NoAccountWarning({ appState }: Props) { const canShow = useAutorunValue(() => appState.noAccountWarning.show); if (!canShow) { return null; @@ -14,7 +16,7 @@ function NoAccountWarning({ appState }: { appState: AppState }) { Sign in or register to back up your notes.

); } -export const NoAccountWarningDirective = toDirective(NoAccountWarning); +export const NoAccountWarningDirective = toDirective(NoAccountWarning); diff --git a/app/assets/javascripts/components/NoProtectionsNoteWarning.tsx b/app/assets/javascripts/components/NoProtectionsNoteWarning.tsx new file mode 100644 index 000000000..074bf9288 --- /dev/null +++ b/app/assets/javascripts/components/NoProtectionsNoteWarning.tsx @@ -0,0 +1,36 @@ +import { AppState } from '@/ui_models/app_state'; +import { toDirective } from './utils'; + +type Props = { appState: AppState; onViewNote: () => void }; + +function NoProtectionsNoteWarning({ appState, onViewNote }: Props) { + return ( +
+

This note is protected

+

+ Add a passcode or create an account to require authentication to view + this note. +

+
+ + +
+
+ ); +} + +export const NoProtectionsdNoteWarningDirective = toDirective( + NoProtectionsNoteWarning, + { + onViewNote: '&', + } +); diff --git a/app/assets/javascripts/components/SessionsModal.tsx b/app/assets/javascripts/components/SessionsModal.tsx index 67c534a9e..e00b07a26 100644 --- a/app/assets/javascripts/components/SessionsModal.tsx +++ b/app/assets/javascripts/components/SessionsModal.tsx @@ -171,7 +171,7 @@ const SessionsModal: FunctionComponent<{ {formatter.format(session.updated_at)}

- diff --git a/app/assets/javascripts/components/utils.ts b/app/assets/javascripts/components/utils.ts index 96fa8a4f8..f1918328f 100644 --- a/app/assets/javascripts/components/utils.ts +++ b/app/assets/javascripts/components/utils.ts @@ -20,11 +20,9 @@ export function useAutorun( useEffect(() => autorun(view, opts), [view, opts]); } -export function toDirective( - component: FunctionComponent<{ - application: WebApplication; - appState: AppState; - }> +export function toDirective( + component: FunctionComponent, + scope: Record = {} ) { // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types return function () { @@ -37,10 +35,7 @@ export function toDirective( return { $onChanges() { render( - h(component, { - application: $scope.application, - appState: $scope.appState, - }), + h(component, $scope), $element[0] ); }, @@ -50,6 +45,7 @@ export function toDirective( scope: { application: '=', appState: '=', + ...scope, }, }; }; diff --git a/app/assets/javascripts/directives/views/accountMenu.ts b/app/assets/javascripts/directives/views/accountMenu.ts index 6012d5fbe..10c35da52 100644 --- a/app/assets/javascripts/directives/views/accountMenu.ts +++ b/app/assets/javascripts/directives/views/accountMenu.ts @@ -533,7 +533,7 @@ class AccountMenuCtrl extends PureViewCtrl { if (this.application!.hasPasscode()) { await this.application!.changePasscode(passcode); } else { - await this.application!.setPasscode(passcode); + await this.application!.addPasscode(passcode); } } ); diff --git a/app/assets/javascripts/directives/views/index.ts b/app/assets/javascripts/directives/views/index.ts index 99931e2bb..386e123d1 100644 --- a/app/assets/javascripts/directives/views/index.ts +++ b/app/assets/javascripts/directives/views/index.ts @@ -7,7 +7,6 @@ export { InputModal } from './inputModal'; export { MenuRow } from './menuRow'; export { PanelResizer } from './panelResizer'; export { PasswordWizard } from './passwordWizard'; -export { PermissionsModal } from './permissionsModal'; export { RevisionPreviewModal } from './revisionPreviewModal'; export { HistoryMenu } from './historyMenu'; export { SyncResolutionMenu } from './syncResolutionMenu'; diff --git a/app/assets/javascripts/directives/views/permissionsModal.ts b/app/assets/javascripts/directives/views/permissionsModal.ts deleted file mode 100644 index b547fec05..000000000 --- a/app/assets/javascripts/directives/views/permissionsModal.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { WebDirective } from './../../types'; -import template from '%/directives/permissions-modal.pug'; - -class PermissionsModalCtrl { - - $element: JQLite - callback!: (success: boolean) => void - - /* @ngInject */ - constructor($element: JQLite) { - this.$element = $element; - } - - dismiss() { - const elem = this.$element; - const scope = elem.scope(); - scope.$destroy(); - elem.remove(); - } - - accept() { - this.callback(true); - this.dismiss(); - } - - deny() { - this.callback(false); - this.dismiss(); - } -} - -export class PermissionsModal extends WebDirective { - constructor() { - super(); - this.restrict = 'E'; - this.template = template; - this.controller = PermissionsModalCtrl; - this.controllerAs = 'ctrl'; - this.bindToController = true; - this.scope = { - show: '=', - component: '=', - permissionsString: '=', - callback: '=' - }; - } -} diff --git a/app/assets/javascripts/index.ts b/app/assets/javascripts/index.ts index d623c79f5..592300a31 100644 --- a/app/assets/javascripts/index.ts +++ b/app/assets/javascripts/index.ts @@ -4,7 +4,6 @@ import '@reach/dialog/styles.css'; import 'sn-stylekit/dist/stylekit.css'; import '../stylesheets/index.css.scss'; -// import '../stylesheets/_reach-sub.scss'; // Vendor import 'angular'; diff --git a/app/assets/javascripts/services/bridge.ts b/app/assets/javascripts/services/bridge.ts index 7b0da9931..5cf08d83a 100644 --- a/app/assets/javascripts/services/bridge.ts +++ b/app/assets/javascripts/services/bridge.ts @@ -17,6 +17,7 @@ export interface Bridge { syncComponents(payloads: unknown[]): void; onMajorDataChange(): void; onInitialDataLoad(): void; + onSignOut(): void; onSearch(text?: string): void; downloadBackup(): void | Promise; } diff --git a/app/assets/javascripts/services/browserBridge.ts b/app/assets/javascripts/services/browserBridge.ts index d0408ffa6..29796ec80 100644 --- a/app/assets/javascripts/services/browserBridge.ts +++ b/app/assets/javascripts/services/browserBridge.ts @@ -30,4 +30,5 @@ export class BrowserBridge implements Bridge { onInitialDataLoad(): void {} onSearch(): void {} downloadBackup(): void {} + onSignOut(): void {} } diff --git a/app/assets/javascripts/services/desktopManager.ts b/app/assets/javascripts/services/desktopManager.ts index 3587b174c..671663f22 100644 --- a/app/assets/javascripts/services/desktopManager.ts +++ b/app/assets/javascripts/services/desktopManager.ts @@ -7,6 +7,7 @@ import { ApplicationService, ApplicationEvent, removeFromArray, + BackupFile, } from '@standardnotes/snjs'; /* eslint-disable camelcase */ import { WebApplication } from '@/ui_models/application'; @@ -14,33 +15,32 @@ import { WebApplication } from '@/ui_models/application'; import { isDesktopApplication } from '@/utils'; import { Bridge } from './bridge'; -type UpdateObserverCallback = (component: SNComponent) => void -type ComponentActivationCallback = (payload: PurePayload) => void +type UpdateObserverCallback = (component: SNComponent) => void; +type ComponentActivationCallback = (payload: PurePayload) => void; type ComponentActivationObserver = { id: string; callback: ComponentActivationCallback; -} +}; export class DesktopManager extends ApplicationService { - - $rootScope: ng.IRootScopeService - $timeout: ng.ITimeoutService - componentActivationObservers: ComponentActivationObserver[] = [] + $rootScope: ng.IRootScopeService; + $timeout: ng.ITimeoutService; + componentActivationObservers: ComponentActivationObserver[] = []; updateObservers: { callback: UpdateObserverCallback; }[] = []; isDesktop = isDesktopApplication(); - dataLoaded = false - lastSearchedText?: string + dataLoaded = false; + lastSearchedText?: string; private removeComponentObserver?: () => void; constructor( $rootScope: ng.IRootScopeService, $timeout: ng.ITimeoutService, application: WebApplication, - private bridge: Bridge, + private bridge: Bridge ) { super(application); this.$rootScope = $rootScope; @@ -74,10 +74,7 @@ export class DesktopManager extends ApplicationService { } getExtServerHost() { - console.assert( - !!this.bridge.extensionsServerHost, - 'extServerHost is null' - ); + console.assert(!!this.bridge.extensionsServerHost, 'extServerHost is null'); return this.bridge.extensionsServerHost; } @@ -97,12 +94,14 @@ export class DesktopManager extends ApplicationService { if (!this.isDesktop) { return; } - Promise.all(components.map((component) => { - return this.convertComponentForTransmission(component); - })).then((payloads) => { + Promise.all( + components.map((component) => { + return this.convertComponentForTransmission(component); + }) + ).then((payloads) => { this.bridge.syncComponents( - payloads.filter(payload => - !payload.errorDecrypting && !payload.waitingForKey + payloads.filter( + (payload) => !payload.errorDecrypting && !payload.waitingForKey ) ); }); @@ -110,7 +109,7 @@ export class DesktopManager extends ApplicationService { registerUpdateObserver(callback: UpdateObserverCallback) { const observer = { - callback: callback + callback: callback, }; this.updateObservers.push(observer); return () => { @@ -153,19 +152,14 @@ export class DesktopManager extends ApplicationService { (m) => { const mutator = m as ComponentMutator; if (error) { - mutator.setAppDataItem( - AppDataField.ComponentInstallError, - error - ); + mutator.setAppDataItem(AppDataField.ComponentInstallError, error); } else { mutator.local_url = componentData.content.local_url; mutator.package_info = componentData.content.package_info; - mutator.setAppDataItem( - AppDataField.ComponentInstallError, - undefined - ); + mutator.setAppDataItem(AppDataField.ComponentInstallError, undefined); } - }); + } + ); this.$timeout(() => { for (const observer of this.updateObservers) { @@ -174,13 +168,17 @@ export class DesktopManager extends ApplicationService { }); } - desktop_registerComponentActivationObserver(callback: ComponentActivationCallback) { + desktop_registerComponentActivationObserver( + callback: ComponentActivationCallback + ) { const observer = { id: `${Math.random}`, callback: callback }; this.componentActivationObservers.push(observer); return observer; } - desktop_deregisterComponentActivationObserver(observer: ComponentActivationObserver) { + desktop_deregisterComponentActivationObserver( + observer: ComponentActivationObserver + ) { removeFromArray(this.componentActivationObservers, observer); } @@ -198,7 +196,9 @@ export class DesktopManager extends ApplicationService { async desktop_requestBackupFile() { const data = await this.application!.createBackupFile( - EncryptionIntent.FileEncrypted + this.application.hasProtectionSources() + ? EncryptionIntent.FileEncrypted + : EncryptionIntent.FileDecrypted ); if (data) { return JSON.stringify(data, null, 2); diff --git a/app/assets/javascripts/ui_models/application.ts b/app/assets/javascripts/ui_models/application.ts index 38cc85274..07256a10e 100644 --- a/app/assets/javascripts/ui_models/application.ts +++ b/app/assets/javascripts/ui_models/application.ts @@ -6,13 +6,12 @@ import { InputModalScope } from '@/directives/views/inputModal'; import { PasswordWizardType, PasswordWizardScope } from '@/types'; import { SNApplication, - platformFromString, SNComponent, PermissionDialog, DeinitSource, } from '@standardnotes/snjs'; import angular from 'angular'; -import { getPlatform, getPlatformString } from '@/utils'; +import { getPlatform } from '@/utils'; import { AlertService } from '@/services/alertService'; import { WebDeviceInterface } from '@/web_device_interface'; import { @@ -96,6 +95,9 @@ export class WebApplication extends SNApplication { * to complete before destroying the global application instance and all its services */ setTimeout(() => { super.deinit(source); + if (source === DeinitSource.SignOut) { + this.bridge.onSignOut(); + } }, 0); } @@ -204,10 +206,17 @@ export class WebApplication extends SNApplication { } async openModalComponent(component: SNComponent): Promise { - if (component.package_info?.identifier === "org.standardnotes.batch-manager") { - if (!await this.authorizeBatchManagerAccess()) { - return; - } + switch (component.package_info?.identifier) { + case 'org.standardnotes.batch-manager': + if (!await this.authorizeBatchManagerAccess()) { + return; + } + break; + case 'org.standardnotes.cloudlink': + if (!await this.authorizeCloudLinkAccess()) { + return; + } + break; } const scope = this.scope!.$new(true) as Partial; scope.componentUuid = component.uuid; diff --git a/app/assets/javascripts/views/account_switcher/account-switcher.pug b/app/assets/javascripts/views/account_switcher/account-switcher.pug index 8596611a7..b65e05fa9 100644 --- a/app/assets/javascripts/views/account_switcher/account-switcher.pug +++ b/app/assets/javascripts/views/account_switcher/account-switcher.pug @@ -26,7 +26,6 @@ .sk-sublabel(ng-if='descriptor.identifier == ctrl.activeApplication.identifier') | Current Application .sk-menu-panel-column(ng-if='descriptor.identifier == ctrl.activeApplication.identifier') - .sk-button.success( + button.sn-button.success( ng-click='ctrl.renameDescriptor($event, descriptor)' - ) - .sk-label Rename \ No newline at end of file + ) Rename diff --git a/app/assets/javascripts/views/application/application-view.pug b/app/assets/javascripts/views/application/application-view.pug index b428a9fab..34d47c37e 100644 --- a/app/assets/javascripts/views/application/application-view.pug +++ b/app/assets/javascripts/views/application/application-view.pug @@ -1,4 +1,4 @@ -.main-ui-view( +.main-ui-view.sn-component( ng-class='self.platformString' ) #app.app( diff --git a/app/assets/javascripts/views/challenge_modal/challenge_modal.tsx b/app/assets/javascripts/views/challenge_modal/challenge_modal.tsx index a1f486b7c..d5564f59d 100644 --- a/app/assets/javascripts/views/challenge_modal/challenge_modal.tsx +++ b/app/assets/javascripts/views/challenge_modal/challenge_modal.tsx @@ -279,18 +279,16 @@ function ChallengeModalView({ ctrl }: { ctrl: ChallengeModalCtrl }) {
-
ctrl.submit()} > -
- {ctrl.state.processing ? 'Generating Keys…' : 'Submit'} -
-
+ {ctrl.state.processing ? 'Generating Keys…' : 'Submit'} + {ctrl.challenge.cancelable && ( <>
diff --git a/app/assets/javascripts/views/editor/editor-view.pug b/app/assets/javascripts/views/editor/editor-view.pug index 4784a9151..a19b4c0fe 100644 --- a/app/assets/javascripts/views/editor/editor-view.pug +++ b/app/assets/javascripts/views/editor/editor-view.pug @@ -1,267 +1,275 @@ #editor-column.section.editor.sn-component(aria-label='Note') - .sn-component - .sk-app-bar.no-edges( - ng-if='self.noteLocked', - ng-init="self.lockText = 'Note Locked'", - ng-mouseleave="self.lockText = 'Note Locked'", - ng-mouseover="self.lockText = 'Unlock'" - ) - .left - .sk-app-bar-item(ng-click='self.toggleLockNote()') - .sk-label.warning - i.icon.ion-locked - | {{self.lockText}} - #editor-title-bar.section-title-bar( - ng-class="{'locked' : self.noteLocked}", - ng-show='self.note && !self.note.errorDecrypting' - ) - .title - input#note-title-editor.input( - ng-blur='self.onTitleBlur()', - ng-change='self.onTitleChange()', - ng-disabled='self.noteLocked', - ng-focus='self.onTitleFocus()', - ng-keyup='$event.keyCode == 13 && self.onTitleEnter($event)', - ng-model='self.editorValues.title', - select-on-focus='true', - spellcheck='false' - ) - #save-status - .message( - ng-class="{'warning sk-bold': self.state.syncTakingTooLong, 'danger sk-bold': self.state.saveError}" - ) {{self.state.noteStatus.message}} - .desc(ng-show='self.state.noteStatus.desc') {{self.state.noteStatus.desc}} - .editor-tags - #note-tags-component-container(ng-if='self.state.tagsComponent && !self.note.errorDecrypting') - component-view.component-view( - component-uuid='self.state.tagsComponent.uuid', - ng-class="{'locked' : self.noteLocked}", - ng-style="self.noteLocked && {'pointer-events' : 'none'}", - application='self.application' - ) - input.tags-input( - ng-blur='self.onTagsInputBlur()', - ng-disabled='self.noteLocked', - ng-if='!self.state.tagsComponent', - ng-keyup='$event.keyCode == 13 && $event.target.blur();', - ng-model='self.editorValues.tagsInputValue', - placeholder='#tags', - spellcheck='false', - type='text' - ) - .sn-component(ng-if='self.note') - #editor-menu-bar.sk-app-bar.no-edges - .left - .sk-app-bar-item( - click-outside=`self.setMenuState('showOptionsMenu', false)`, - is-open='self.state.showOptionsMenu', - ng-class="{'selected' : self.state.showOptionsMenu}", - ng-click="self.toggleMenu('showOptionsMenu')" - ) - .sk-label Options - .sk-menu-panel.dropdown-menu(ng-if='self.state.showOptionsMenu') - .sk-menu-panel-section - .sk-menu-panel-header - .sk-menu-panel-header-title Note Options - menu-row( - action='self.selectedMenuItem(true); self.togglePin()' - desc="'Pin or unpin a note from the top of your list'", - label="self.note.pinned ? 'Unpin' : 'Pin'" - ) - menu-row( - action='self.selectedMenuItem(true); self.toggleArchiveNote()' - desc="'Archive or unarchive a note from your Archived system tag'", - label="self.note.archived ? 'Unarchive' : 'Archive'" - ) - menu-row( - action='self.selectedMenuItem(true); self.toggleLockNote()' - desc="'Locking notes prevents unintentional editing'", - label="self.noteLocked ? 'Unlock' : 'Lock'" - ) - menu-row( - action='self.selectedMenuItem(true); self.toggleProtectNote()' - desc=`'Protecting a note will require credentials to view it'`, - label="self.note.protected ? 'Unprotect' : 'Protect'" - ) - menu-row( - action='self.selectedMenuItem(true); self.toggleNotePreview()' - circle="self.note.hidePreview ? 'danger' : 'success'", - circle-align="'right'", - desc="'Hide or unhide the note preview from the list of notes'", - label="'Preview'" - ) - menu-row( - action='self.selectedMenuItem(); self.deleteNote()' - desc="'Send this note to the trash'", - label="'Move to Trash'", - ng-show='!self.state.altKeyDown && !self.note.trashed && !self.note.errorDecrypting', - stylekit-class="'warning'" - ) - menu-row( - action='self.selectedMenuItem(); self.deleteNotePermanantely()' - desc="'Delete this note permanently from all your devices'", - label="'Delete Permanently'", - ng-show='!self.note.trashed && self.note.errorDecrypting', - stylekit-class="'danger'" - ) - div(ng-if='self.note.trashed || self.state.altKeyDown') - menu-row( - action='self.selectedMenuItem(true); self.restoreTrashedNote()' - desc="'Undelete this note and restore it back into your notes'", - label="'Restore'", - ng-show='self.note.trashed', - stylekit-class="'info'" - ) - menu-row( - action='self.selectedMenuItem(true); self.deleteNotePermanantely()' - desc="'Delete this note permanently from all your devices'", - label="'Delete Permanently'", - stylekit-class="'danger'" - ) - menu-row( - action='self.selectedMenuItem(true); self.emptyTrash()' - desc="'Permanently delete all notes in the trash'", - label="'Empty Trash'", - ng-show='self.note.trashed || !self.state.altKeyDown', - stylekit-class="'danger'", - subtitle="self.getTrashCount() + ' notes in trash'" - ) - .sk-menu-panel-section - .sk-menu-panel-header - .sk-menu-panel-header-title Global Display - menu-row( - action="self.selectedMenuItem(true); self.toggleWebPrefKey(self.prefKeyMonospace)" - circle="self.state.monospaceFont ? 'success' : 'neutral'", - desc="'Toggles the font style for the default editor'", - disabled='self.state.editorComponent', - label="'Monospace Font'", - subtitle="self.state.editorComponent ? 'Not available with editor extensions' : null" - ) - menu-row( - action="self.selectedMenuItem(true); self.toggleWebPrefKey(self.prefKeySpellcheck)" - circle="self.state.spellcheck ? 'success' : 'neutral'", - desc="'Toggles spellcheck for the default editor'", - disabled='self.state.editorComponent', - label="'Spellcheck'", - subtitle=` - self.state.editorComponent - ? 'Not available with editor extensions' - : (self.state.isDesktop ? 'May degrade editor performance' : null) - `) - menu-row( - action="self.selectedMenuItem(true); self.toggleWebPrefKey(self.prefKeyMarginResizers)" - circle="self.state.marginResizersEnabled ? 'success' : 'neutral'", - desc="'Allows for editor left and right margins to be resized'", - faded='!self.state.marginResizersEnabled', - label="'Margin Resizers'" - ) - .sk-app-bar-item( - click-outside=`self.setMenuState('showEditorMenu', false)` - is-open='self.state.showEditorMenu', - ng-class="{'selected' : self.state.showEditorMenu}", - ng-click="self.toggleMenu('showEditorMenu')" - ) - .sk-label Editor - editor-menu( - callback='self.editorMenuOnSelect', - current-item='self.note', - ng-if='self.state.showEditorMenu', - selected-editor-uuid='self.state.editorComponent && self.state.editorComponent.uuid', - application='self.application' - ) - .sk-app-bar-item( - click-outside=`self.setMenuState('showActionsMenu', false)`, - is-open='self.state.showActionsMenu', - ng-class="{'selected' : self.state.showActionsMenu}", - ng-click="self.toggleMenu('showActionsMenu')" - ) - .sk-label Actions - actions-menu( - item='self.note', - ng-if='self.state.showActionsMenu', - application='self.application' - ) - .sk-app-bar-item( - click-outside=`self.setMenuState('showHistoryMenu', false)`, - is-open='self.state.showHistoryMenu', - ng-class="{'selected' : self.state.showHistoryMenu}", - ng-click="self.toggleMenu('showHistoryMenu')" - ) - .sk-label History - history-menu( - item='self.note', - ng-if='self.state.showHistoryMenu', - application='self.application' - ) - #editor-content.editor-content(ng-if='!self.note.errorDecrypting') - panel-resizer.left( - control='self.leftPanelPuppet', - hoverable='true', - min-width='300', - ng-if='self.state.marginResizersEnabled', - on-resize-finish='self.onPanelResizeFinish', - panel-id="'editor-content'", - property="'left'" - ) - component-view.component-view( - component-uuid='self.state.editorComponent.uuid', - ng-if='self.state.editorComponent && !self.state.editorUnloading', - on-load='self.onEditorLoad', - application='self.application' - ) - textarea#note-text-editor.editable.font-editor( - dir='auto', - ng-attr-spellcheck='{{self.state.spellcheck}}', - ng-change='self.contentChanged()', - ng-click='self.clickedTextArea()', - ng-focus='self.onContentFocus()', - ng-if='!self.state.editorComponent && !self.state.textareaUnloading', - ng-model='self.editorValues.text', - ng-model-options='{ debounce: self.state.editorDebounce}', - ng-readonly='self.noteLocked', - ng-trim='false' - autocomplete='off' - ) - | {{self.onSystemEditorLoad()}} - panel-resizer( - control='self.rightPanelPuppet', - hoverable='true', min-width='300', - ng-if='self.state.marginResizersEnabled', - on-resize-finish='self.onPanelResizeFinish', - panel-id="'editor-content'", - property="'right'" - ) - .section(ng-show='self.note.errorDecrypting') - .sn-component#error-decrypting-container - .sk-panel#error-decrypting-panel - .sk-panel-header - .sk-panel-header-title {{self.note.waitingForKey ? 'Waiting for Key' : 'Unable to Decrypt'}} - .sk-panel-content - .sk-panel-section - p.sk-p(ng-if='self.note.waitingForKey') - | This note is awaiting its encryption key to be ready. Please wait for syncing to complete - | for this note to be decrypted. - p.sk-p(ng-if='!self.note.waitingForKey') - | There was an error decrypting this item. Ensure you are running the - | latest version of this app, then sign out and sign back in to try again. - #editor-pane-component-stack(ng-if='!self.note.errorDecrypting' ng-show='self.note') - #component-stack-menu-bar.sk-app-bar.no-edges(ng-if='self.state.stackComponents.length') - .left - .sk-app-bar-item( - ng-repeat='component in self.state.stackComponents track by component.uuid' - ng-click='self.toggleStackComponentForCurrentItem(component)', - ) - .sk-app-bar-item-column - .sk-circle.small( - ng-class="{'info' : !self.stackComponentHidden(component) && component.active, 'neutral' : self.stackComponentHidden(component) || !component.active}" - ) - .sk-app-bar-item-column - .sk-label {{component.name}} + protected-note-panel.h-full.flex.justify-center.items-center( + ng-if='self.state.showProtectedWarning' + app-state='self.appState' + on-view-note='self.dismissProtectedWarning()' + ) + .flex-grow.flex.flex-col( + ng-if='!self.state.showProtectedWarning' + ) .sn-component - component-view.component-view.component-stack-item( - ng-repeat='component in self.state.stackComponents track by component.uuid', - component-uuid='component.uuid', - manual-dealloc='true', - ng-show='!self.stackComponentHidden(component)', - application='self.application' + .sk-app-bar.no-edges( + ng-if='self.noteLocked', + ng-init="self.lockText = 'Note Locked'", + ng-mouseleave="self.lockText = 'Note Locked'", + ng-mouseover="self.lockText = 'Unlock'" + ) + .left + .sk-app-bar-item(ng-click='self.toggleLockNote()') + .sk-label.warning + i.icon.ion-locked + | {{self.lockText}} + #editor-title-bar.section-title-bar( + ng-class="{'locked' : self.noteLocked}", + ng-show='self.note && !self.note.errorDecrypting' ) + .title + input#note-title-editor.input( + ng-blur='self.onTitleBlur()', + ng-change='self.onTitleChange()', + ng-disabled='self.noteLocked', + ng-focus='self.onTitleFocus()', + ng-keyup='$event.keyCode == 13 && self.onTitleEnter($event)', + ng-model='self.editorValues.title', + select-on-focus='true', + spellcheck='false' + ) + #save-status + .message( + ng-class="{'warning sk-bold': self.state.syncTakingTooLong, 'danger sk-bold': self.state.saveError}" + ) {{self.state.noteStatus.message}} + .desc(ng-show='self.state.noteStatus.desc') {{self.state.noteStatus.desc}} + .editor-tags + #note-tags-component-container(ng-if='self.state.tagsComponent && !self.note.errorDecrypting') + component-view.component-view( + component-uuid='self.state.tagsComponent.uuid', + ng-class="{'locked' : self.noteLocked}", + ng-style="self.noteLocked && {'pointer-events' : 'none'}", + application='self.application' + ) + input.tags-input( + ng-blur='self.onTagsInputBlur()', + ng-disabled='self.noteLocked', + ng-if='!self.state.tagsComponent', + ng-keyup='$event.keyCode == 13 && $event.target.blur();', + ng-model='self.editorValues.tagsInputValue', + placeholder='#tags', + spellcheck='false', + type='text' + ) + .sn-component(ng-if='self.note') + #editor-menu-bar.sk-app-bar.no-edges + .left + .sk-app-bar-item( + click-outside=`self.setMenuState('showOptionsMenu', false)`, + is-open='self.state.showOptionsMenu', + ng-class="{'selected' : self.state.showOptionsMenu}", + ng-click="self.toggleMenu('showOptionsMenu')" + ) + .sk-label Options + .sk-menu-panel.dropdown-menu(ng-if='self.state.showOptionsMenu') + .sk-menu-panel-section + .sk-menu-panel-header + .sk-menu-panel-header-title Note Options + menu-row( + action='self.selectedMenuItem(true); self.togglePin()' + desc="'Pin or unpin a note from the top of your list'", + label="self.note.pinned ? 'Unpin' : 'Pin'" + ) + menu-row( + action='self.selectedMenuItem(true); self.toggleArchiveNote()' + desc="'Archive or unarchive a note from your Archived system tag'", + label="self.note.archived ? 'Unarchive' : 'Archive'" + ) + menu-row( + action='self.selectedMenuItem(true); self.toggleLockNote()' + desc="'Locking notes prevents unintentional editing'", + label="self.noteLocked ? 'Unlock' : 'Lock'" + ) + menu-row( + action='self.selectedMenuItem(true); self.toggleProtectNote()' + desc=`'Protecting a note will require credentials to view it'`, + label="self.note.protected ? 'Unprotect' : 'Protect'" + ) + menu-row( + action='self.selectedMenuItem(true); self.toggleNotePreview()' + circle="self.note.hidePreview ? 'danger' : 'success'", + circle-align="'right'", + desc="'Hide or unhide the note preview from the list of notes'", + label="'Preview'" + ) + menu-row( + action='self.selectedMenuItem(); self.deleteNote()' + desc="'Send this note to the trash'", + label="'Move to Trash'", + ng-show='!self.state.altKeyDown && !self.note.trashed && !self.note.errorDecrypting', + stylekit-class="'warning'" + ) + menu-row( + action='self.selectedMenuItem(); self.deleteNotePermanantely()' + desc="'Delete this note permanently from all your devices'", + label="'Delete Permanently'", + ng-show='!self.note.trashed && self.note.errorDecrypting', + stylekit-class="'danger'" + ) + div(ng-if='self.note.trashed || self.state.altKeyDown') + menu-row( + action='self.selectedMenuItem(true); self.restoreTrashedNote()' + desc="'Undelete this note and restore it back into your notes'", + label="'Restore'", + ng-show='self.note.trashed', + stylekit-class="'info'" + ) + menu-row( + action='self.selectedMenuItem(true); self.deleteNotePermanantely()' + desc="'Delete this note permanently from all your devices'", + label="'Delete Permanently'", + stylekit-class="'danger'" + ) + menu-row( + action='self.selectedMenuItem(true); self.emptyTrash()' + desc="'Permanently delete all notes in the trash'", + label="'Empty Trash'", + ng-show='self.note.trashed || !self.state.altKeyDown', + stylekit-class="'danger'", + subtitle="self.getTrashCount() + ' notes in trash'" + ) + .sk-menu-panel-section + .sk-menu-panel-header + .sk-menu-panel-header-title Global Display + menu-row( + action="self.selectedMenuItem(true); self.toggleWebPrefKey(self.prefKeyMonospace)" + circle="self.state.monospaceFont ? 'success' : 'neutral'", + desc="'Toggles the font style for the default editor'", + disabled='self.state.editorComponent', + label="'Monospace Font'", + subtitle="self.state.editorComponent ? 'Not available with editor extensions' : null" + ) + menu-row( + action="self.selectedMenuItem(true); self.toggleWebPrefKey(self.prefKeySpellcheck)" + circle="self.state.spellcheck ? 'success' : 'neutral'", + desc="'Toggles spellcheck for the default editor'", + disabled='self.state.editorComponent', + label="'Spellcheck'", + subtitle=` + self.state.editorComponent + ? 'Not available with editor extensions' + : (self.state.isDesktop ? 'May degrade editor performance' : null) + `) + menu-row( + action="self.selectedMenuItem(true); self.toggleWebPrefKey(self.prefKeyMarginResizers)" + circle="self.state.marginResizersEnabled ? 'success' : 'neutral'", + desc="'Allows for editor left and right margins to be resized'", + faded='!self.state.marginResizersEnabled', + label="'Margin Resizers'" + ) + .sk-app-bar-item( + click-outside=`self.setMenuState('showEditorMenu', false)` + is-open='self.state.showEditorMenu', + ng-class="{'selected' : self.state.showEditorMenu}", + ng-click="self.toggleMenu('showEditorMenu')" + ) + .sk-label Editor + editor-menu( + callback='self.editorMenuOnSelect', + current-item='self.note', + ng-if='self.state.showEditorMenu', + selected-editor-uuid='self.state.editorComponent && self.state.editorComponent.uuid', + application='self.application' + ) + .sk-app-bar-item( + click-outside=`self.setMenuState('showActionsMenu', false)`, + is-open='self.state.showActionsMenu', + ng-class="{'selected' : self.state.showActionsMenu}", + ng-click="self.toggleMenu('showActionsMenu')" + ) + .sk-label Actions + actions-menu( + item='self.note', + ng-if='self.state.showActionsMenu', + application='self.application' + ) + .sk-app-bar-item( + click-outside=`self.setMenuState('showHistoryMenu', false)`, + is-open='self.state.showHistoryMenu', + ng-class="{'selected' : self.state.showHistoryMenu}", + ng-click="self.toggleMenu('showHistoryMenu')" + ) + .sk-label History + history-menu( + item='self.note', + ng-if='self.state.showHistoryMenu', + application='self.application' + ) + #editor-content.editor-content(ng-if='!self.note.errorDecrypting') + panel-resizer.left( + control='self.leftPanelPuppet', + hoverable='true', + min-width='300', + ng-if='self.state.marginResizersEnabled', + on-resize-finish='self.onPanelResizeFinish', + panel-id="'editor-content'", + property="'left'" + ) + component-view.component-view( + component-uuid='self.state.editorComponent.uuid', + ng-if='self.state.editorComponent && !self.state.editorUnloading', + on-load='self.onEditorLoad', + application='self.application' + ) + textarea#note-text-editor.editable.font-editor( + dir='auto', + ng-attr-spellcheck='{{self.state.spellcheck}}', + ng-change='self.contentChanged()', + ng-click='self.clickedTextArea()', + ng-focus='self.onContentFocus()', + ng-if='!self.state.editorComponent && !self.state.textareaUnloading', + ng-model='self.editorValues.text', + ng-model-options='{ debounce: self.state.editorDebounce}', + ng-readonly='self.noteLocked', + ng-trim='false' + autocomplete='off' + ) + | {{self.onSystemEditorLoad()}} + panel-resizer( + control='self.rightPanelPuppet', + hoverable='true', min-width='300', + ng-if='self.state.marginResizersEnabled', + on-resize-finish='self.onPanelResizeFinish', + panel-id="'editor-content'", + property="'right'" + ) + .section(ng-show='self.note.errorDecrypting') + .sn-component#error-decrypting-container + .sk-panel#error-decrypting-panel + .sk-panel-header + .sk-panel-header-title {{self.note.waitingForKey ? 'Waiting for Key' : 'Unable to Decrypt'}} + .sk-panel-content + .sk-panel-section + p.sk-p(ng-if='self.note.waitingForKey') + | This note is awaiting its encryption key to be ready. Please wait for syncing to complete + | for this note to be decrypted. + p.sk-p(ng-if='!self.note.waitingForKey') + | There was an error decrypting this item. Ensure you are running the + | latest version of this app, then sign out and sign back in to try again. + #editor-pane-component-stack(ng-if='!self.note.errorDecrypting' ng-show='self.note') + #component-stack-menu-bar.sk-app-bar.no-edges(ng-if='self.state.stackComponents.length') + .left + .sk-app-bar-item( + ng-repeat='component in self.state.stackComponents track by component.uuid' + ng-click='self.toggleStackComponentForCurrentItem(component)', + ) + .sk-app-bar-item-column + .sk-circle.small( + ng-class="{'info' : !self.stackComponentHidden(component) && component.active, 'neutral' : self.stackComponentHidden(component) || !component.active}" + ) + .sk-app-bar-item-column + .sk-label {{component.name}} + .sn-component + component-view.component-view.component-stack-item( + ng-repeat='component in self.state.stackComponents track by component.uuid', + component-uuid='component.uuid', + manual-dealloc='true', + ng-show='!self.stackComponentHidden(component)', + application='self.application' + ) diff --git a/app/assets/javascripts/views/editor/editor_view.ts b/app/assets/javascripts/views/editor/editor_view.ts index 5f36183d7..c99bb0991 100644 --- a/app/assets/javascripts/views/editor/editor_view.ts +++ b/app/assets/javascripts/views/editor/editor_view.ts @@ -1,4 +1,8 @@ -import { Strings, STRING_ARCHIVE_LOCKED_ATTEMPT, STRING_SAVING_WHILE_DOCUMENT_HIDDEN, STRING_UNARCHIVE_LOCKED_ATTEMPT } from './../../strings'; +import { + STRING_ARCHIVE_LOCKED_ATTEMPT, + STRING_SAVING_WHILE_DOCUMENT_HIDDEN, + STRING_UNARCHIVE_LOCKED_ATTEMPT, +} from './../../strings'; import { Editor } from '@/ui_models/editor'; import { WebApplication } from '@/ui_models/application'; import { PanelPuppet, WebDirective } from '@/types'; @@ -31,7 +35,7 @@ import { STRING_DELETE_PLACEHOLDER_ATTEMPT, STRING_DELETE_LOCKED_ATTEMPT, StringDeleteNote, - StringEmptyTrash + StringEmptyTrash, } from '@/strings'; import { alertDialog, confirmDialog } from '@/services/alertService'; @@ -45,88 +49,91 @@ const ElementIds = { NoteTextEditor: 'note-text-editor', NoteTitleEditor: 'note-title-editor', EditorContent: 'editor-content', - NoteTagsComponentContainer: 'note-tags-component-container' + NoteTagsComponentContainer: 'note-tags-component-container', }; type NoteStatus = { - message?: string - date?: Date -} + message?: string; + date?: Date; +}; type EditorState = { - stackComponents: SNComponent[] - editorComponent?: SNComponent - tagsComponent?: SNComponent - saveError?: any - noteStatus?: NoteStatus - tagsAsStrings?: string - marginResizersEnabled?: boolean - monospaceFont?: boolean - isDesktop?: boolean - syncTakingTooLong: boolean - showActionsMenu: boolean - showOptionsMenu: boolean - showEditorMenu: boolean - showHistoryMenu: boolean - altKeyDown: boolean - spellcheck: boolean + stackComponents: SNComponent[]; + editorComponent?: SNComponent; + tagsComponent?: SNComponent; + saveError?: any; + noteStatus?: NoteStatus; + tagsAsStrings?: string; + marginResizersEnabled?: boolean; + monospaceFont?: boolean; + isDesktop?: boolean; + syncTakingTooLong: boolean; + showActionsMenu: boolean; + showOptionsMenu: boolean; + showEditorMenu: boolean; + showHistoryMenu: boolean; + altKeyDown: boolean; + spellcheck: boolean; /** * Setting to false then true will allow the current editor component-view to be destroyed * then re-initialized. Used when changing between component editors. */ - editorUnloading: boolean + editorUnloading: boolean; /** Setting to true then false will allow the main content textarea to be destroyed * then re-initialized. Used when reloading spellcheck status. */ - textareaUnloading: boolean + textareaUnloading: boolean; /** Fields that can be directly mutated by the template */ - mutable: any -} + mutable: any; + showProtectedWarning: boolean; +}; type EditorValues = { - title?: string - text?: string - tagsInputValue?: string -} + title?: string; + text?: string; + tagsInputValue?: string; +}; function sortAlphabetically(array: SNComponent[]): SNComponent[] { - return array.sort((a, b) => a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1); + return array.sort((a, b) => + a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1 + ); } class EditorViewCtrl extends PureViewCtrl { /** Passed through template */ - readonly application!: WebApplication - readonly editor!: Editor + readonly application!: WebApplication; + readonly editor!: Editor; - private leftPanelPuppet?: PanelPuppet - private rightPanelPuppet?: PanelPuppet - private unregisterComponent: any - private saveTimeout?: ng.IPromise - private statusTimeout?: ng.IPromise - private lastEditorFocusEventSource?: EventSource - public editorValues: EditorValues = {} - onEditorLoad?: () => void + private leftPanelPuppet?: PanelPuppet; + private rightPanelPuppet?: PanelPuppet; + private unregisterComponent: any; + private saveTimeout?: ng.IPromise; + private statusTimeout?: ng.IPromise; + private lastEditorFocusEventSource?: EventSource; + public editorValues: EditorValues = {}; + onEditorLoad?: () => void; private tags: SNTag[] = []; - private removeAltKeyObserver?: any - private removeTrashKeyObserver?: any - private removeTabObserver?: any + private removeAltKeyObserver?: any; + private removeTrashKeyObserver?: any; + private removeTabObserver?: any; - private removeTagsObserver!: () => void - private removeComponentsObserver!: () => void + private removeTagsObserver!: () => void; + private removeComponentsObserver!: () => void; - prefKeyMonospace: string - prefKeySpellcheck: string - prefKeyMarginResizers: string + prefKeyMonospace: string; + prefKeySpellcheck: string; + prefKeyMarginResizers: string; /* @ngInject */ constructor($timeout: ng.ITimeoutService) { super($timeout); this.leftPanelPuppet = { - onReady: () => this.reloadPreferences() + onReady: () => this.reloadPreferences(), }; this.rightPanelPuppet = { - onReady: () => this.reloadPreferences() + onReady: () => this.reloadPreferences(), }; /** Used by .pug template */ this.prefKeyMonospace = PrefKey.EditorMonospaceEnabled; @@ -195,7 +202,9 @@ class EditorViewCtrl extends PureViewCtrl { if (note.lastSyncEnd) { if (note.lastSyncBegan!.getTime() > note.lastSyncEnd!.getTime()) { this.showSavingStatus(); - } else if (note.lastSyncEnd!.getTime() > note.lastSyncBegan!.getTime()) { + } else if ( + note.lastSyncEnd!.getTime() > note.lastSyncBegan!.getTime() + ) { this.showAllChangesSavedStatus(); } } else { @@ -222,8 +231,9 @@ class EditorViewCtrl extends PureViewCtrl { editorUnloading: false, textareaUnloading: false, mutable: { - tagsString: '' - } + tagsString: '', + }, + showProtectedWarning: false, } as EditorState; } @@ -263,8 +273,8 @@ class EditorViewCtrl extends PureViewCtrl { break; case ApplicationEvent.LocalDatabaseWriteError: this.showErrorStatus({ - message: "Offline Saving Issue", - desc: "Changes not saved" + message: 'Offline Saving Issue', + desc: 'Changes not saved', }); break; } @@ -272,15 +282,18 @@ class EditorViewCtrl extends PureViewCtrl { async handleEditorNoteChange() { this.cancelPendingSetStatus(); + const note = this.editor.note; + const showProtectedWarning = + note.protected && !this.application.hasProtectionSources(); await this.setState({ showActionsMenu: false, showOptionsMenu: false, showEditorMenu: false, showHistoryMenu: false, altKeyDown: false, - noteStatus: undefined + noteStatus: undefined, + showProtectedWarning, }); - const note = this.editor.note; this.editorValues.title = note.title; this.editorValues.text = note.text; this.reloadEditor(); @@ -288,11 +301,18 @@ class EditorViewCtrl extends PureViewCtrl { this.reloadPreferences(); this.reloadStackComponents(); this.reloadNoteTagsComponent(); - if (note.safeText().length === 0) { + if (note.safeText().length === 0 && !showProtectedWarning) { this.focusTitle(); } } + async dismissProtectedWarning() { + await this.setState({ + showProtectedWarning: false, + }); + this.focusTitle(); + } + /** * Because note.locked accesses note.content.appData, * we do not want to expose the template to direct access to note.locked, @@ -331,7 +351,9 @@ class EditorViewCtrl extends PureViewCtrl { } private async reloadEditor() { - const newEditor = this.application.componentManager!.editorForNote(this.note); + const newEditor = this.application.componentManager!.editorForNote( + this.note + ); /** Editors cannot interact with template notes so the note must be inserted */ if (newEditor && this.editor.isTemplateNote) { await this.editor.insertTemplatedNote(); @@ -340,7 +362,7 @@ class EditorViewCtrl extends PureViewCtrl { if (currentEditor?.uuid !== newEditor?.uuid) { await this.setState({ /** Unload current component view so that we create a new one */ - editorUnloading: true + editorUnloading: true, }); await this.setState({ /** Reload component view */ @@ -349,12 +371,14 @@ class EditorViewCtrl extends PureViewCtrl { }); this.reloadFont(); } - this.application.componentManager!.contextItemDidChangeInArea(ComponentArea.Editor); + this.application.componentManager!.contextItemDidChangeInArea( + ComponentArea.Editor + ); } setMenuState(menu: string, state: boolean) { this.setState({ - [menu]: state + [menu]: state, }); this.closeAllMenus(menu); } @@ -392,12 +416,15 @@ class EditorViewCtrl extends PureViewCtrl { }); this.reloadEditor(); } - if (this.state.editorComponent?.isExplicitlyEnabledForItem(this.note.uuid)) { - await this.disassociateComponentWithCurrentNote(this.state.editorComponent); + if ( + this.state.editorComponent?.isExplicitlyEnabledForItem(this.note.uuid) + ) { + await this.disassociateComponentWithCurrentNote( + this.state.editorComponent + ); } this.reloadFont(); - } - else if (component.area === ComponentArea.Editor) { + } else if (component.area === ComponentArea.Editor) { const currentEditor = this.state.editorComponent; if (currentEditor && component !== currentEditor) { await this.disassociateComponentWithCurrentNote(currentEditor); @@ -410,8 +437,7 @@ class EditorViewCtrl extends PureViewCtrl { }); } await this.associateComponentWithCurrentNote(component); - } - else if (component.area === ComponentArea.EditorStack) { + } else if (component.area === ComponentArea.EditorStack) { await this.toggleStackComponentForCurrentItem(component); } /** Dirtying can happen above */ @@ -419,8 +445,10 @@ class EditorViewCtrl extends PureViewCtrl { } hasAvailableExtensions() { - return this.application.actionsManager!. - extensionsInContextOfItem(this.note).length > 0; + return ( + this.application.actionsManager!.extensionsInContextOfItem(this.note) + .length > 0 + ); } /** @@ -444,52 +472,50 @@ class EditorViewCtrl extends PureViewCtrl { closeAfterSync = false ) { if (document.hidden) { - this.application.alertService!.alert( - STRING_SAVING_WHILE_DOCUMENT_HIDDEN - ); + this.application.alertService!.alert(STRING_SAVING_WHILE_DOCUMENT_HIDDEN); return; } const note = this.note; if (note.deleted) { - this.application.alertService!.alert( - STRING_DELETED_NOTE - ); + this.application.alertService!.alert(STRING_DELETED_NOTE); return; } if (this.editor.isTemplateNote) { await this.editor.insertTemplatedNote(); } const selectedTag = this.appState.selectedTag; - if (!selectedTag?.isSmartTag && !selectedTag?.hasRelationshipWithItem(note)) { - await this.application.changeItem( - selectedTag!.uuid, - (mutator) => { - mutator.addItemAsRelationship(note); - } - ); + if ( + !selectedTag?.isSmartTag && + !selectedTag?.hasRelationshipWithItem(note) + ) { + await this.application.changeItem(selectedTag!.uuid, (mutator) => { + mutator.addItemAsRelationship(note); + }); } if (!this.application.findItem(note.uuid)) { - this.application.alertService!.alert( - STRING_INVALID_NOTE - ); + this.application.alertService!.alert(STRING_INVALID_NOTE); return; } - await this.application.changeItem(note.uuid, (mutator) => { - const noteMutator = mutator as NoteMutator; - if (customMutate) { - customMutate(noteMutator); - } - noteMutator.title = this.editorValues.title!; - noteMutator.text = this.editorValues.text!; - if (!dontUpdatePreviews) { - const text = this.editorValues.text || ''; - const truncate = text.length > NOTE_PREVIEW_CHAR_LIMIT; - const substring = text.substring(0, NOTE_PREVIEW_CHAR_LIMIT); - const previewPlain = substring + (truncate ? STRING_ELLIPSES : ''); - noteMutator.preview_plain = previewPlain; - noteMutator.preview_html = undefined; - } - }, isUserModified); + await this.application.changeItem( + note.uuid, + (mutator) => { + const noteMutator = mutator as NoteMutator; + if (customMutate) { + customMutate(noteMutator); + } + noteMutator.title = this.editorValues.title!; + noteMutator.text = this.editorValues.text!; + if (!dontUpdatePreviews) { + const text = this.editorValues.text || ''; + const truncate = text.length > NOTE_PREVIEW_CHAR_LIMIT; + const substring = text.substring(0, NOTE_PREVIEW_CHAR_LIMIT); + const previewPlain = substring + (truncate ? STRING_ELLIPSES : ''); + noteMutator.preview_plain = previewPlain; + noteMutator.preview_html = undefined; + } + }, + isUserModified + ); if (this.saveTimeout) { this.$timeout.cancel(this.saveTimeout); } @@ -506,16 +532,13 @@ class EditorViewCtrl extends PureViewCtrl { } showSavingStatus() { - this.setStatus( - { message: "Saving…" }, - false - ); + this.setStatus({ message: 'Saving…' }, false); } showAllChangesSavedStatus() { this.setState({ saveError: false, - syncTakingTooLong: false + syncTakingTooLong: false, }); this.setStatus({ message: 'All changes saved', @@ -525,13 +548,13 @@ class EditorViewCtrl extends PureViewCtrl { showErrorStatus(error?: any) { if (!error) { error = { - message: "Sync Unreachable", - desc: "Changes saved offline" + message: 'Sync Unreachable', + desc: 'Changes saved offline', }; } this.setState({ saveError: true, - syncTakingTooLong: false + syncTakingTooLong: false, }); this.setStatus(error); } @@ -543,12 +566,12 @@ class EditorViewCtrl extends PureViewCtrl { if (wait) { this.statusTimeout = this.$timeout(() => { this.setState({ - noteStatus: status + noteStatus: status, }); }, MINIMUM_STATUS_DURATION); } else { this.setState({ - noteStatus: status + noteStatus: status, }); } } @@ -560,10 +583,7 @@ class EditorViewCtrl extends PureViewCtrl { } contentChanged() { - this.saveNote( - false, - true - ); + this.saveNote(false, true); } onTitleEnter($event: Event) { @@ -573,11 +593,7 @@ class EditorViewCtrl extends PureViewCtrl { } onTitleChange() { - this.saveNote( - false, - true, - true, - ); + this.saveNote(false, true, true); } focusEditor() { @@ -597,17 +613,15 @@ class EditorViewCtrl extends PureViewCtrl { } // eslint-disable-next-line @typescript-eslint/no-empty-function - onTitleFocus() { - - } + onTitleFocus() {} // eslint-disable-next-line @typescript-eslint/no-empty-function - onTitleBlur() { - - } + onTitleBlur() {} onContentFocus() { - this.application.getAppState().editorDidFocus(this.lastEditorFocusEventSource!); + this.application + .getAppState() + .editorDidFocus(this.lastEditorFocusEventSource!); this.lastEditorFocusEventSource = undefined; } @@ -619,39 +633,29 @@ class EditorViewCtrl extends PureViewCtrl { async deleteNote(permanently: boolean) { if (this.editor.isTemplateNote) { - this.application.alertService!.alert( - STRING_DELETE_PLACEHOLDER_ATTEMPT - ); + this.application.alertService!.alert(STRING_DELETE_PLACEHOLDER_ATTEMPT); return; } if (this.note.locked) { - this.application.alertService!.alert( - STRING_DELETE_LOCKED_ATTEMPT - ); + this.application.alertService!.alert(STRING_DELETE_LOCKED_ATTEMPT); return; } const title = this.note.safeTitle().length ? `'${this.note.title}'` - : "this note"; - const text = StringDeleteNote( - title, - permanently - ); - if (await confirmDialog({ - text, - confirmButtonStyle: 'danger' - })) { + : 'this note'; + const text = StringDeleteNote(title, permanently); + if ( + await confirmDialog({ + text, + confirmButtonStyle: 'danger', + }) + ) { if (permanently) { this.performNoteDeletion(this.note); } else { - this.saveNote( - true, - false, - true, - (mutator) => { - mutator.trashed = true; - } - ); + this.saveNote(true, false, true, (mutator) => { + mutator.trashed = true; + }); } } } @@ -682,35 +686,27 @@ class EditorViewCtrl extends PureViewCtrl { async emptyTrash() { const count = this.getTrashCount(); - if (await confirmDialog({ - text: StringEmptyTrash(count), - confirmButtonStyle: 'danger' - })) { + if ( + await confirmDialog({ + text: StringEmptyTrash(count), + confirmButtonStyle: 'danger', + }) + ) { this.application.emptyTrash(); this.application.sync(); } } togglePin() { - this.saveNote( - true, - false, - true, - (mutator) => { - mutator.pinned = !this.note.pinned; - } - ); + this.saveNote(true, false, true, (mutator) => { + mutator.pinned = !this.note.pinned; + }); } toggleLockNote() { - this.saveNote( - true, - false, - true, - (mutator) => { - mutator.locked = !this.note.locked; - } - ); + this.saveNote(true, false, true, (mutator) => { + mutator.locked = !this.note.locked; + }); } async toggleProtectNote() { @@ -719,34 +715,25 @@ class EditorViewCtrl extends PureViewCtrl { } else { const note = await this.application.protectNote(this.note); if (note?.protected && !this.application.hasProtectionSources()) { - if (await confirmDialog({ - text: Strings.protectingNoteWithoutProtectionSources, - confirmButtonText: Strings.openAccountMenu, - confirmButtonStyle: 'info', - })) { - this.appState.accountMenu.setShow(true); - } + this.setState({ + showProtectedWarning: true, + }); } } } toggleNotePreview() { - this.saveNote( - true, - false, - true, - (mutator) => { - mutator.hidePreview = !this.note.hidePreview; - } - ); + this.saveNote(true, false, true, (mutator) => { + mutator.hidePreview = !this.note.hidePreview; + }); } toggleArchiveNote() { if (this.note.locked) { alertDialog({ - text: this.note.archived ? - STRING_UNARCHIVE_LOCKED_ATTEMPT : - STRING_ARCHIVE_LOCKED_ATTEMPT, + text: this.note.archived + ? STRING_UNARCHIVE_LOCKED_ATTEMPT + : STRING_ARCHIVE_LOCKED_ATTEMPT, }); return; } @@ -774,10 +761,7 @@ class EditorViewCtrl extends PureViewCtrl { for (let i = 0; i < tags.length; i++) { const localTag = this.tags[i]; const tag = tags[i]; - if ( - tag.title !== localTag.title || - tag.uuid !== localTag.uuid - ) { + if (tag.title !== localTag.title || tag.uuid !== localTag.uuid) { this.reloadTagsString(tags); break; } @@ -803,11 +787,13 @@ class EditorViewCtrl extends PureViewCtrl { removeTag(tag: SNTag) { const tags = this.appState.getNoteTags(this.note); - const strings = tags.map((currentTag) => { - return currentTag.title; - }).filter((title) => { - return title !== tag.title; - }); + const strings = tags + .map((currentTag) => { + return currentTag.title; + }) + .filter((title) => { + return title !== tag.title; + }); this.saveTagsFromStrings(strings); } @@ -818,14 +804,14 @@ class EditorViewCtrl extends PureViewCtrl { public async saveTagsFromStrings(strings?: string[]) { if ( - !strings - && this.editorValues.tagsInputValue === this.state.tagsAsStrings + !strings && + this.editorValues.tagsInputValue === this.state.tagsAsStrings ) { return; } if (!strings) { - strings = this.editorValues.tagsInputValue! - .split('#') + strings = this.editorValues + .tagsInputValue!.split('#') .filter((string) => { return string.length > 0; }) @@ -848,23 +834,15 @@ class EditorViewCtrl extends PureViewCtrl { } const newRelationships: SNTag[] = []; for (const title of strings) { - const existingRelationship = find( - currentTags, - { title: title } - ); + const existingRelationship = find(currentTags, { title: title }); if (!existingRelationship) { - newRelationships.push( - await this.application.findOrCreateTag(title) - ); + newRelationships.push(await this.application.findOrCreateTag(title)); } } if (newRelationships.length > 0) { - await this.application.changeItems( - Uuids(newRelationships), - (mutator) => { - mutator.addItemAsRelationship(note); - } - ); + await this.application.changeItems(Uuids(newRelationships), (mutator) => { + mutator.addItemAsRelationship(note); + }); } this.application.sync(); this.reloadTags(); @@ -872,24 +850,15 @@ class EditorViewCtrl extends PureViewCtrl { async onPanelResizeFinish(width: number, left: number, isMaxWidth: boolean) { if (isMaxWidth) { - await this.application.setPreference( - PrefKey.EditorWidth, - null - ); + await this.application.setPreference(PrefKey.EditorWidth, null); } else { if (width !== undefined && width !== null) { - await this.application.setPreference( - PrefKey.EditorWidth, - width - ); + await this.application.setPreference(PrefKey.EditorWidth, width); this.leftPanelPuppet!.setWidth!(width); } } if (left !== undefined && left !== null) { - await this.application.setPreference( - PrefKey.EditorLeft, - left - ); + await this.application.setPreference(PrefKey.EditorLeft, left); this.rightPanelPuppet!.setLeft!(left); } this.application.sync(); @@ -911,7 +880,7 @@ class EditorViewCtrl extends PureViewCtrl { await this.setState({ monospaceFont, spellcheck, - marginResizersEnabled + marginResizersEnabled, }); if (!document.getElementById(ElementIds.EditorContent)) { @@ -926,18 +895,12 @@ class EditorViewCtrl extends PureViewCtrl { this.leftPanelPuppet?.ready && this.rightPanelPuppet?.ready ) { - const width = this.application.getPreference( - PrefKey.EditorWidth, - null - ); + const width = this.application.getPreference(PrefKey.EditorWidth, null); if (width != null) { this.leftPanelPuppet!.setWidth!(width); this.rightPanelPuppet!.setWidth!(width); } - const left = this.application.getPreference( - PrefKey.EditorLeft, - null - ); + const left = this.application.getPreference(PrefKey.EditorLeft, null); if (left != null) { this.leftPanelPuppet!.setLeft!(left); this.rightPanelPuppet!.setLeft!(left); @@ -949,10 +912,7 @@ class EditorViewCtrl extends PureViewCtrl { const root = document.querySelector(':root') as HTMLElement; const propertyName = '--sn-stylekit-editor-font-family'; if (this.state.monospaceFont) { - root.style.setProperty( - propertyName, - 'var(--sn-stylekit-monospace-font)' - ); + root.style.setProperty(propertyName, 'var(--sn-stylekit-monospace-font)'); } else { root.style.setProperty( propertyName, @@ -963,12 +923,9 @@ class EditorViewCtrl extends PureViewCtrl { async toggleWebPrefKey(key: PrefKey) { const currentValue = (this.state as any)[key]; - await this.application.setPreference( - key, - !currentValue, - ); + await this.application.setPreference(key, !currentValue); await this.setState({ - [key]: !currentValue + [key]: !currentValue, }); this.reloadFont(); @@ -977,7 +934,10 @@ class EditorViewCtrl extends PureViewCtrl { await this.setState({ textareaUnloading: true }); await this.setState({ textareaUnloading: false }); this.reloadFont(); - } else if (key === PrefKey.EditorResizersEnabled && this.state[key] === true) { + } else if ( + key === PrefKey.EditorResizersEnabled && + this.state[key] === true + ) { this.$timeout(() => { this.leftPanelPuppet!.flash!(); this.rightPanelPuppet!.flash!(); @@ -988,99 +948,106 @@ class EditorViewCtrl extends PureViewCtrl { /** @components */ registerComponentHandler() { - this.unregisterComponent = this.application.componentManager!.registerHandler({ - identifier: 'editor', - areas: [ - ComponentArea.NoteTags, - ComponentArea.EditorStack, - ComponentArea.Editor - ], - contextRequestHandler: (componentUuid) => { - const currentEditor = this.state.editorComponent; - if ( - componentUuid === currentEditor?.uuid || - componentUuid === this.state.tagsComponent?.uuid || - Uuids(this.state.stackComponents).includes(componentUuid) - ) { - return this.note; - } - }, - focusHandler: (component, focused) => { - if (component.isEditor() && focused) { - this.closeAllMenus(); - } - }, - actionHandler: (component, action, data) => { - if (action === ComponentAction.SetSize) { - const setSize = ( - element: HTMLElement, - size: { width: string | number, height: string | number } - ) => { - const widthString = typeof size.width === 'string' - ? size.width - : `${data.width}px`; - const heightString = typeof size.height === 'string' - ? size.height - : `${data.height}px`; - element.setAttribute( - 'style', - `width: ${widthString}; height: ${heightString};` - ); - }; - if (data.type === 'container') { - if (component.area === ComponentArea.NoteTags) { - const container = document.getElementById( - ElementIds.NoteTagsComponentContainer - ); - setSize(container!, { width: data.width!, height: data.height! }); - } + this.unregisterComponent = this.application.componentManager!.registerHandler( + { + identifier: 'editor', + areas: [ + ComponentArea.NoteTags, + ComponentArea.EditorStack, + ComponentArea.Editor, + ], + contextRequestHandler: (componentUuid) => { + const currentEditor = this.state.editorComponent; + if ( + componentUuid === currentEditor?.uuid || + componentUuid === this.state.tagsComponent?.uuid || + Uuids(this.state.stackComponents).includes(componentUuid) + ) { + return this.note; } - } - else if (action === ComponentAction.AssociateItem) { - if (data.item!.content_type === ContentType.Tag) { + }, + focusHandler: (component, focused) => { + if (component.isEditor() && focused) { + this.closeAllMenus(); + } + }, + actionHandler: (component, action, data) => { + if (action === ComponentAction.SetSize) { + const setSize = ( + element: HTMLElement, + size: { width: string | number; height: string | number } + ) => { + const widthString = + typeof size.width === 'string' ? size.width : `${data.width}px`; + const heightString = + typeof size.height === 'string' + ? size.height + : `${data.height}px`; + element.setAttribute( + 'style', + `width: ${widthString}; height: ${heightString};` + ); + }; + if (data.type === 'container') { + if (component.area === ComponentArea.NoteTags) { + const container = document.getElementById( + ElementIds.NoteTagsComponentContainer + ); + setSize(container!, { + width: data.width!, + height: data.height!, + }); + } + } + } else if (action === ComponentAction.AssociateItem) { + if (data.item!.content_type === ContentType.Tag) { + const tag = this.application.findItem(data.item!.uuid) as SNTag; + this.addTag(tag); + } + } else if (action === ComponentAction.DeassociateItem) { const tag = this.application.findItem(data.item!.uuid) as SNTag; - this.addTag(tag); - } - } - else if (action === ComponentAction.DeassociateItem) { - const tag = this.application.findItem(data.item!.uuid) as SNTag; - this.removeTag(tag); - } else if ( - action === ComponentAction.SaveSuccess - ) { - const savedUuid = data.item ? data.item.uuid : data.items![0].uuid; - if (savedUuid === this.note.uuid) { - const selectedTag = this.appState.selectedTag; - if ( - !selectedTag?.isSmartTag && - !selectedTag?.hasRelationshipWithItem(this.note) - ) { - this.application.changeAndSaveItem( - selectedTag!.uuid, - (mutator) => { - mutator.addItemAsRelationship(this.note); - } - ); + this.removeTag(tag); + } else if (action === ComponentAction.SaveSuccess) { + const savedUuid = data.item ? data.item.uuid : data.items![0].uuid; + if (savedUuid === this.note.uuid) { + const selectedTag = this.appState.selectedTag; + if ( + !selectedTag?.isSmartTag && + !selectedTag?.hasRelationshipWithItem(this.note) + ) { + this.application.changeAndSaveItem( + selectedTag!.uuid, + (mutator) => { + mutator.addItemAsRelationship(this.note); + } + ); + } } } - } + }, } - }); + ); } async reloadNoteTagsComponent() { - const [tagsComponent] = - this.application.componentManager!.componentsForArea(ComponentArea.NoteTags); + const [ + tagsComponent, + ] = this.application.componentManager!.componentsForArea( + ComponentArea.NoteTags + ); await this.setState({ - tagsComponent: tagsComponent?.active ? tagsComponent : undefined + tagsComponent: tagsComponent?.active ? tagsComponent : undefined, }); - this.application.componentManager!.contextItemDidChangeInArea(ComponentArea.NoteTags); + this.application.componentManager!.contextItemDidChangeInArea( + ComponentArea.NoteTags + ); } async reloadStackComponents() { const stackComponents = sortAlphabetically( - this.application.componentManager!.componentsForArea(ComponentArea.EditorStack) - .filter(component => component.active) + this.application + .componentManager!.componentsForArea(ComponentArea.EditorStack) + .filter((component) => component.active) ); if (this.note) { for (const component of stackComponents) { @@ -1093,7 +1060,9 @@ class EditorViewCtrl extends PureViewCtrl { } } await this.setState({ stackComponents }); - this.application.componentManager!.contextItemDidChangeInArea(ComponentArea.EditorStack); + this.application.componentManager!.contextItemDidChangeInArea( + ComponentArea.EditorStack + ); } stackComponentHidden(component: SNComponent) { @@ -1101,11 +1070,15 @@ class EditorViewCtrl extends PureViewCtrl { } async toggleStackComponentForCurrentItem(component: SNComponent) { - const hidden = this.application.componentManager!.isComponentHidden(component); + const hidden = this.application.componentManager!.isComponentHidden( + component + ); if (hidden || !component.active) { this.application.componentManager!.setComponentHidden(component, false); await this.associateComponentWithCurrentNote(component); - this.application.componentManager!.contextItemDidChangeInArea(ComponentArea.EditorStack); + this.application.componentManager!.contextItemDidChangeInArea( + ComponentArea.EditorStack + ); } else { this.application.componentManager!.setComponentHidden(component, true); await this.disassociateComponentWithCurrentNote(component); @@ -1132,33 +1105,32 @@ class EditorViewCtrl extends PureViewCtrl { } registerKeyboardShortcuts() { - this.removeAltKeyObserver = this.application.getKeyboardService().addKeyObserver({ - modifiers: [ - KeyboardModifier.Alt - ], - onKeyDown: () => { - this.setState({ - altKeyDown: true - }); - }, - onKeyUp: () => { - this.setState({ - altKeyDown: false - }); - } - }); + this.removeAltKeyObserver = this.application + .getKeyboardService() + .addKeyObserver({ + modifiers: [KeyboardModifier.Alt], + onKeyDown: () => { + this.setState({ + altKeyDown: true, + }); + }, + onKeyUp: () => { + this.setState({ + altKeyDown: false, + }); + }, + }); - this.removeTrashKeyObserver = this.application.getKeyboardService().addKeyObserver({ - key: KeyboardKey.Backspace, - notElementIds: [ - ElementIds.NoteTextEditor, - ElementIds.NoteTitleEditor - ], - modifiers: [KeyboardModifier.Meta], - onKeyDown: () => { - this.deleteNote(false); - }, - }); + this.removeTrashKeyObserver = this.application + .getKeyboardService() + .addKeyObserver({ + key: KeyboardKey.Backspace, + notElementIds: [ElementIds.NoteTextEditor, ElementIds.NoteTitleEditor], + modifiers: [KeyboardModifier.Meta], + onKeyDown: () => { + this.deleteNote(false); + }, + }); } onSystemEditorLoad() { @@ -1170,37 +1142,43 @@ class EditorViewCtrl extends PureViewCtrl { * only used when inside of the text editor. * If the shift key is pressed first, this event is * not fired. - */ - const editor = document.getElementById(ElementIds.NoteTextEditor)! as HTMLInputElement; - this.removeTabObserver = this.application.getKeyboardService().addKeyObserver({ - element: editor, - key: KeyboardKey.Tab, - onKeyDown: (event) => { - if (document.hidden || this.note.locked || event.shiftKey) { - return; - } - event.preventDefault(); - /** Using document.execCommand gives us undo support */ - const insertSuccessful = document.execCommand( - 'insertText', - false, - '\t' - ); - if (!insertSuccessful) { - /** document.execCommand works great on Chrome/Safari but not Firefox */ - const start = editor.selectionStart!; - const end = editor.selectionEnd!; - const spaces = ' '; - /** Insert 4 spaces */ - editor.value = editor.value.substring(0, start) - + spaces + editor.value.substring(end); - /** Place cursor 4 spaces away from where the tab key was pressed */ - editor.selectionStart = editor.selectionEnd = start + 4; - } - this.editorValues.text = editor.value; - this.saveNote(true); - }, - }); + */ + const editor = document.getElementById( + ElementIds.NoteTextEditor + )! as HTMLInputElement; + this.removeTabObserver = this.application + .getKeyboardService() + .addKeyObserver({ + element: editor, + key: KeyboardKey.Tab, + onKeyDown: (event) => { + if (document.hidden || this.note.locked || event.shiftKey) { + return; + } + event.preventDefault(); + /** Using document.execCommand gives us undo support */ + const insertSuccessful = document.execCommand( + 'insertText', + false, + '\t' + ); + if (!insertSuccessful) { + /** document.execCommand works great on Chrome/Safari but not Firefox */ + const start = editor.selectionStart!; + const end = editor.selectionEnd!; + const spaces = ' '; + /** Insert 4 spaces */ + editor.value = + editor.value.substring(0, start) + + spaces + + editor.value.substring(end); + /** Place cursor 4 spaces away from where the tab key was pressed */ + editor.selectionStart = editor.selectionEnd = start + 4; + } + this.editorValues.text = editor.value; + this.saveNote(true); + }, + }); /** * Handles when the editor is destroyed, @@ -1219,7 +1197,7 @@ export class EditorView extends WebDirective { this.restrict = 'E'; this.scope = { editor: '=', - application: '=' + application: '=', }; this.template = template; this.replace = true; diff --git a/app/assets/javascripts/views/editor_group/editor-group-view.pug b/app/assets/javascripts/views/editor_group/editor-group-view.pug index 22f7e72ec..96a0517e0 100644 --- a/app/assets/javascripts/views/editor_group/editor-group-view.pug +++ b/app/assets/javascripts/views/editor_group/editor-group-view.pug @@ -1,5 +1,7 @@ -editor-view( +.flex-grow( ng-repeat='editor in self.editors' - application='self.application' - editor='editor' +) + editor-view( + application='self.application' + editor='editor' ) diff --git a/app/assets/javascripts/views/notes/notes_view.ts b/app/assets/javascripts/views/notes/notes_view.ts index 9d71f391b..5eee017f7 100644 --- a/app/assets/javascripts/views/notes/notes_view.ts +++ b/app/assets/javascripts/views/notes/notes_view.ts @@ -706,7 +706,12 @@ class NotesViewCtrl extends PureViewCtrl { async onIncludeProtectedNoteTextChange(event: Event) { this.searchBarInput?.[0].focus(); if (this.state.noteFilter.includeProtectedNoteText) { - this.state.noteFilter.includeProtectedNoteText = false; + await this.setState({ + noteFilter: { + ...this.state.noteFilter, + includeProtectedNoteText: false, + }, + }); this.reloadNotesDisplayOptions(); await this.reloadNotes(); } else { @@ -715,7 +720,12 @@ class NotesViewCtrl extends PureViewCtrl { }); event.preventDefault(); if (await this.application.authorizeSearchingProtectedNotesText()) { - this.state.noteFilter.includeProtectedNoteText = true; + await this.setState({ + noteFilter: { + ...this.state.noteFilter, + includeProtectedNoteText: true, + }, + }); this.reloadNotesDisplayOptions(); await this.reloadNotes(); } diff --git a/app/assets/stylesheets/_main.scss b/app/assets/stylesheets/_main.scss index 7503712c6..06c659c4e 100644 --- a/app/assets/stylesheets/_main.scss +++ b/app/assets/stylesheets/_main.scss @@ -63,7 +63,7 @@ h3 { input, button, select, textarea { font-family: inherit; font-size: inherit; - line-height: inherit; + line-height: normal; } a { @@ -93,7 +93,6 @@ a { } p { - overflow: auto; color: var(--sn-stylekit-paragraph-text-color); margin: 0; } diff --git a/app/assets/stylesheets/_sn.scss b/app/assets/stylesheets/_sn.scss deleted file mode 100644 index 420dd8b24..000000000 --- a/app/assets/stylesheets/_sn.scss +++ /dev/null @@ -1,16 +0,0 @@ -/* Generic UI controls that have yet to be extracted into Stylekit */ - -.sn-btn { - @extend .border-0; - @extend .bg-main; - @extend .cursor-pointer; - @extend .capitalize; - @extend .font-bold; - @extend .py-2; - @extend .px-3; - @extend .rounded; - @extend .text-info-contrast; - @extend .text-sm; - - @extend .hover\:brightness-130; -} diff --git a/app/assets/stylesheets/_ui.scss b/app/assets/stylesheets/_ui.scss index 2b44ab40f..e4785998d 100644 --- a/app/assets/stylesheets/_ui.scss +++ b/app/assets/stylesheets/_ui.scss @@ -81,14 +81,6 @@ $screen-md-max: ($screen-lg-min - 1) !default; opacity: 0.5; } -.flex { - display: flex; -} - -.flex-column { - flex-direction: column; -} - .self-start { align-self: flex-start; } @@ -226,6 +218,12 @@ $screen-md-max: ($screen-lg-min - 1) !default; .grid-template-cols-1fr { grid-template-columns: 1fr; } +.col-span-all { + grid-column: 1 / -1; +} +.grid-col-2 { + grid-column: 2; +} .relative { position: relative; diff --git a/app/assets/stylesheets/index.css.scss b/app/assets/stylesheets/index.css.scss index 2dc340e5e..52ca8b997 100644 --- a/app/assets/stylesheets/index.css.scss +++ b/app/assets/stylesheets/index.css.scss @@ -11,4 +11,3 @@ @import "ionicons"; @import "reach-sub"; @import "sessions-modal"; -@import "sn"; diff --git a/app/assets/templates/directives/account-menu.pug b/app/assets/templates/directives/account-menu.pug index bde3c0f21..65433ea0e 100644 --- a/app/assets/templates/directives/account-menu.pug +++ b/app/assets/templates/directives/account-menu.pug @@ -12,12 +12,15 @@ ) .sk-panel-row .sk-h1 Sign in or register to enable sync and end-to-end encryption. - .sk-panel-row - .sk-button-group.stretch - .sk-button.info.featured(ng-click='self.state.formData.showLogin = true') - .sk-label Sign In - .sk-button.info.featured(ng-click='self.showRegister()') - .sk-label Register + .flex.my-1 + button( + class="sn-button info flex-grow text-base py-3 mr-1.5" + ng-click='self.state.formData.showLogin = true' + ) Sign In + button( + class="sn-button info flex-grow text-base py-3 ml-1.5" + ng-click='self.showRegister()' + ) Register .sk-panel-row.sk-p | Standard Notes is free on every platform, and comes | standard with sync and encryption. @@ -95,12 +98,10 @@ target='_blank' ) (Learn more) .sk-panel-section.form-submit(ng-if='!self.state.formData.authenticating') - .sk-button-group.stretch - .sk-button.info.featured( - ng-click='self.submitAuthForm()', - ng-disabled='self.state.formData.authenticating' - ) - .sk-label {{self.state.formData.showLogin ? "Sign In" : "Register"}} + button.sn-button.info.text-base.py-3.text-center( + ng-click='self.submitAuthForm()', + ng-disabled='self.state.formData.authenticating' + ) {{self.state.formData.showLogin ? "Sign In" : "Register"}} .sk-notification.neutral(ng-if='self.state.formData.showRegister') .sk-notification-title No Password Reset. .sk-notification-text @@ -174,17 +175,16 @@ | or revoking an active session, require additional authentication | like entering your account password or application passcode. .sk-panel-row(ng-if="self.state.protectionsDisabledUntil") - button.sk-button.info(ng-click="self.enableProtections()") - span.sk-label.capitalize Enable protections + button.sn-button.info(ng-click="self.enableProtections()") + | Enable protections .sk-panel-section .sk-panel-section-title Passcode Lock div(ng-if='!self.state.hasPasscode') div(ng-if='self.state.canAddPasscode') .sk-panel-row(ng-if='!self.state.formData.showPasscodeForm') - .sk-button.info( + button.sn-button.info( ng-click='self.addPasscodeClicked(); $event.stopPropagation();' - ) - .sk-label Add Passcode + ) Add Passcode p.sk-p | Add a passcode to lock the application and | encrypt on-device key storage. @@ -211,12 +211,10 @@ placeholder='Confirm Passcode', type='password' ) - .sk-button-group.stretch.sk-panel-row.form-submit - button.sk-button.info(type='submit') - .sk-label Set Passcode - a.neutral.sk-a.sk-panel-row( + button.sn-button.info.mt-2(type='submit') Set Passcode + button.sn-button.outlined.ml-2( ng-click='self.state.formData.showPasscodeForm = false' - ) Cancel + ) Cancel div(ng-if='self.state.hasPasscode && !self.state.formData.showPasscodeForm') .sk-panel-section-subtitle.info Passcode lock is enabled .sk-notification.contrast @@ -245,7 +243,6 @@ .sk-panel-section-title Data Backups .sk-p | Download a backup of all your data. - .sk-panel-row form.sk-panel-form.sk-panel-row(ng-if='self.state.encryptionEnabled') .sk-input-group label.sk-horizontal-group.tight @@ -265,18 +262,18 @@ ) p.sk-p Decrypted .sk-panel-row - .sk-button-group.sk-panel-row.justify-left - .sk-button.info(ng-click='self.downloadDataArchive()') - .sk-label Download Backup - label.sk-button.info + .flex + button.sn-button.info(ng-click='self.downloadDataArchive()') + | Download Backup + label.sn-button.info.ml-2 input( file-change='->', handler='self.importFileSelected(files)', style='display: none;', type='file' - ) - .sk-label Import Backup - span(ng-if='self.isDesktopApplication()') + ) + | Import Backup + p.mt-5(ng-if='self.isDesktopApplication()') | Backups are automatically created on desktop and can be managed | via the "Backups" top-level menu. .sk-panel-row @@ -296,8 +293,8 @@ | local storage, and a new identifier will be created should you | decide to enable error reporting again in the future. .sk-panel-row - button(ng-click="self.toggleErrorReportingEnabled()").sk-button.info - span.sk-label {{ self.state.errorReportingEnabled ? 'Disable' : 'Enable'}} Error Reporting + button(ng-click="self.toggleErrorReportingEnabled()").sn-button.info + | {{ self.state.errorReportingEnabled ? 'Disable' : 'Enable'}} Error Reporting .sk-panel-row a(ng-click="self.openErrorReportingDialog()").sk-a What data is being sent? .sk-panel-footer @@ -313,7 +310,7 @@ ng-if='self.state.formData.showLogin || self.state.formData.showRegister' ) | Cancel - a.sk-a.right.danger( + a.sk-a.right.danger.capitalize( ng-click='self.destroyLocalData()', ng-if=` !self.state.formData.showLogin && diff --git a/app/assets/templates/directives/component-view.pug b/app/assets/templates/directives/component-view.pug index f31383027..4ae471f85 100644 --- a/app/assets/templates/directives/component-view.pug +++ b/app/assets/templates/directives/component-view.pug @@ -5,8 +5,7 @@ .sk-label.warning There was an issue loading {{ctrl.component.name}}. .right .sk-app-bar-item(ng-click='ctrl.reloadIframe()') - .sk-button.info - .sk-label Reload + button.sn-button.info Reload .sn-component(ng-if='ctrl.expired') .sk-app-bar.no-edges.no-top-edge.dynamic-height .left @@ -26,16 +25,14 @@ | Extensions are in a read-only state. .right .sk-app-bar-item(ng-click='ctrl.reloadStatus(true)') - .sk-button.info - .sk-label Reload + button.sn-button.info Reload .sk-app-bar-item .sk-app-bar-item-column - .sk-button.warning - a.sk-label( - href='https://standardnotes.org/help/41/expired', - rel='noopener', - target='_blank' - ) Help + a.sn-button.warning( + href='https://standardnotes.org/help/41/expired', + rel='noopener', + target='_blank' + ) Help .sn-component(ng-if="ctrl.error == 'offline-restricted'") .sk-panel.static @@ -57,11 +54,10 @@ li.sk-p strong Use the Desktop application. .sk-panel-row - .sk-button.info( + button.sn-button.info( ng-click='ctrl.reloadStatus()', ng-if='!ctrl.reloading' - ) - .sk-label Reload + ) Reload .sk-spinner.info.small(ng-if='ctrl.reloading') .sn-component(ng-if="ctrl.error == 'url-missing'") .sk-panel.static diff --git a/app/assets/templates/directives/menu-row.pug b/app/assets/templates/directives/menu-row.pug index 25b7a18ea..6714228cd 100644 --- a/app/assets/templates/directives/menu-row.pug +++ b/app/assets/templates/directives/menu-row.pug @@ -30,10 +30,9 @@ .sk-menu-panel-column(ng-if="ctrl.circle && ctrl.circleAlign == 'right'") .sk-circle.small(ng-class='ctrl.circle') .sk-menu-panel-column(ng-if='ctrl.hasButton') - .sk-button( + button.sn-button.small( ng-class='ctrl.buttonClass', ng-click='ctrl.clickAccessoryButton($event)' - ) - .sk-label {{ctrl.buttonText}} + ) {{ctrl.buttonText}} .sk-menu-panel-column(ng-if='ctrl.spinnerClass') .sk-spinner.small(ng-class='ctrl.spinnerClass') diff --git a/app/assets/templates/directives/password-wizard.pug b/app/assets/templates/directives/password-wizard.pug index 2fde1edc6..52a58940f 100644 --- a/app/assets/templates/directives/password-wizard.pug +++ b/app/assets/templates/directives/password-wizard.pug @@ -41,8 +41,7 @@ | Please ensure you are running the latest version of Standard Notes | on all platforms to ensure maximum compatibility. .sk-panel-footer - .sk-button.info( + button.sn-button.info( ng-click='ctrl.nextStep()', ng-disabled='ctrl.state.lockContinue' - ) - .sk-label {{ctrl.state.continueTitle}} + ) {{ctrl.state.continueTitle}} diff --git a/app/assets/templates/directives/permissions-modal.pug b/app/assets/templates/directives/permissions-modal.pug deleted file mode 100644 index d401c97b1..000000000 --- a/app/assets/templates/directives/permissions-modal.pug +++ /dev/null @@ -1,25 +0,0 @@ -.sk-modal-background(ng-click='ctrl.deny()') -#permissions-modal.sk-modal-content - .sn-component - .sk-panel - .sk-panel-header - .sk-panel-header-title Activate Extension - a.sk-a.info.close-button(ng-click='ctrl.deny()') Cancel - .sk-panel-content - .sk-panel-section - .sk-panel-row - .sk-h2 - strong {{ctrl.component.name}} - | would like to interact with your - | {{ctrl.permissionsString}} - .sk-panel-row - p.sk-p - | Extensions use an offline messaging system to communicate. Learn more at - a.sk-a.info( - href='https://standardnotes.org/permissions', - rel='noopener', - target='_blank' - ) https://standardnotes.org/permissions. - .sk-panel-footer - .sk-button.info.big.block.font-bold(ng-click='ctrl.accept()') - .sk-label Continue diff --git a/app/assets/templates/directives/sync-resolution-menu.pug b/app/assets/templates/directives/sync-resolution-menu.pug index 471944349..46b18ca16 100644 --- a/app/assets/templates/directives/sync-resolution-menu.pug +++ b/app/assets/templates/directives/sync-resolution-menu.pug @@ -6,44 +6,41 @@ .sk-panel-content .sk-panel-section .sk-panel-row.sk-p - | We've detected that the data on the server may not match + | We've detected that the data on the server may not match | the data in the current application session. .sk-p.sk-panel-row .sk-panel-column strong.sk-panel-row Option 1 — Restart App: - .sk-p - | Quit the application and re-open it. + .sk-p + | Quit the application and re-open it. | Sometimes, this may resolve the issue. .sk-p.sk-panel-row .sk-panel-column strong.sk-panel-row Option 2 (recommended) — Sign Out: .sk-p - | Sign out of your account, then sign back in. + | Sign out of your account, then sign back in. | This will ensure your data is consistent with the server. | Be sure to download a backup of your data before doing so. .sk-p.sk-panel-row .sk-panel-column strong.sk-panel-row Option 3 — Sync Resolution: .sk-p - | We can attempt to reconcile changes by downloading all data from the - | server. No existing data will be overwritten. If the local contents of + | We can attempt to reconcile changes by downloading all data from the + | server. No existing data will be overwritten. If the local contents of | an item differ from what the server has, a conflicted copy will be created. div(ng-if='!ctrl.status.backupFinished') .sk-p.sk-panel-row - | Please download a backup before we attempt to + | Please download a backup before we attempt to | perform a full account sync resolution. .sk-panel-row - .sk-button-group - .sk-button.info(ng-click='ctrl.downloadBackup(true)') - .sk-label Encrypted - .sk-button.info(ng-click='ctrl.downloadBackup(false)') - .sk-label Decrypted - .sk-button.danger(ng-click='ctrl.skipBackup()') - .sk-label Skip + .flex.gap-2 + button.sn-button.info(ng-click='ctrl.downloadBackup(true)') Encrypted + button.sn-button.info(ng-click='ctrl.downloadBackup(false)') Decrypted + button.sn-button.danger(ng-click='ctrl.skipBackup()') Skip div(ng-if='ctrl.status.backupFinished') .sk-panel-row(ng-if='!ctrl.status.resolving && !ctrl.status.attemptedResolution') - .sk-button.info(ng-click='ctrl.performSyncResolution()') - .sk-label Perform Sync Resolution + button.sn-button.info(ng-click='ctrl.performSyncResolution()') + | Perform Sync Resolution .sk-panel-row.justify-left(ng-if='ctrl.status.resolving') .sk-horizontal-group .sk-spinner.small.info @@ -51,8 +48,8 @@ .sk-panel-column(ng-if='ctrl.status.fail') .sk-panel-row.sk-label.danger Sync Resolution Failed .sk-p.sk-panel-row - | We attempted to reconcile local content and server content, but were - | unable to do so. At this point, we recommend signing out of your account + | We attempted to reconcile local content and server content, but were + | unable to do so. At this point, we recommend signing out of your account | and signing back in. You may wish to download a data backup before doing so. .sk-panel-column(ng-if='ctrl.status.success') .sk-panel-row.sk-label.success Sync Resolution Success diff --git a/package.json b/package.json index 6a94b0981..d2339387d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "standard-notes-web", - "version": "3.6.2", + "version": "3.6.3", "license": "AGPL-3.0-or-later", "repository": { "type": "git", @@ -53,7 +53,7 @@ "pug-loader": "^2.4.0", "sass-loader": "^8.0.2", "serve-static": "^1.14.1", - "sn-stylekit": "^2.2.1", + "sn-stylekit": "github:standardnotes/StyleKit#1ec13454cc6d8cf97651263a039de8872be527b8", "ts-loader": "^8.0.17", "typescript": "^4.1.5", "typescript-eslint": "0.0.1-alpha.0", @@ -68,7 +68,7 @@ "@reach/alert-dialog": "^0.13.0", "@reach/dialog": "^0.13.0", "@standardnotes/sncrypto-web": "^1.2.10", - "@standardnotes/snjs": "^2.0.67", + "@standardnotes/snjs": "^2.0.70", "mobx": "^6.1.6", "preact": "^10.5.12" } diff --git a/yarn.lock b/yarn.lock index e12a45984..b2ccc02d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1850,10 +1850,10 @@ "@standardnotes/sncrypto-common" "^1.2.7" libsodium-wrappers "^0.7.8" -"@standardnotes/snjs@^2.0.67": - version "2.0.67" - resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.0.67.tgz#87e29f40bb5efaa36f30ddc5905164f7dce194d9" - integrity sha512-XCDxlFQCh0zmV3Hc9mjU7ritZ/2Ma5JPoCbDy4CIAlkKdmVL4tu/4jCfRFILM0zpKF/kLsCTbLGdG7TgU/ReKg== +"@standardnotes/snjs@^2.0.70": + version "2.0.70" + resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.0.70.tgz#259a4fadc4f9b360f35a399b674b921179bd5768" + integrity sha512-D11E+sCeoram2+NhQuFmn333JoHr1kGjVZrkRmZlfhGi+dYXJqZuDDDHbRn5WEJQneDg43Z8DjjAlxL+Mqhp6w== dependencies: "@standardnotes/auth" "^2.0.0" "@standardnotes/sncrypto-common" "^1.2.9" @@ -7808,10 +7808,9 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" -sn-stylekit@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/sn-stylekit/-/sn-stylekit-2.2.1.tgz#4a81a05b2b2d67a11af9d06f3964ac1ece3aa84a" - integrity sha512-mrvUbf2HbWOfxbNglo7RXa5JBe9UV9rurupeGoX/Kh4/lVlB2n/56ZT103xk/4tp0/mVCpxjD7Dt1stKFxsvFA== +"sn-stylekit@github:standardnotes/StyleKit#1ec13454cc6d8cf97651263a039de8872be527b8": + version "3.0.1" + resolved "https://codeload.github.com/standardnotes/StyleKit/tar.gz/1ec13454cc6d8cf97651263a039de8872be527b8" snapdragon-node@^2.0.1: version "2.1.1"