diff --git a/app/assets/javascripts/directives/views/accountMenu.ts b/app/assets/javascripts/directives/views/accountMenu.ts index 505a68d93..f09ba210a 100644 --- a/app/assets/javascripts/directives/views/accountMenu.ts +++ b/app/assets/javascripts/directives/views/accountMenu.ts @@ -26,6 +26,7 @@ import { SyncOpStatus } from 'snjs/dist/@types/services/sync/sync_op_status'; import { PasswordWizardType } from '@/types'; import { BackupFile } from 'snjs/dist/@types/services/protocol_service'; import { confirmDialog, alertDialog } from '@/services/alertService'; +import { autorun, IReactionDisposer } from 'mobx'; const ELEMENT_ID_IMPORT_PASSWORD_INPUT = 'import-password-request'; @@ -63,6 +64,7 @@ type AccountMenuState = { server: string; encryptionEnabled: boolean; selectedAutoLockInterval: any; + showBetaWarning: boolean; } class AccountMenuCtrl extends PureViewCtrl<{}, AccountMenuState> { @@ -71,6 +73,7 @@ class AccountMenuCtrl extends PureViewCtrl<{}, AccountMenuState> { /** @template */ syncStatus?: SyncOpStatus private closeFunction?: () => void + private removeBetaWarningListener?: IReactionDisposer /* @ngInject */ constructor( @@ -91,7 +94,8 @@ class AccountMenuCtrl extends PureViewCtrl<{}, AccountMenuState> { mergeLocal: true, ephemeral: false, }, - mutable: {} + mutable: {}, + showBetaWarning: false, } as AccountMenuState; } @@ -124,6 +128,16 @@ class AccountMenuCtrl extends PureViewCtrl<{}, AccountMenuState> { $onInit() { super.$onInit(); this.syncStatus = this.application!.getSyncStatus(); + this.removeBetaWarningListener = autorun(() => { + this.setState({ + showBetaWarning: this.appState.showBetaWarning + }); + }); + } + + deinit() { + this.removeBetaWarningListener?.(); + super.deinit(); } close() { diff --git a/app/assets/javascripts/ui_models/app_state.ts b/app/assets/javascripts/ui_models/app_state.ts index 88e8a4965..f22e177de 100644 --- a/app/assets/javascripts/ui_models/app_state.ts +++ b/app/assets/javascripts/ui_models/app_state.ts @@ -8,10 +8,12 @@ import { SNUserPrefs, ContentType, SNSmartTag, - PayloadSource + PayloadSource, + DeinitSource } from 'snjs'; import { WebApplication } from '@/ui_models/application'; import { Editor } from '@/ui_models/editor'; +import { action, makeObservable, observable } from 'mobx'; export enum AppStateEvent { TagChanged = 1, @@ -32,19 +34,22 @@ export enum EventSource { type ObserverCallback = (event: AppStateEvent, data?: any) => Promise +const SHOW_BETA_WARNING_KEY = 'show_beta_warning'; + export class AppState { - $rootScope: ng.IRootScopeService - $timeout: ng.ITimeoutService - application: WebApplication - observers: ObserverCallback[] = [] - locked = true - unsubApp: any - rootScopeCleanup1: any - rootScopeCleanup2: any - onVisibilityChange: any - selectedTag?: SNTag - userPreferences?: SNUserPrefs - multiEditorEnabled = false + $rootScope: ng.IRootScopeService; + $timeout: ng.ITimeoutService; + application: WebApplication; + observers: ObserverCallback[] = []; + locked = true; + unsubApp: any; + rootScopeCleanup1: any; + rootScopeCleanup2: any; + onVisibilityChange: any; + selectedTag?: SNTag; + userPreferences?: SNUserPrefs; + multiEditorEnabled = false; + showBetaWarning = false; /* @ngInject */ constructor( @@ -55,6 +60,11 @@ export class AppState { this.$timeout = $timeout; this.$rootScope = $rootScope; this.application = application; + makeObservable(this, { + showBetaWarning: observable, + enableBetaWarning: action, + disableBetaWarning: action, + }); this.addAppEventObserver(); this.streamNotesAndTags(); this.onVisibilityChange = () => { @@ -65,9 +75,13 @@ export class AppState { this.notifyEvent(event); } this.registerVisibilityObservers(); + this.determineBetaWarningValue(); } - deinit() { + deinit(source: DeinitSource) { + if (source === DeinitSource.SignOut) { + localStorage.removeItem(SHOW_BETA_WARNING_KEY); + } this.unsubApp(); this.unsubApp = undefined; this.observers.length = 0; @@ -81,6 +95,34 @@ export class AppState { this.onVisibilityChange = undefined; } + disableBetaWarning() { + this.showBetaWarning = false; + localStorage.setItem(SHOW_BETA_WARNING_KEY, 'false'); + } + + enableBetaWarning() { + this.showBetaWarning = true; + localStorage.setItem(SHOW_BETA_WARNING_KEY, 'true'); + } + + clearBetaWarning() { + localStorage.setItem(SHOW_BETA_WARNING_KEY, 'true'); + } + + private determineBetaWarningValue() { + if ((window as any).electronAppVersion?.includes('-beta')) { + switch (localStorage.getItem(SHOW_BETA_WARNING_KEY)) { + case 'true': + default: + this.enableBetaWarning(); + break; + case 'false': + this.disableBetaWarning(); + break; + } + } + } + /** * Creates a new editor if one doesn't exist. If one does, we'll replace the * editor's note with an empty one. diff --git a/app/assets/javascripts/ui_models/application.ts b/app/assets/javascripts/ui_models/application.ts index 29e8a3fa8..ebf1323c0 100644 --- a/app/assets/javascripts/ui_models/application.ts +++ b/app/assets/javascripts/ui_models/application.ts @@ -81,12 +81,11 @@ export class WebApplication extends SNApplication { /** @override */ deinit(source: DeinitSource) { - for (const key of Object.keys(this.webServices)) { - const service = (this.webServices as any)[key]; - if (service.deinit) { - service.deinit(); + for (const service of Object.values(this.webServices)) { + if ('deinit' in service) { + service.deinit?.(source); } - service.application = undefined; + (service as any).application = undefined; } this.webServices = {} as WebServices; (this.$compile as any) = undefined; diff --git a/app/assets/javascripts/views/footer/footer-view.pug b/app/assets/javascripts/views/footer/footer-view.pug index 7e069a88c..06883ce2a 100644 --- a/app/assets/javascripts/views/footer/footer-view.pug +++ b/app/assets/javascripts/views/footer/footer-view.pug @@ -34,7 +34,14 @@ ng-if='ctrl.roomShowState[room.uuid]', on-dismiss='ctrl.onRoomDismiss(room)', application='ctrl.application' - ) + ) + .sk-app-bar-item.border(ng-if="ctrl.state.showBetaWarning") + .sk-app-bar-item(ng-if="ctrl.state.showBetaWarning") + a.no-decoration.sk-label.title.uppercase( + href='https://github.com/standardnotes/forum/issues/1114', + rel='noopener', + target='_blank' + ) You are using a beta version of the app .center .sk-app-bar-item(ng-if='ctrl.arbitraryStatusMessage') .sk-app-bar-item-column @@ -42,7 +49,7 @@ .right .sk-app-bar-item( ng-click='ctrl.openSecurityUpdate()', - ng-if='ctrl.state.dataUpgradeAvailable' + ng-if='ctrl.state.dataUpgradeAvailable && ctrl.state.showDataUpgrade' ) span.success.sk-label Encryption upgrade available. .sk-app-bar-item( diff --git a/app/assets/javascripts/views/footer/footer_view.ts b/app/assets/javascripts/views/footer/footer_view.ts index e749a5855..0b4ee5486 100644 --- a/app/assets/javascripts/views/footer/footer_view.ts +++ b/app/assets/javascripts/views/footer/footer_view.ts @@ -27,6 +27,7 @@ import { } from '@/strings'; import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl'; import { confirmDialog } from '@/services/alertService'; +import { autorun, IReactionDisposer } from 'mobx'; /** * Disable before production release. @@ -51,7 +52,9 @@ class FooterViewCtrl extends PureViewCtrl<{}, { hasPasscode: boolean; dataUpgradeAvailable: boolean; dockShortcuts: DockShortcut[]; - hasAccountSwitcher: boolean + hasAccountSwitcher: boolean; + showBetaWarning: boolean; + showDataUpgrade: boolean; }> { private $rootScope: ng.IRootScopeService private rooms: SNComponent[] = [] @@ -76,6 +79,7 @@ class FooterViewCtrl extends PureViewCtrl<{}, { private observerRemovers: Array<() => void> = []; private completedInitialSync = false; private showingDownloadStatus = false; + private removeBetaWarningListener?: IReactionDisposer; /* @ngInject */ constructor( @@ -103,6 +107,7 @@ class FooterViewCtrl extends PureViewCtrl<{}, { this.rootScopeListener2 = undefined; (this.closeAccountMenu as any) = undefined; (this.toggleSyncResolutionMenu as any) = undefined; + this.removeBetaWarningListener?.(); super.deinit(); } @@ -114,6 +119,13 @@ class FooterViewCtrl extends PureViewCtrl<{}, { }); }); this.loadAccountSwitcherState(); + this.removeBetaWarningListener = autorun(() => { + const showBetaWarning = this.appState.showBetaWarning; + this.setState({ + showBetaWarning: showBetaWarning, + showDataUpgrade: !showBetaWarning + }); + }); } loadAccountSwitcherState() { @@ -133,7 +145,9 @@ class FooterViewCtrl extends PureViewCtrl<{}, { hasPasscode: false, dockShortcuts: [], descriptors: this.mainApplicationGroup.getDescriptors(), - hasAccountSwitcher: false + hasAccountSwitcher: false, + showBetaWarning: false, + showDataUpgrade: false, }; } diff --git a/app/assets/stylesheets/_main.scss b/app/assets/stylesheets/_main.scss index ec3fbd982..cacf5add5 100644 --- a/app/assets/stylesheets/_main.scss +++ b/app/assets/stylesheets/_main.scss @@ -36,6 +36,10 @@ body { box-sizing: border-box; } +.uppercase { + text-transform: uppercase; +} + .tinted { color: var(--sn-stylekit-info-color); } diff --git a/app/assets/templates/directives/account-menu.pug b/app/assets/templates/directives/account-menu.pug index 4e9f08255..e55e232a0 100644 --- a/app/assets/templates/directives/account-menu.pug +++ b/app/assets/templates/directives/account-menu.pug @@ -164,13 +164,12 @@ .sk-panel-row a.sk-a.info.sk-panel-row.condensed( ng-click="self.openPasswordWizard()" - ) - | Change Password + ng-if="!self.state.showBetaWarning" + ) Change Password a.sk-a.info.sk-panel-row.condensed( ng-click="self.openPrivilegesModal('')", ng-show='self.state.user' - ) - | Manage Privileges + ) Manage Privileges .sk-panel-section .sk-panel-section-title Encryption .sk-panel-section-subtitle.info(ng-if='self.state.encryptionEnabled') @@ -305,7 +304,12 @@ .sk-spinner.small.info(ng-if='self.state.importData.loading') .sk-panel-footer .sk-panel-row - .sk-p.left.neutral.faded {{self.state.appVersion}} + .sk-p.left.neutral + span.faded {{self.state.appVersion}} + span(ng-if="self.state.showBetaWarning") + span.faded ( + a.sk-a(ng-click="self.appState.disableBetaWarning()") Hide beta warning + span.faded ) a.sk-a.right( ng-click='self.hidePasswordForm()', ng-if='self.state.formData.showLogin || self.state.formData.showRegister' diff --git a/package-lock.json b/package-lock.json index 18e2498f3..10ad5d6a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8380,6 +8380,11 @@ "minimist": "0.0.8" } }, + "mobx": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.0.1.tgz", + "integrity": "sha512-Pk6uJXZ34yqd661yRmS6z/9avm4FOGXpFpVjnEfiYYOsZXnAxv1fpYjxTCEZ9tuwk0Xe1qnUUlgm+rJtGe0YJA==" + }, "mocha": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.0.tgz", diff --git a/package.json b/package.json index 72dc912b5..28e0375f9 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "webpack-merge": "^4.2.2" }, "dependencies": { + "mobx": "^6.0.1", "sncrypto": "github:standardnotes/sncrypto#8794c88daa967eaae493cd5fdec7506d52b257ad", "snjs": "github:standardnotes/snjs#2009f754384475bfc6a2658eb247e1192f12c784" }