feat: snjs app groups (#468)

* feat: snjs app groups

* fix: update snjs version to point to wip commit

* wip: account switcher

* feat: rename lock manager to auto lock service

* fix: more relevant sign out copy

* chore(deps): update snjs

* fix: use setTimeout instead of setImmediate

* feat: make account switcher expiremental feature

* chore(deps): upgrade snjs
This commit is contained in:
Mo Bitar
2020-09-15 10:55:32 -05:00
committed by GitHub
parent ae6ef50f88
commit 2b6abeebfc
46 changed files with 590 additions and 375 deletions

View File

@@ -3,7 +3,7 @@
.left
.sk-app-bar-item(
click-outside='ctrl.clickOutsideAccountMenu()',
is-open='ctrl.showAccountMenu',
is-open='ctrl.showAccountMenu',
ng-click='ctrl.accountMenuPressed()'
)
.sk-app-bar-item-column
@@ -13,15 +13,15 @@
.sk-app-bar-item-column
.sk-label.title(ng-class='{red: ctrl.hasError}') Account
account-menu(
close-function='ctrl.closeAccountMenu()',
ng-click='$event.stopPropagation()',
close-function='ctrl.closeAccountMenu()',
ng-click='$event.stopPropagation()',
ng-if='ctrl.showAccountMenu',
application='ctrl.application'
)
.sk-app-bar-item
a.no-decoration.sk-label.title(
href='https://standardnotes.org/help',
rel='noopener',
href='https://standardnotes.org/help',
rel='noopener',
target='_blank'
)
| Help
@@ -30,8 +30,8 @@
.sk-app-bar-item-column(ng-click='ctrl.selectRoom(room)')
.sk-label {{room.name}}
component-modal(
component-uuid='room.uuid',
ng-if='ctrl.roomShowState[room.uuid]',
component-uuid='room.uuid',
ng-if='ctrl.roomShowState[room.uuid]',
on-dismiss='ctrl.onRoomDismiss(room)',
application='ctrl.application'
)
@@ -41,12 +41,12 @@
span.neutral.sk-label {{ctrl.arbitraryStatusMessage}}
.right
.sk-app-bar-item(
ng-click='ctrl.openSecurityUpdate()',
ng-click='ctrl.openSecurityUpdate()',
ng-if='ctrl.state.dataUpgradeAvailable'
)
span.success.sk-label Encryption upgrade available.
.sk-app-bar-item(
ng-click='ctrl.clickedNewUpdateAnnouncement()',
ng-click='ctrl.clickedNewUpdateAnnouncement()',
ng-if='ctrl.newUpdateAvailable == true'
)
span.info.sk-label New update available.
@@ -56,13 +56,13 @@
.sk-label.subtle(ng-if='!ctrl.offline')
| Last refreshed {{ctrl.lastSyncDate}}
.sk-app-bar-item(
ng-click='ctrl.toggleSyncResolutionMenu()',
ng-click='ctrl.toggleSyncResolutionMenu()',
ng-if='(ctrl.state.outOfSync && !ctrl.isRefreshing) || ctrl.showSyncResolution'
)
.sk-label.warning(ng-if='ctrl.state.outOfSync') Potentially Out of Sync
sync-resolution-menu(
close-function='ctrl.toggleSyncResolutionMenu()',
ng-click='$event.stopPropagation();',
close-function='ctrl.toggleSyncResolutionMenu()',
ng-click='$event.stopPropagation();',
ng-if='ctrl.showSyncResolution',
application='ctrl.application'
)
@@ -70,27 +70,35 @@
.sk-spinner.small
.sk-app-bar-item(ng-if='ctrl.offline')
.sk-label Offline
.sk-app-bar-item(ng-click='ctrl.refreshData()', ng-if='!ctrl.offline')
.sk-app-bar-item(ng-click='ctrl.refreshData()' ng-if='!ctrl.offline')
.sk-label Refresh
.sk-app-bar-item.border(ng-if='ctrl.state.dockShortcuts.length > 0')
.sk-app-bar-item.dock-shortcut(ng-repeat='shortcut in ctrl.state.dockShortcuts')
.sk-app-bar-item-column(
ng-class="{'underline': shortcut.component.active}",
ng-class="{'underline': shortcut.component.active}",
ng-click='ctrl.selectShortcut(shortcut)'
)
.div(ng-if="shortcut.icon.type == 'circle'", title='{{shortcut.name}}')
.div(ng-if="shortcut.icon.type == 'circle'" title='{{shortcut.name}}')
.sk-circle.small(
ng-style="{'background-color': shortcut.icon.background_color, 'border-color': shortcut.icon.border_color}"
)
.div(ng-if="shortcut.icon.type == 'svg'", title='{{shortcut.name}}')
.div(ng-if="shortcut.icon.type == 'svg'" title='{{shortcut.name}}')
.svg-item(
elem-ready='ctrl.initSvgForShortcut(shortcut)',
elem-ready='ctrl.initSvgForShortcut(shortcut)',
ng-attr-id='dock-svg-{{shortcut.component.uuid}}'
)
.sk-app-bar-item.border(ng-if='ctrl.state.hasAccountSwitcher')
.sk-app-bar-item(
ng-if='ctrl.state.hasAccountSwitcher'
ng-click='ctrl.openAccountSwitcher()',
)
#account-switcher-icon(ng-class='{"alone": !ctrl.state.hasPasscode}')
svg.info.ionicon
use(href="#layers-sharp")
.sk-app-bar-item.border(ng-if='ctrl.state.hasPasscode')
#lock-item.sk-app-bar-item(
ng-click='ctrl.lockApp()',
ng-if='ctrl.state.hasPasscode',
ng-click='ctrl.lockApp()',
ng-if='ctrl.state.hasPasscode',
title='Locks application and wipes unencrypted data from memory.'
)
.sk-label

