Footer TS
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { PanelPuppet, WebDirective, PermissionsModalScope, ModalComponentScope } from './../types';
|
||||
import { getPlatformString } from '@/utils';
|
||||
import template from '%/application-view.pug';
|
||||
import { AppStateEvent } from '@/services/state';
|
||||
import { ApplicationEvent } from 'snjs';
|
||||
import { ApplicationEvent, SNComponent } from 'snjs';
|
||||
import angular from 'angular';
|
||||
import {
|
||||
PANEL_NAME_NOTES,
|
||||
@@ -12,14 +13,27 @@ import {
|
||||
STRING_DEFAULT_FILE_ERROR
|
||||
} from '@/strings';
|
||||
import { PureCtrl } from './abstract/pure_ctrl';
|
||||
import { PermissionDialog } from '@/../../../../snjs/dist/@types/services/component_manager';
|
||||
|
||||
class ApplicationViewCtrl extends PureCtrl {
|
||||
private $compile?: ng.ICompileService
|
||||
private $location?: ng.ILocationService
|
||||
private $rootScope?: ng.IRootScopeService
|
||||
public platformString: string
|
||||
private completedInitialSync = false
|
||||
private syncStatus: any
|
||||
private notesCollapsed = false
|
||||
private tagsCollapsed = false
|
||||
private showingDownloadStatus = false
|
||||
private uploadSyncStatus: any
|
||||
private lastAlertShownDate?: Date
|
||||
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
$compile,
|
||||
$location,
|
||||
$rootScope,
|
||||
$timeout
|
||||
$compile: ng.ICompileService,
|
||||
$location: ng.ILocationService,
|
||||
$rootScope: ng.IRootScopeService,
|
||||
$timeout: ng.ITimeoutService
|
||||
) {
|
||||
super($timeout);
|
||||
this.$location = $location;
|
||||
@@ -35,17 +49,16 @@ class ApplicationViewCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
deinit() {
|
||||
this.$location = null;
|
||||
this.$rootScope = null;
|
||||
this.$compile = null;
|
||||
this.application = null;
|
||||
this.lockScreenPuppet = null;
|
||||
this.$location = undefined;
|
||||
this.$rootScope = undefined;
|
||||
this.$compile = undefined;
|
||||
this.application = undefined;
|
||||
window.removeEventListener('dragover', this.onDragOver, true);
|
||||
window.removeEventListener('drop', this.onDragDrop, true);
|
||||
this.onDragDrop = null;
|
||||
this.onDragOver = null;
|
||||
this.openModalComponent = null;
|
||||
this.presentPermissionsDialog = null;
|
||||
(this.onDragDrop as any) = undefined;
|
||||
(this.onDragOver as any) = undefined;
|
||||
(this.openModalComponent as any) = undefined;
|
||||
(this.presentPermissionsDialog as any) = undefined;
|
||||
super.deinit();
|
||||
}
|
||||
|
||||
@@ -55,41 +68,39 @@ class ApplicationViewCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
async loadApplication() {
|
||||
await this.application.prepareForLaunch({
|
||||
callbacks: {
|
||||
receiveChallenge: async (challenge, orchestrator) => {
|
||||
this.application.promptForChallenge(challenge, orchestrator);
|
||||
}
|
||||
await this.application!.prepareForLaunch({
|
||||
receiveChallenge: async (challenge, orchestrator) => {
|
||||
this.application!.promptForChallenge(challenge, orchestrator);
|
||||
}
|
||||
});
|
||||
await this.application.launch();
|
||||
await this.application!.launch();
|
||||
|
||||
}
|
||||
|
||||
onAppStart() {
|
||||
async onAppStart() {
|
||||
super.onAppStart();
|
||||
this.overrideComponentManagerFunctions();
|
||||
this.application.componentManager.setDesktopManager(
|
||||
this.application.getDesktopService()
|
||||
this.application!.componentManager!.setDesktopManager(
|
||||
this.application!.getDesktopService()
|
||||
);
|
||||
this.setState({
|
||||
ready: true,
|
||||
needsUnlock: this.application.hasPasscode()
|
||||
needsUnlock: this.application!.hasPasscode()
|
||||
});
|
||||
}
|
||||
|
||||
onAppLaunch() {
|
||||
async onAppLaunch() {
|
||||
super.onAppLaunch();
|
||||
this.setState({ needsUnlock: false });
|
||||
this.handleAutoSignInFromParams();
|
||||
}
|
||||
|
||||
onUpdateAvailable() {
|
||||
this.$rootScope.$broadcast('new-update-available');
|
||||
this.$rootScope!.$broadcast('new-update-available');
|
||||
};
|
||||
|
||||
/** @override */
|
||||
async onAppEvent(eventName) {
|
||||
async onAppEvent(eventName: ApplicationEvent) {
|
||||
super.onAppEvent(eventName);
|
||||
if (eventName === ApplicationEvent.LocalDataIncrementalLoad) {
|
||||
this.updateLocalDataStatus();
|
||||
@@ -102,31 +113,31 @@ class ApplicationViewCtrl extends PureCtrl {
|
||||
this.updateLocalDataStatus();
|
||||
} else if (eventName === ApplicationEvent.WillSync) {
|
||||
if (!this.completedInitialSync) {
|
||||
this.syncStatus = this.application.getStatusService().replaceStatusWithString(
|
||||
this.syncStatus = this.application!.getStatusService().replaceStatusWithString(
|
||||
this.syncStatus,
|
||||
"Syncing..."
|
||||
);
|
||||
}
|
||||
} else if (eventName === ApplicationEvent.CompletedSync) {
|
||||
if (!this.completedInitialSync) {
|
||||
this.syncStatus = this.application.getStatusService().removeStatus(this.syncStatus);
|
||||
this.syncStatus = this.application!.getStatusService().removeStatus(this.syncStatus);
|
||||
this.completedInitialSync = true;
|
||||
}
|
||||
} else if (eventName === ApplicationEvent.InvalidSyncSession) {
|
||||
this.showInvalidSessionAlert();
|
||||
} else if (eventName === ApplicationEvent.LocalDatabaseReadError) {
|
||||
this.application.alertService.alert({
|
||||
text: 'Unable to load local database. Please restart the app and try again.'
|
||||
});
|
||||
this.application!.alertService!.alert(
|
||||
'Unable to load local database. Please restart the app and try again.'
|
||||
);
|
||||
} else if (eventName === ApplicationEvent.LocalDatabaseWriteError) {
|
||||
this.application.alertService.alert({
|
||||
text: 'Unable to write to local database. Please restart the app and try again.'
|
||||
});
|
||||
this.application!.alertService!.alert(
|
||||
'Unable to write to local database. Please restart the app and try again.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async onAppStateEvent(eventName, data) {
|
||||
async onAppStateEvent(eventName: AppStateEvent, data?: any) {
|
||||
if (eventName === AppStateEvent.PanelResized) {
|
||||
if (data.panel === PANEL_NAME_NOTES) {
|
||||
this.notesCollapsed = data.collapsed;
|
||||
@@ -139,41 +150,41 @@ class ApplicationViewCtrl extends PureCtrl {
|
||||
if (this.tagsCollapsed) { appClass += " collapsed-tags"; }
|
||||
this.setState({ appClass });
|
||||
} else if (eventName === AppStateEvent.WindowDidFocus) {
|
||||
if (!(await this.application.isLocked())) {
|
||||
this.application.sync();
|
||||
if (!(await this.application!.isLocked())) {
|
||||
this.application!.sync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateLocalDataStatus() {
|
||||
const syncStatus = this.application.getSyncStatus();
|
||||
const syncStatus = this.application!.getSyncStatus();
|
||||
const stats = syncStatus.getStats();
|
||||
const encryption = this.application.isEncryptionAvailable();
|
||||
const encryption = this.application!.isEncryptionAvailable();
|
||||
if (stats.localDataDone) {
|
||||
this.syncStatus = this.application.getStatusService().removeStatus(this.syncStatus);
|
||||
this.syncStatus = this.application!.getStatusService().removeStatus(this.syncStatus);
|
||||
return;
|
||||
}
|
||||
const notesString = `${stats.localDataCurrent}/${stats.localDataTotal} items...`;
|
||||
const loadingStatus = encryption
|
||||
? `Decrypting ${notesString}`
|
||||
: `Loading ${notesString}`;
|
||||
this.syncStatus = this.application.getStatusService().replaceStatusWithString(
|
||||
this.syncStatus = this.application!.getStatusService().replaceStatusWithString(
|
||||
this.syncStatus,
|
||||
loadingStatus
|
||||
);
|
||||
}
|
||||
|
||||
updateSyncStatus() {
|
||||
const syncStatus = this.application.getSyncStatus();
|
||||
const syncStatus = this.application!.getSyncStatus();
|
||||
const stats = syncStatus.getStats();
|
||||
if (syncStatus.hasError()) {
|
||||
this.syncStatus = this.application.getStatusService().replaceStatusWithString(
|
||||
this.syncStatus = this.application!.getStatusService().replaceStatusWithString(
|
||||
this.syncStatus,
|
||||
'Unable to Sync'
|
||||
);
|
||||
} else if (stats.downloadCount > 20) {
|
||||
const text = `Downloading ${stats.downloadCount} items. Keep app open.`;
|
||||
this.syncStatus = this.application.getStatusService().replaceStatusWithString(
|
||||
this.syncStatus = this.application!.getStatusService().replaceStatusWithString(
|
||||
this.syncStatus,
|
||||
text
|
||||
);
|
||||
@@ -181,56 +192,63 @@ class ApplicationViewCtrl extends PureCtrl {
|
||||
} else if (this.showingDownloadStatus) {
|
||||
this.showingDownloadStatus = false;
|
||||
const text = "Download Complete.";
|
||||
this.syncStatus = this.application.getStatusService().replaceStatusWithString(
|
||||
this.syncStatus = this.application!.getStatusService().replaceStatusWithString(
|
||||
this.syncStatus,
|
||||
text
|
||||
);
|
||||
setTimeout(() => {
|
||||
this.syncStatus = this.application.getStatusService().removeStatus(this.syncStatus);
|
||||
this.syncStatus = this.application!.getStatusService().removeStatus(this.syncStatus);
|
||||
}, 2000);
|
||||
} else if (stats.uploadTotalCount > 20) {
|
||||
this.uploadSyncStatus = this.application.getStatusService().replaceStatusWithString(
|
||||
this.uploadSyncStatus = this.application!.getStatusService().replaceStatusWithString(
|
||||
this.uploadSyncStatus,
|
||||
`Syncing ${stats.uploadCompletionCount}/${stats.uploadTotalCount} items...`
|
||||
);
|
||||
} else if (this.uploadSyncStatus) {
|
||||
this.uploadSyncStatus = this.application.getStatusService().removeStatus(
|
||||
this.uploadSyncStatus = this.application!.getStatusService().removeStatus(
|
||||
this.uploadSyncStatus
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
openModalComponent(component) {
|
||||
const scope = this.$rootScope.$new(true);
|
||||
openModalComponent(component: SNComponent) {
|
||||
const scope = this.$rootScope!.$new(true) as ModalComponentScope;
|
||||
scope.component = component;
|
||||
const el = this.$compile("<component-modal component='component' class='sk-modal'></component-modal>")(scope);
|
||||
const el = this.$compile!(
|
||||
"<component-modal component='component' class='sk-modal'></component-modal>"
|
||||
)(scope as any);
|
||||
angular.element(document.body).append(el);
|
||||
}
|
||||
|
||||
presentPermissionsDialog(dialog) {
|
||||
const scope = this.$rootScope.$new(true);
|
||||
presentPermissionsDialog(dialog: PermissionDialog) {
|
||||
const scope = this.$rootScope!.$new(true) as PermissionsModalScope;
|
||||
scope.permissionsString = dialog.permissionsString;
|
||||
scope.component = dialog.component;
|
||||
scope.callback = dialog.callback;
|
||||
const el = this.$compile("<permissions-modal component='component' permissions-string='permissionsString' callback='callback' class='sk-modal'></permissions-modal>")(scope);
|
||||
const el = this.$compile!(
|
||||
"<permissions-modal component='component' permissions-string='permissionsString'"
|
||||
+ " callback='callback' class='sk-modal'></permissions-modal>"
|
||||
)(scope as any);
|
||||
angular.element(document.body).append(el);
|
||||
}
|
||||
|
||||
overrideComponentManagerFunctions() {
|
||||
this.application.componentManager.openModalComponent = this.openModalComponent;
|
||||
this.application.componentManager.presentPermissionsDialog = this.presentPermissionsDialog;
|
||||
this.application!.componentManager!.openModalComponent = this.openModalComponent;
|
||||
this.application!.componentManager!.presentPermissionsDialog = this.presentPermissionsDialog;
|
||||
}
|
||||
|
||||
showInvalidSessionAlert() {
|
||||
/** Don't show repeatedly; at most 30 seconds in between */
|
||||
const SHOW_INTERVAL = 30;
|
||||
const lastShownSeconds = (new Date() - this.lastShownDate) / 1000;
|
||||
if (!this.lastShownDate || lastShownSeconds > SHOW_INTERVAL) {
|
||||
this.lastShownDate = new Date();
|
||||
if (
|
||||
!this.lastAlertShownDate ||
|
||||
(new Date().getTime() - this.lastAlertShownDate!.getTime()) / 1000 > SHOW_INTERVAL
|
||||
) {
|
||||
this.lastAlertShownDate = new Date();
|
||||
setTimeout(() => {
|
||||
this.application.alertService.alert({
|
||||
text: STRING_SESSION_EXPIRED
|
||||
});
|
||||
this.application!.alertService!.alert(
|
||||
STRING_SESSION_EXPIRED
|
||||
);
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
@@ -245,49 +263,49 @@ class ApplicationViewCtrl extends PureCtrl {
|
||||
window.addEventListener('drop', this.onDragDrop, true);
|
||||
}
|
||||
|
||||
onDragOver(event) {
|
||||
if (event.dataTransfer.files.length > 0) {
|
||||
onDragOver(event: DragEvent) {
|
||||
if (event.dataTransfer!.files.length > 0) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
onDragDrop(event) {
|
||||
if (event.dataTransfer.files.length > 0) {
|
||||
onDragDrop(event: DragEvent) {
|
||||
if (event.dataTransfer!.files.length > 0) {
|
||||
event.preventDefault();
|
||||
this.application.alertService.alert({
|
||||
text: STRING_DEFAULT_FILE_ERROR
|
||||
});
|
||||
this.application!.alertService!.alert(
|
||||
STRING_DEFAULT_FILE_ERROR
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async handleAutoSignInFromParams() {
|
||||
const params = this.$location.search();
|
||||
const params = this.$location!.search();
|
||||
const server = params.server;
|
||||
const email = params.email;
|
||||
const password = params.pw;
|
||||
if (!server || !email || !password) return;
|
||||
|
||||
const user = this.application.getUser();
|
||||
const user = this.application!.getUser();
|
||||
if (user) {
|
||||
if (user.email === email && await this.application.getHost() === server) {
|
||||
if (user.email === email && await this.application!.getHost() === server) {
|
||||
/** Already signed in, return */
|
||||
return;
|
||||
} else {
|
||||
/** Sign out */
|
||||
await this.application.signOut();
|
||||
await this.application.restart();
|
||||
await this.application!.signOut();
|
||||
}
|
||||
}
|
||||
await this.application.setHost(server);
|
||||
this.application.signIn({
|
||||
email: email,
|
||||
password: password,
|
||||
});
|
||||
await this.application!.setHost(server);
|
||||
this.application!.signIn(
|
||||
email,
|
||||
password,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class ApplicationView {
|
||||
export class ApplicationView extends WebDirective {
|
||||
constructor() {
|
||||
super();
|
||||
this.template = template;
|
||||
this.controller = ApplicationViewCtrl;
|
||||
this.replace = true;
|
||||
@@ -1,9 +1,14 @@
|
||||
import { FooterStatus, WebDirective } from './../types';
|
||||
import { dateToLocalizedString } from '@/utils';
|
||||
import {
|
||||
ApplicationEvent,
|
||||
TIMING_STRATEGY_FORCE_SPAWN_NEW,
|
||||
SyncQueueStrategy,
|
||||
ProtectedAction,
|
||||
ContentTypes
|
||||
ContentType,
|
||||
SNComponent,
|
||||
SNTheme,
|
||||
ComponentArea,
|
||||
ComponentAction
|
||||
} from 'snjs';
|
||||
import template from '%/footer.pug';
|
||||
import { AppStateEvent, EventSource } from '@/services/state';
|
||||
@@ -11,40 +16,71 @@ import {
|
||||
STRING_GENERIC_SYNC_ERROR,
|
||||
STRING_NEW_UPDATE_READY
|
||||
} from '@/strings';
|
||||
import { PureCtrl } from '@Controllers';
|
||||
import { PureCtrl } from '@Controllers/abstract/pure_ctrl';
|
||||
import { ComponentMutator } from '@/../../../../snjs/dist/@types/models';
|
||||
|
||||
type DockShortcut = {
|
||||
name: string,
|
||||
component: SNComponent,
|
||||
icon: {
|
||||
type: string
|
||||
background_color: string
|
||||
border_color: string
|
||||
}
|
||||
}
|
||||
|
||||
class FooterCtrl extends PureCtrl {
|
||||
|
||||
private $rootScope: ng.IRootScopeService
|
||||
private rooms: SNComponent[] = []
|
||||
private themesWithIcons: SNTheme[] = []
|
||||
private showSyncResolution = false
|
||||
private unregisterComponent: any
|
||||
private rootScopeListener1: any
|
||||
private rootScopeListener2: any
|
||||
public arbitraryStatusMessage?: string
|
||||
public user?: any
|
||||
private backupStatus?: FooterStatus
|
||||
private offline = true
|
||||
private showAccountMenu = false
|
||||
private queueExtReload = false
|
||||
private reloadInProgress = false
|
||||
public hasError = false
|
||||
public isRefreshing = false
|
||||
public lastSyncDate?: string
|
||||
public newUpdateAvailable = false
|
||||
public dockShortcuts: DockShortcut[] = []
|
||||
public roomShowState: Partial<Record<string, boolean>> = {}
|
||||
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
$rootScope,
|
||||
$timeout,
|
||||
$rootScope: ng.IRootScopeService,
|
||||
$timeout: ng.ITimeoutService,
|
||||
) {
|
||||
super($timeout);
|
||||
this.$rootScope = $rootScope;
|
||||
this.rooms = [];
|
||||
this.themesWithIcons = [];
|
||||
this.showSyncResolution = false;
|
||||
this.addRootScopeListeners();
|
||||
this.toggleSyncResolutionMenu = this.toggleSyncResolutionMenu.bind(this);
|
||||
this.closeAccountMenu = this.closeAccountMenu.bind(this);
|
||||
}
|
||||
|
||||
deinit() {
|
||||
this.rooms.length = 0;
|
||||
this.themesWithIcons.length = 0;
|
||||
this.unregisterComponent();
|
||||
this.unregisterComponent = null;
|
||||
this.unregisterComponent = undefined;
|
||||
this.rootScopeListener1();
|
||||
this.rootScopeListener2();
|
||||
this.rootScopeListener1 = null;
|
||||
this.rootScopeListener2 = null;
|
||||
this.closeAccountMenu = null;
|
||||
this.toggleSyncResolutionMenu = null;
|
||||
this.rootScopeListener1 = undefined;
|
||||
this.rootScopeListener2 = undefined;
|
||||
(this.closeAccountMenu as any) = undefined;
|
||||
(this.toggleSyncResolutionMenu as any) = undefined;
|
||||
super.deinit();
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
super.$onInit();
|
||||
this.application.getStatusService().addStatusObserver((string) => {
|
||||
this.application!.getStatusService().addStatusObserver((string: string) => {
|
||||
this.$timeout(() => {
|
||||
this.arbitraryStatusMessage = string;
|
||||
});
|
||||
@@ -58,18 +94,18 @@ class FooterCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
reloadUpgradeStatus() {
|
||||
this.application.checkForSecurityUpdate().then((available) => {
|
||||
this.application!.checkForSecurityUpdate().then((available) => {
|
||||
this.setState({
|
||||
dataUpgradeAvailable: available
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onAppLaunch() {
|
||||
async onAppLaunch() {
|
||||
super.onAppLaunch();
|
||||
this.reloadPasscodeStatus();
|
||||
this.reloadUpgradeStatus();
|
||||
this.user = this.application.getUser();
|
||||
this.user = this.application!.getUser();
|
||||
this.updateOfflineStatus();
|
||||
this.findErrors();
|
||||
this.streamItems();
|
||||
@@ -77,7 +113,7 @@ class FooterCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
async reloadPasscodeStatus() {
|
||||
const hasPasscode = this.application.hasPasscode();
|
||||
const hasPasscode = this.application!.hasPasscode();
|
||||
this.setState({
|
||||
hasPasscode: hasPasscode
|
||||
});
|
||||
@@ -95,30 +131,30 @@ class FooterCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
/** @override */
|
||||
onAppStateEvent(eventName, data) {
|
||||
onAppStateEvent(eventName: AppStateEvent, data: any) {
|
||||
if (eventName === AppStateEvent.EditorFocused) {
|
||||
if (data.eventSource === EventSource.UserInteraction) {
|
||||
this.closeAllRooms();
|
||||
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.backupStatus = this.application!.getStatusService().replaceStatusWithString(
|
||||
this.backupStatus!,
|
||||
"Successfully saved backup."
|
||||
);
|
||||
} else {
|
||||
this.backupStatus = this.application.getStatusService().replaceStatusWithString(
|
||||
this.backupStatus,
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -131,7 +167,7 @@ class FooterCtrl extends PureCtrl {
|
||||
|
||||
|
||||
/** @override */
|
||||
onAppEvent(eventName) {
|
||||
onAppEvent(eventName: ApplicationEvent) {
|
||||
if (eventName === ApplicationEvent.KeyStatusChanged) {
|
||||
this.reloadUpgradeStatus();
|
||||
} else if (eventName === ApplicationEvent.EnteredOutOfSync) {
|
||||
@@ -143,7 +179,7 @@ class FooterCtrl extends PureCtrl {
|
||||
outOfSync: false
|
||||
});
|
||||
} else if (eventName === ApplicationEvent.CompletedSync) {
|
||||
if (this.offline && this.application.getNoteCount() === 0) {
|
||||
if (this.offline && this.application!.getNoteCount() === 0) {
|
||||
this.showAccountMenu = true;
|
||||
}
|
||||
this.syncUpdated();
|
||||
@@ -156,52 +192,53 @@ class FooterCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
streamItems() {
|
||||
this.application.streamItems({
|
||||
contentType: ContentType.Component,
|
||||
stream: async () => {
|
||||
this.rooms = this.application.getItems({
|
||||
contentType: ContentType.Component
|
||||
}).filter((candidate) => {
|
||||
return candidate.area === 'rooms' && !candidate.deleted;
|
||||
this.application!.streamItems(
|
||||
ContentType.Component,
|
||||
async () => {
|
||||
const components = this.application!.getItems(ContentType.Component) as SNComponent[];
|
||||
this.rooms = components.filter((candidate) => {
|
||||
return candidate.area === ComponentArea.Rooms && !candidate.deleted;
|
||||
});
|
||||
if (this.queueExtReload) {
|
||||
this.queueExtReload = false;
|
||||
this.reloadExtendedData();
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
this.application.streamItems({
|
||||
contentType: 'SN|Theme',
|
||||
stream: async () => {
|
||||
const themes = this.application.getDisplayableItems({
|
||||
contentType: ContentType.Theme
|
||||
}).filter((candidate) => {
|
||||
this.application!.streamItems(
|
||||
ContentType.Theme,
|
||||
async () => {
|
||||
const themes = this.application!.getDisplayableItems(ContentType.Theme) as SNTheme[];
|
||||
const filteredThemes = themes.filter((candidate) => {
|
||||
return (
|
||||
!candidate.deleted &&
|
||||
candidate.content.package_info &&
|
||||
candidate.content.package_info.dock_icon
|
||||
candidate.package_info &&
|
||||
candidate.package_info.dock_icon
|
||||
);
|
||||
}).sort((a, b) => {
|
||||
return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1;
|
||||
});
|
||||
const differ = themes.length !== this.themesWithIcons.length;
|
||||
this.themesWithIcons = themes;
|
||||
const differ = filteredThemes.length !== this.themesWithIcons.length;
|
||||
this.themesWithIcons = filteredThemes;
|
||||
if (differ) {
|
||||
this.reloadDockShortcuts();
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
registerComponentHandler() {
|
||||
this.unregisterComponent = this.application.componentManager.registerHandler({
|
||||
identifier: "roomBar",
|
||||
areas: ["rooms", "modal"],
|
||||
activationHandler: (component) => { },
|
||||
this.unregisterComponent = this.application!.componentManager!.registerHandler({
|
||||
identifier: 'room-bar',
|
||||
areas: [ComponentArea.Rooms, ComponentArea.Modal],
|
||||
activationHandler: () => { },
|
||||
actionHandler: (component, action, data) => {
|
||||
if (action === "set-size") {
|
||||
component.setLastSize(data);
|
||||
if (action === ComponentAction.SetSize) {
|
||||
this.application!.changeItem(component.uuid, (m) => {
|
||||
const mutator = m as ComponentMutator;
|
||||
mutator.setLastSize(data);
|
||||
})
|
||||
}
|
||||
},
|
||||
focusHandler: (component, focused) => {
|
||||
@@ -224,7 +261,8 @@ class FooterCtrl extends PureCtrl {
|
||||
* then closing it after a short delay.
|
||||
*/
|
||||
const extWindow = this.rooms.find((room) => {
|
||||
return room.package_info.identifier === this.application.getNativeExtService().extManagerId;
|
||||
return room.package_info.identifier === this.application!
|
||||
.getNativeExtService().extManagerId;
|
||||
});
|
||||
if (!extWindow) {
|
||||
this.queueExtReload = true;
|
||||
@@ -240,15 +278,15 @@ class FooterCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
updateOfflineStatus() {
|
||||
this.offline = this.application.noAccount();
|
||||
this.offline = this.application!.noAccount();
|
||||
}
|
||||
|
||||
openSecurityUpdate() {
|
||||
this.application.performProtocolUpgrade();
|
||||
this.application!.performProtocolUpgrade();
|
||||
}
|
||||
|
||||
findErrors() {
|
||||
this.error = this.application.getSyncStatus().error;
|
||||
this.hasError = this.application!.getSyncStatus().hasError();
|
||||
}
|
||||
|
||||
accountMenuPressed() {
|
||||
@@ -256,31 +294,31 @@ class FooterCtrl extends PureCtrl {
|
||||
this.closeAllRooms();
|
||||
}
|
||||
|
||||
toggleSyncResolutionMenu = () => {
|
||||
toggleSyncResolutionMenu() {
|
||||
this.showSyncResolution = !this.showSyncResolution;
|
||||
}
|
||||
|
||||
closeAccountMenu = () => {
|
||||
closeAccountMenu() {
|
||||
this.showAccountMenu = false;
|
||||
}
|
||||
|
||||
lockApp() {
|
||||
this.application.lock();
|
||||
this.application!.lock();
|
||||
}
|
||||
|
||||
refreshData() {
|
||||
this.isRefreshing = true;
|
||||
this.application.sync({
|
||||
timingStrategy: TIMING_STRATEGY_FORCE_SPAWN_NEW,
|
||||
this.application!.sync({
|
||||
queueStrategy: SyncQueueStrategy.ForceSpawnNew,
|
||||
checkIntegrity: true
|
||||
}).then((response) => {
|
||||
this.$timeout(() => {
|
||||
this.isRefreshing = false;
|
||||
}, 200);
|
||||
if (response && response.error) {
|
||||
this.application.alertService.alert({
|
||||
text: STRING_GENERIC_SYNC_ERROR
|
||||
});
|
||||
this.application!.alertService!.alert(
|
||||
STRING_GENERIC_SYNC_ERROR
|
||||
);
|
||||
} else {
|
||||
this.syncUpdated();
|
||||
}
|
||||
@@ -288,7 +326,7 @@ class FooterCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
syncUpdated() {
|
||||
this.lastSyncDate = dateToLocalizedString(this.application.getLastSyncDate());
|
||||
this.lastSyncDate = dateToLocalizedString(this.application!.getLastSyncDate()!);
|
||||
}
|
||||
|
||||
onNewUpdateAvailable() {
|
||||
@@ -297,16 +335,16 @@ class FooterCtrl extends PureCtrl {
|
||||
|
||||
clickedNewUpdateAnnouncement() {
|
||||
this.newUpdateAvailable = false;
|
||||
this.application.alertService.alert({
|
||||
text: STRING_NEW_UPDATE_READY
|
||||
});
|
||||
this.application!.alertService!.alert(
|
||||
STRING_NEW_UPDATE_READY
|
||||
);
|
||||
}
|
||||
|
||||
reloadDockShortcuts() {
|
||||
const shortcuts = [];
|
||||
for (const theme of this.themesWithIcons) {
|
||||
const name = theme.content.package_info.name;
|
||||
const icon = theme.content.package_info.dock_icon;
|
||||
const name = theme.package_info.name;
|
||||
const icon = theme.package_info.dock_icon;
|
||||
if (!icon) {
|
||||
continue;
|
||||
}
|
||||
@@ -314,7 +352,7 @@ class FooterCtrl extends PureCtrl {
|
||||
name: name,
|
||||
component: theme,
|
||||
icon: icon
|
||||
});
|
||||
} as DockShortcut);
|
||||
}
|
||||
|
||||
this.dockShortcuts = shortcuts.sort((a, b) => {
|
||||
@@ -327,46 +365,49 @@ class FooterCtrl extends PureCtrl {
|
||||
return -1;
|
||||
} else if (bType === 'circle' && aType === 'svg') {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initSvgForShortcut(shortcut) {
|
||||
initSvgForShortcut(shortcut: DockShortcut) {
|
||||
const id = 'dock-svg-' + shortcut.component.uuid;
|
||||
const element = document.getElementById(id);
|
||||
const element = document.getElementById(id)!;
|
||||
const parser = new DOMParser();
|
||||
const svg = shortcut.component.content.package_info.dock_icon.source;
|
||||
const svg = shortcut.component.package_info.dock_icon.source;
|
||||
const doc = parser.parseFromString(svg, 'image/svg+xml');
|
||||
element.appendChild(doc.documentElement);
|
||||
}
|
||||
|
||||
selectShortcut(shortcut) {
|
||||
this.application.componentManager.toggleComponent(shortcut.component);
|
||||
selectShortcut(shortcut: DockShortcut) {
|
||||
this.application!.componentManager!.toggleComponent(shortcut.component);
|
||||
}
|
||||
|
||||
onRoomDismiss(room) {
|
||||
room.showRoom = false;
|
||||
onRoomDismiss(room: SNComponent) {
|
||||
this.roomShowState[room.uuid] = false;
|
||||
}
|
||||
|
||||
closeAllRooms() {
|
||||
for (const room of this.rooms) {
|
||||
room.showRoom = false;
|
||||
this.roomShowState[room.uuid] = false;
|
||||
}
|
||||
}
|
||||
|
||||
async selectRoom(room) {
|
||||
async selectRoom(room: SNComponent) {
|
||||
const run = () => {
|
||||
this.$timeout(() => {
|
||||
room.showRoom = !room.showRoom;
|
||||
this.roomShowState[room.uuid] = !this.roomShowState[room.uuid];
|
||||
});
|
||||
};
|
||||
|
||||
if (!room.showRoom) {
|
||||
const requiresPrivilege = await this.application.privilegesService.actionRequiresPrivilege(
|
||||
ProtectedAction.ManageExtensions
|
||||
);
|
||||
if (!this.roomShowState[room.uuid]) {
|
||||
const requiresPrivilege = await this.application!.privilegesService!
|
||||
.actionRequiresPrivilege(
|
||||
ProtectedAction.ManageExtensions
|
||||
);
|
||||
if (requiresPrivilege) {
|
||||
this.application.presentPrivilegesModal(
|
||||
this.application!.presentPrivilegesModal(
|
||||
ProtectedAction.ManageExtensions,
|
||||
run
|
||||
);
|
||||
@@ -379,15 +420,16 @@ class FooterCtrl extends PureCtrl {
|
||||
}
|
||||
|
||||
clickOutsideAccountMenu() {
|
||||
if (this.application && this.application.authenticationInProgress()) {
|
||||
if (this.application && this.application!.authenticationInProgress()) {
|
||||
return;
|
||||
}
|
||||
this.showAccountMenu = false;
|
||||
}
|
||||
}
|
||||
|
||||
export class Footer {
|
||||
export class Footer extends WebDirective {
|
||||
constructor() {
|
||||
super();
|
||||
this.restrict = 'E';
|
||||
this.template = template;
|
||||
this.controller = FooterCtrl;
|
||||
@@ -1,78 +0,0 @@
|
||||
import template from '%/lock-screen.pug';
|
||||
import { AppStateEvent } from '@/services/state';
|
||||
import { PureCtrl } from './abstract/pure_ctrl';
|
||||
|
||||
const ELEMENT_ID_PASSCODE_INPUT = 'passcode-input';
|
||||
|
||||
class LockScreenCtrl extends PureCtrl {
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
$timeout,
|
||||
) {
|
||||
super($timeout);
|
||||
this.formData = {};
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
super.$onInit();
|
||||
this.puppet.focusInput = () => {
|
||||
this.passcodeInput.focus();
|
||||
};
|
||||
}
|
||||
|
||||
get passcodeInput() {
|
||||
return document.getElementById(
|
||||
ELEMENT_ID_PASSCODE_INPUT
|
||||
);
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async onAppStateEvent(eventName, data) {
|
||||
if (eventName === AppStateEvent.WindowDidFocus) {
|
||||
const input = this.passcodeInput;
|
||||
if (input) {
|
||||
input.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async submitPasscodeForm() {
|
||||
if (
|
||||
!this.formData.passcode ||
|
||||
this.formData.passcode.length === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.passcodeInput.blur();
|
||||
this.onValue()(this.formData.passcode);
|
||||
}
|
||||
|
||||
forgotPasscode() {
|
||||
this.formData.showRecovery = true;
|
||||
}
|
||||
|
||||
beginDeleteData() {
|
||||
this.application.alertService.confirm({
|
||||
text: "Are you sure you want to clear all local data?",
|
||||
destructive: true,
|
||||
onConfirm: async () => {
|
||||
await this.application.signOut();
|
||||
await this.application.restart();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class LockScreen {
|
||||
constructor() {
|
||||
this.restrict = 'E';
|
||||
this.template = template;
|
||||
this.controller = LockScreenCtrl;
|
||||
this.controllerAs = 'ctrl';
|
||||
this.bindToController = true;
|
||||
this.scope = {
|
||||
onValue: '&',
|
||||
puppet: '='
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,16 @@
|
||||
import { ApplicationManager } from './../applicationManager';
|
||||
import { WebDirective } from './../types';
|
||||
import template from '%/root.pug';
|
||||
import { WebApplication } from '@/application';
|
||||
|
||||
class RootCtrl {
|
||||
|
||||
private $timeout: ng.ITimeoutService
|
||||
private applicationManager: ApplicationManager
|
||||
public applications: WebApplication[] = []
|
||||
|
||||
/* @ngInject */
|
||||
constructor($timeout, applicationManager) {
|
||||
constructor($timeout: ng.ITimeoutService, applicationManager: ApplicationManager) {
|
||||
this.$timeout = $timeout;
|
||||
this.applicationManager = applicationManager;
|
||||
this.applicationManager.addApplicationChangeObserver(() => {
|
||||
@@ -17,8 +25,9 @@ class RootCtrl {
|
||||
}
|
||||
}
|
||||
|
||||
export class Root {
|
||||
export class Root extends WebDirective {
|
||||
constructor() {
|
||||
super();
|
||||
this.template = template;
|
||||
this.controller = RootCtrl;
|
||||
this.replace = true;
|
||||
@@ -1,5 +1,6 @@
|
||||
import { WebApplication } from './application';
|
||||
import { SNAlertService } from "../../../../snjs/dist/@types";
|
||||
import { RawPayload } from '../../../../snjs/dist/@types/protocol/payloads/generator';
|
||||
|
||||
const DB_NAME = 'standardnotes';
|
||||
const STORE_NAME = 'items';
|
||||
@@ -105,7 +106,7 @@ export class Database {
|
||||
});
|
||||
}
|
||||
|
||||
public async getAllPayloads() {
|
||||
public async getAllPayloads(): Promise<any[]> {
|
||||
const db = (await this.openDatabase())!;
|
||||
return new Promise((resolve) => {
|
||||
const objectStore =
|
||||
@@ -126,11 +127,11 @@ export class Database {
|
||||
});
|
||||
}
|
||||
|
||||
public async savePayload(payload: any) {
|
||||
public async savePayload(payload: any): Promise<void> {
|
||||
return this.savePayloads([payload]);
|
||||
}
|
||||
|
||||
public async savePayloads(payloads: any[]) {
|
||||
public async savePayloads(payloads: any[]): Promise<void> {
|
||||
if (payloads.length === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -157,8 +158,8 @@ export class Database {
|
||||
});
|
||||
}
|
||||
|
||||
private putItems(objectStore: IDBObjectStore, items: any[]) {
|
||||
return Promise.all(items.map((item) => {
|
||||
private async putItems(objectStore: IDBObjectStore, items: any[]): Promise<void> {
|
||||
await Promise.all(items.map((item) => {
|
||||
return new Promise((resolve) => {
|
||||
const request = objectStore.put(item);
|
||||
request.onerror = resolve;
|
||||
@@ -167,19 +168,21 @@ export class Database {
|
||||
}));
|
||||
}
|
||||
|
||||
public async deletePayload(uuid: string) {
|
||||
public async deletePayload(uuid: string): Promise<void> {
|
||||
const db = (await this.openDatabase())!;
|
||||
return new Promise((resolve, reject) => {
|
||||
const request =
|
||||
db.transaction(STORE_NAME, READ_WRITE)
|
||||
.objectStore(STORE_NAME)
|
||||
.delete(uuid);
|
||||
request.onsuccess = resolve;
|
||||
request.onsuccess = () => {
|
||||
resolve();
|
||||
};
|
||||
request.onerror = reject;
|
||||
});
|
||||
}
|
||||
|
||||
public async clearAllPayloads() {
|
||||
public async clearAllPayloads(): Promise<void> {
|
||||
const deleteRequest = window.indexedDB.deleteDatabase(DB_NAME);
|
||||
return new Promise((resolve, reject) => {
|
||||
deleteRequest.onerror = () => {
|
||||
@@ -200,7 +203,7 @@ export class Database {
|
||||
this.alertService!.alert(message);
|
||||
}
|
||||
|
||||
private showGenericError(error: {code: number, name: string}) {
|
||||
private showGenericError(error: { code: number, name: string }) {
|
||||
const message =
|
||||
`Unable to save changes locally due to an unknown system issue. ` +
|
||||
`Issue Code: ${error.code} Issue Name: ${error.name}.`;
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
export class StatusManager {
|
||||
constructor() {
|
||||
this.statuses = [];
|
||||
this.observers = [];
|
||||
}
|
||||
|
||||
statusFromString(string) {
|
||||
return {string: string};
|
||||
}
|
||||
|
||||
replaceStatusWithString(status, string) {
|
||||
this.removeStatus(status);
|
||||
return this.addStatusFromString(string);
|
||||
}
|
||||
|
||||
addStatusFromString(string) {
|
||||
return this.addStatus(this.statusFromString(string));
|
||||
}
|
||||
|
||||
addStatus(status) {
|
||||
if(typeof status !== "object") {
|
||||
console.error("Attempting to set non-object status", status);
|
||||
return;
|
||||
}
|
||||
|
||||
this.statuses.push(status);
|
||||
this.notifyObservers();
|
||||
return status;
|
||||
}
|
||||
|
||||
removeStatus(status) {
|
||||
_.pull(this.statuses, status);
|
||||
this.notifyObservers();
|
||||
return null;
|
||||
}
|
||||
|
||||
getStatusString() {
|
||||
let result = "";
|
||||
this.statuses.forEach((status, index) => {
|
||||
if(index > 0) {
|
||||
result += " ";
|
||||
}
|
||||
result += status.string;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
notifyObservers() {
|
||||
for(const observer of this.observers) {
|
||||
observer(this.getStatusString());
|
||||
}
|
||||
}
|
||||
|
||||
addStatusObserver(callback) {
|
||||
this.observers.push(callback);
|
||||
}
|
||||
|
||||
removeStatusObserver(callback) {
|
||||
_.pull(this.statuses, callback);
|
||||
}
|
||||
}
|
||||
60
app/assets/javascripts/services/statusManager.ts
Normal file
60
app/assets/javascripts/services/statusManager.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { removeFromArray } from 'snjs';
|
||||
import { FooterStatus } from './../types';
|
||||
|
||||
type StatusCallback = (string: string) => void
|
||||
|
||||
export class StatusManager {
|
||||
|
||||
private statuses: FooterStatus[] = []
|
||||
private observers: StatusCallback[] = []
|
||||
|
||||
statusFromString(string: string) {
|
||||
return {string: string};
|
||||
}
|
||||
|
||||
replaceStatusWithString(status: FooterStatus, string: string) {
|
||||
this.removeStatus(status);
|
||||
return this.addStatusFromString(string);
|
||||
}
|
||||
|
||||
addStatusFromString(string: string) {
|
||||
return this.addStatus(this.statusFromString(string));
|
||||
}
|
||||
|
||||
addStatus(status: FooterStatus) {
|
||||
this.statuses.push(status);
|
||||
this.notifyObservers();
|
||||
return status;
|
||||
}
|
||||
|
||||
removeStatus(status: FooterStatus) {
|
||||
removeFromArray(this.statuses, status);
|
||||
this.notifyObservers();
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getStatusString() {
|
||||
let result = '';
|
||||
this.statuses.forEach((status, index) => {
|
||||
if(index > 0) {
|
||||
result += ' ';
|
||||
}
|
||||
result += status.string;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
notifyObservers() {
|
||||
for(const observer of this.observers) {
|
||||
observer(this.getStatusString());
|
||||
}
|
||||
}
|
||||
|
||||
addStatusObserver(callback: StatusCallback) {
|
||||
this.observers.push(callback);
|
||||
return () => {
|
||||
removeFromArray(this.observers, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { SNComponent } from 'snjs';
|
||||
export class WebDirective implements ng.IDirective {
|
||||
controller?: string | ng.Injectable<ng.IControllerConstructor>;
|
||||
controllerAs?: string;
|
||||
@@ -18,6 +19,17 @@ export interface PasswordWizardScope extends Partial<ng.IScope> {
|
||||
application: any
|
||||
}
|
||||
|
||||
export interface PermissionsModalScope extends Partial<ng.IScope> {
|
||||
application: any
|
||||
component: SNComponent
|
||||
permissionsString: string
|
||||
callback: (approved: boolean) => void
|
||||
}
|
||||
|
||||
export interface ModalComponentScope extends Partial<ng.IScope> {
|
||||
component: SNComponent
|
||||
}
|
||||
|
||||
export type PanelPuppet = {
|
||||
onReady?: () => void
|
||||
ready?: boolean
|
||||
@@ -25,4 +37,8 @@ export type PanelPuppet = {
|
||||
setLeft?: (left: number) => void
|
||||
isCollapsed?: () => boolean
|
||||
flash?: () => void
|
||||
}
|
||||
|
||||
export type FooterStatus = {
|
||||
string: string
|
||||
}
|
||||
@@ -8,10 +8,10 @@
|
||||
)
|
||||
.sk-app-bar-item-column
|
||||
.sk-circle.small(
|
||||
ng-class="ctrl.error ? 'danger' : (ctrl.user ? 'info' : 'neutral')"
|
||||
ng-class="ctrl.hasError ? 'danger' : (ctrl.user ? 'info' : 'neutral')"
|
||||
)
|
||||
.sk-app-bar-item-column
|
||||
.sk-label.title(ng-class='{red: ctrl.error}') Account
|
||||
.sk-label.title(ng-class='{red: ctrl.hasError}') Account
|
||||
account-menu(
|
||||
close-function='ctrl.closeAccountMenu()',
|
||||
ng-click='$event.stopPropagation()',
|
||||
@@ -31,7 +31,7 @@
|
||||
.sk-label {{room.name}}
|
||||
component-modal(
|
||||
component='room',
|
||||
ng-if='room.showRoom',
|
||||
ng-if='ctrl.roomShowState[room.uuid]',
|
||||
on-dismiss='ctrl.onRoomDismiss()',
|
||||
application='ctrl.application'
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user