View File

@@ -1,3 +1,4 @@
import { ApplicationGroup } from '@/ui_models/application_group';
import { FooterStatus, WebDirective } from '@/types';
import { dateToLocalizedString, preventRefreshing } from '@/utils';
import {
@@ -11,7 +12,7 @@ import {
ComponentAction,
topLevelCompare,
CollectionSort,
ComponentMutator,
ComponentMutator
} from 'snjs';
import template from './footer-view.pug';
import { AppStateEvent, EventSource } from '@/ui_models/app_state';
@@ -22,6 +23,14 @@ import {
} from '@/strings';
import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl';
/**
* Disable before production release.
* Anyone who used the beta will still have access to
* the account switcher in production via local storage flag
*/
const ACCOUNT_SWITCHER_ENABLED = true;
const ACCOUNT_SWITCHER_FEATURE_KEY = 'account_switcher';
type DockShortcut = {
name: string,
component: SNComponent,
@@ -37,8 +46,8 @@ class FooterViewCtrl extends PureViewCtrl<{}, {
hasPasscode: boolean;
dataUpgradeAvailable: boolean;
dockShortcuts: DockShortcut[];
hasAccountSwitcher: boolean
}> {
private $rootScope: ng.IRootScopeService
private rooms: SNComponent[] = []
private themesWithIcons: SNTheme[] = []
@@ -66,6 +75,7 @@ class FooterViewCtrl extends PureViewCtrl<{}, {
constructor(
$rootScope: ng.IRootScopeService,
$timeout: ng.ITimeoutService,
private mainApplicationGroup: ApplicationGroup
) {
super($timeout);
this.$rootScope = $rootScope;
@@ -92,11 +102,22 @@ class FooterViewCtrl extends PureViewCtrl<{}, {
$onInit() {
super.$onInit();
this.application!.getStatusService().addStatusObserver((string: string) => {
this.application.getStatusService().addStatusObserver((string: string) => {
this.$timeout(() => {
this.arbitraryStatusMessage = string;
});
});
this.loadAccountSwitcherState();
}
loadAccountSwitcherState() {
const stringValue = localStorage.getItem(ACCOUNT_SWITCHER_FEATURE_KEY);
if (!stringValue && ACCOUNT_SWITCHER_ENABLED) {
/** Enable permanently for this user so they don't lose the feature after its disabled */
localStorage.setItem(ACCOUNT_SWITCHER_FEATURE_KEY, JSON.stringify(true));
}
const hasAccountSwitcher = stringValue ? JSON.parse(stringValue) : ACCOUNT_SWITCHER_ENABLED;
this.setState({ hasAccountSwitcher });
}
getInitialState() {
@@ -105,17 +126,24 @@ class FooterViewCtrl extends PureViewCtrl<{}, {
dataUpgradeAvailable: false,
hasPasscode: false,
dockShortcuts: [],
descriptors: this.mainApplicationGroup.getDescriptors(),
hasAccountSwitcher: false
};
}
reloadUpgradeStatus() {
this.application!.checkForSecurityUpdate().then((available) => {
this.application.checkForSecurityUpdate().then((available) => {
this.setState({
dataUpgradeAvailable: available
});
});
}
/** @template */
openAccountSwitcher() {
this.application.openAccountSwitcher();
}
async onAppLaunch() {
super.onAppLaunch();
this.reloadPasscodeStatus();
@@ -128,11 +156,11 @@ class FooterViewCtrl extends PureViewCtrl<{}, {
}
reloadUser() {
this.user = this.application!.getUser();
this.user = this.application.getUser();
}
async reloadPasscodeStatus() {
const hasPasscode = this.application!.hasPasscode();
const hasPasscode = this.application.hasPasscode();
this.setState({
hasPasscode: hasPasscode
});
@@ -157,23 +185,23 @@ class FooterViewCtrl extends PureViewCtrl<{}, {
this.closeAccountMenu();
}
} else if (eventName === AppStateEvent.BeganBackupDownload) {
this.backupStatus = this.application!.getStatusService().addStatusFromString(
this.backupStatus = this.application.getStatusService().addStatusFromString(
"Saving local backup..."
);
} else if (eventName === AppStateEvent.EndedBackupDownload) {
if (data.success) {
this.backupStatus = this.application!.getStatusService().replaceStatusWithString(
this.backupStatus = this.application.getStatusService().replaceStatusWithString(
this.backupStatus!,
"Successfully saved backup."
);
} else {
this.backupStatus = this.application!.getStatusService().replaceStatusWithString(
this.backupStatus = this.application.getStatusService().replaceStatusWithString(
this.backupStatus!,
"Unable to save local backup."
);
}
this.$timeout(() => {
this.backupStatus = this.application!.getStatusService().removeStatus(this.backupStatus!);
this.backupStatus = this.application.getStatusService().removeStatus(this.backupStatus!);
}, 2000);
}
}
@@ -199,7 +227,7 @@ class FooterViewCtrl extends PureViewCtrl<{}, {
} else if (eventName === ApplicationEvent.CompletedFullSync) {
if (!this.didCheckForOffline) {
this.didCheckForOffline = true;
if (this.offline && this.application!.getNoteCount() === 0) {
if (this.offline && this.application.getNoteCount() === 0) {
this.showAccountMenu = true;
}
}
@@ -230,10 +258,10 @@ class FooterViewCtrl extends PureViewCtrl<{}, {
}
)
this.observerRemovers.push(this.application!.streamItems(
this.observerRemovers.push(this.application.streamItems(
ContentType.Component,
async () => {
const components = this.application!.getItems(ContentType.Component) as SNComponent[];
const components = this.application.getItems(ContentType.Component) as SNComponent[];
this.rooms = components.filter((candidate) => {
return candidate.area === ComponentArea.Rooms && !candidate.deleted;
});
@@ -244,10 +272,10 @@ class FooterViewCtrl extends PureViewCtrl<{}, {
}
));
this.observerRemovers.push(this.application!.streamItems(
this.observerRemovers.push(this.application.streamItems(
ContentType.Theme,
async () => {
const themes = this.application!.getDisplayableItems(ContentType.Theme) as SNTheme[];
const themes = this.application.getDisplayableItems(ContentType.Theme) as SNTheme[];
this.themesWithIcons = themes;
this.reloadDockShortcuts();
}
@@ -255,14 +283,14 @@ class FooterViewCtrl extends PureViewCtrl<{}, {
}
registerComponentHandler() {
this.unregisterComponent = this.application!.componentManager!.registerHandler({
this.unregisterComponent = this.application.componentManager!.registerHandler({
identifier: 'room-bar',
areas: [ComponentArea.Rooms, ComponentArea.Modal],
actionHandler: (component, action, data) => {
if (action === ComponentAction.SetSize) {
/** Do comparison to avoid repetitive calls by arbitrary component */
if (!topLevelCompare(component.getLastSize(), data)) {
this.application!.changeItem<ComponentMutator>(component.uuid, (mutator) => {
this.application.changeItem<ComponentMutator>(component.uuid, (mutator) => {
mutator.setLastSize(data);
})
}
@@ -288,7 +316,7 @@ class FooterViewCtrl extends PureViewCtrl<{}, {
* then closing it after a short delay.
*/
const extWindow = this.rooms.find((room) => {
return room.package_info.identifier === this.application!
return room.package_info.identifier === this.application
.getNativeExtService().extManagerId;
});
if (!extWindow) {
@@ -305,17 +333,17 @@ class FooterViewCtrl extends PureViewCtrl<{}, {
}
updateOfflineStatus() {
this.offline = this.application!.noAccount();
this.offline = this.application.noAccount();
}
async openSecurityUpdate() {
preventRefreshing(STRING_CONFIRM_APP_QUIT_DURING_UPGRADE, async () => {
await this.application!.performProtocolUpgrade();
await this.application.performProtocolUpgrade();
});
}
findErrors() {
this.hasError = this.application!.getSyncStatus().hasError();
this.hasError = this.application.getSyncStatus().hasError();
}
accountMenuPressed() {
@@ -332,12 +360,12 @@ class FooterViewCtrl extends PureViewCtrl<{}, {
}
lockApp() {
this.application!.lock();
this.application.lock();
}
refreshData() {
this.isRefreshing = true;
this.application!.sync({
this.application.sync({
queueStrategy: SyncQueueStrategy.ForceSpawnNew,
checkIntegrity: true
}).then((response) => {
@@ -345,7 +373,7 @@ class FooterViewCtrl extends PureViewCtrl<{}, {
this.isRefreshing = false;
}, 200);
if (response && response.error) {
this.application!.alertService!.alert(
this.application.alertService!.alert(
STRING_GENERIC_SYNC_ERROR
);
} else {
@@ -355,7 +383,7 @@ class FooterViewCtrl extends PureViewCtrl<{}, {
}
syncUpdated() {
this.lastSyncDate = dateToLocalizedString(this.application!.getLastSyncDate()!);
this.lastSyncDate = dateToLocalizedString(this.application.getLastSyncDate()!);
}
onNewUpdateAvailable() {
@@ -364,7 +392,7 @@ class FooterViewCtrl extends PureViewCtrl<{}, {
clickedNewUpdateAnnouncement() {
this.newUpdateAvailable = false;
this.application!.alertService!.alert(
this.application.alertService!.alert(
STRING_NEW_UPDATE_READY
);
}
@@ -409,7 +437,7 @@ class FooterViewCtrl extends PureViewCtrl<{}, {
}
selectShortcut(shortcut: DockShortcut) {
this.application!.toggleComponent(shortcut.component);
this.application.toggleComponent(shortcut.component);
}
onRoomDismiss(room: SNComponent) {
@@ -430,12 +458,12 @@ class FooterViewCtrl extends PureViewCtrl<{}, {
};
if (!this.roomShowState[room.uuid]) {
const requiresPrivilege = await this.application!.privilegesService!
const requiresPrivilege = await this.application.privilegesService!
.actionRequiresPrivilege(
ProtectedAction.ManageExtensions
);
if (requiresPrivilege) {
this.application!.presentPrivilegesModal(
this.application.presentPrivilegesModal(
ProtectedAction.ManageExtensions,
run
);
@@ -448,7 +476,7 @@ class FooterViewCtrl extends PureViewCtrl<{}, {
}
clickOutsideAccountMenu() {
if (this.application && this.application!.authenticationInProgress()) {
if (this.application && this.application.authenticationInProgress()) {
return;
}
this.showAccountMenu = false;