feat: SNJS preferences

This commit is contained in:
Baptiste Grob
2020-12-10 14:58:00 +01:00
parent 993d241c72
commit aca5ad0dfa
9 changed files with 130 additions and 263 deletions

View File

@@ -4,6 +4,5 @@ export { DesktopManager } from './desktopManager';
export { KeyboardManager } from './keyboardManager';
export { AutolockService } from './autolock_service';
export { NativeExtManager } from './nativeExtManager';
export { PreferencesManager } from './preferencesManager';
export { StatusManager } from './statusManager';
export { ThemeManager } from './themeManager';

View File

@@ -1,99 +0,0 @@
import { WebApplication } from '@/ui_models/application';
import {
SNPredicate,
ContentType,
ApplicationService,
SNUserPrefs,
WebPrefKey,
UserPrefsMutator,
FillItemContent,
ApplicationEvent,
} from '@standardnotes/snjs';
export class PreferencesManager extends ApplicationService {
private userPreferences!: SNUserPrefs;
private loadingPrefs = false;
private unubscribeStreamItems?: () => void;
private needsSingletonReload = true;
/** @override */
async onAppLaunch() {
super.onAppLaunch();
this.reloadSingleton();
this.streamPreferences();
}
async onAppEvent(event: ApplicationEvent) {
super.onAppEvent(event);
if (event === ApplicationEvent.CompletedFullSync) {
this.reloadSingleton();
}
}
deinit() {
this.unubscribeStreamItems?.();
}
get webApplication() {
return this.application as WebApplication;
}
streamPreferences() {
this.unubscribeStreamItems = this.application!.streamItems(
ContentType.UserPrefs,
() => {
this.needsSingletonReload = true;
}
);
}
private async reloadSingleton() {
if (this.loadingPrefs || !this.needsSingletonReload) {
return;
}
this.loadingPrefs = true;
const contentType = ContentType.UserPrefs;
const predicate = new SNPredicate('content_type', '=', contentType);
const previousRef = this.userPreferences;
this.userPreferences = (await this.application!.singletonManager!.findOrCreateSingleton(
predicate,
contentType,
FillItemContent({})
)) as SNUserPrefs;
this.loadingPrefs = false;
this.needsSingletonReload = false;
if (
previousRef?.uuid !== this.userPreferences.uuid ||
this.userPreferences.lastSyncBegan?.getTime() !==
previousRef?.lastSyncBegan?.getTime()
) {
this.webApplication
.getAppState()
.setUserPreferences(this.userPreferences);
}
}
syncUserPreferences() {
if (this.userPreferences) {
this.application!.saveItem(this.userPreferences.uuid);
}
}
getValue(key: WebPrefKey, defaultValue?: any) {
if (!this.userPreferences) {
return defaultValue;
}
const value = this.userPreferences.getPref(key);
return value !== undefined && value !== null ? value : defaultValue;
}
async setUserPrefValue(key: WebPrefKey, value: any, sync = false) {
await this.application!.changeItem(this.userPreferences.uuid, (m) => {
const mutator = m as UserPrefsMutator;
mutator.setWebPref(key, value);
});
if (sync) {
this.syncUserPreferences();
}
}
}

View File

@@ -19,20 +19,19 @@ import { action, makeObservable, observable } from 'mobx';
import { Bridge } from '@/services/bridge';
export enum AppStateEvent {
TagChanged = 1,
ActiveEditorChanged = 2,
PreferencesChanged = 3,
PanelResized = 4,
EditorFocused = 5,
BeganBackupDownload = 6,
EndedBackupDownload = 7,
WindowDidFocus = 9,
WindowDidBlur = 10,
TagChanged,
ActiveEditorChanged,
PanelResized,
EditorFocused,
BeganBackupDownload,
EndedBackupDownload,
WindowDidFocus,
WindowDidBlur,
};
export enum EventSource {
UserInteraction = 1,
Script = 2
UserInteraction,
Script,
};
type ObserverCallback = (event: AppStateEvent, data?: any) => Promise<void>
@@ -103,7 +102,6 @@ export class AppState {
rootScopeCleanup2: any;
onVisibilityChange: any;
selectedTag?: SNTag;
userPreferences?: SNUserPrefs;
multiEditorEnabled = false;
showBetaWarning = false;
readonly actionsMenu = new ActionsMenuState();
@@ -379,13 +377,6 @@ export class AppState {
return this.selectedTag;
}
setUserPreferences(preferences: SNUserPrefs) {
this.userPreferences = preferences;
this.notifyEvent(
AppStateEvent.PreferencesChanged
);
}
panelDidResize(name: string, collapsed: boolean) {
this.notifyEvent(
AppStateEvent.PanelResized,

View File

@@ -22,7 +22,6 @@ import {
NativeExtManager,
StatusManager,
ThemeManager,
PreferencesManager,
KeyboardManager
} from '@/services';
import { AppState } from '@/ui_models/app_state';
@@ -38,7 +37,6 @@ type WebServices = {
nativeExtService: NativeExtManager
statusManager: StatusManager
themeService: ThemeManager
prefsService: PreferencesManager
keyboardService: KeyboardManager
}
@@ -141,10 +139,6 @@ export class WebApplication extends SNApplication {
return this.webServices.themeService;
}
public getPrefsService() {
return this.webServices.prefsService;
}
public getKeyboardService() {
return this.webServices.keyboardService;
}

View File

@@ -7,7 +7,6 @@ import {
KeyboardManager,
AutolockService,
NativeExtManager,
PreferencesManager,
StatusManager,
ThemeManager
} from '@/services';
@@ -82,9 +81,6 @@ export class ApplicationGroup extends SNApplicationGroup {
const nativeExtService = new NativeExtManager(
application
);
const prefsService = new PreferencesManager(
application
);
const statusService = new StatusManager();
const themeService = new ThemeManager(
application,
@@ -96,7 +92,6 @@ export class ApplicationGroup extends SNApplicationGroup {
keyboardService,
autolockService,
nativeExtService,
prefsService,
statusManager: statusService,
themeService
});

View File

@@ -16,7 +16,7 @@ import {
Uuids,
ComponentArea,
ComponentAction,
WebPrefKey,
PrefKey,
ComponentMutator,
} from '@standardnotes/snjs';
import find from 'lodash/find';
@@ -135,9 +135,9 @@ class EditorViewCtrl extends PureViewCtrl<{}, EditorState> {
onReady: () => this.reloadPreferences()
};
/** Used by .pug template */
this.prefKeyMonospace = WebPrefKey.EditorMonospaceEnabled;
this.prefKeySpellcheck = WebPrefKey.EditorSpellcheck;
this.prefKeyMarginResizers = WebPrefKey.EditorResizersEnabled;
this.prefKeyMonospace = PrefKey.EditorMonospaceEnabled;
this.prefKeySpellcheck = PrefKey.EditorSpellcheck;
this.prefKeyMarginResizers = PrefKey.EditorResizersEnabled;
this.editorMenuOnSelect = this.editorMenuOnSelect.bind(this);
this.onPanelResizeFinish = this.onPanelResizeFinish.bind(this);
@@ -239,38 +239,39 @@ class EditorViewCtrl extends PureViewCtrl<{}, EditorState> {
this.registerComponentHandler();
}
/** @override */
onAppStateEvent(eventName: AppStateEvent, data: any) {
if (eventName === AppStateEvent.PreferencesChanged) {
this.reloadPreferences();
}
}
/** @override */
onAppEvent(eventName: ApplicationEvent) {
if (eventName === ApplicationEvent.HighLatencySync) {
this.setState({ syncTakingTooLong: true });
} else if (eventName === ApplicationEvent.CompletedFullSync) {
this.setState({ syncTakingTooLong: false });
const isInErrorState = this.state.saveError;
/** if we're still dirty, don't change status, a sync is likely upcoming. */
if (!this.note.dirty && isInErrorState) {
this.showAllChangesSavedStatus();
}
} else if (eventName === ApplicationEvent.FailedSync) {
/**
* Only show error status in editor if the note is dirty.
* Otherwise, it means the originating sync came from somewhere else
* and we don't want to display an error here.
*/
if (this.note.dirty) {
this.showErrorStatus();
}
} else if (eventName === ApplicationEvent.LocalDatabaseWriteError) {
this.showErrorStatus({
message: "Offline Saving Issue",
desc: "Changes not saved"
});
switch (eventName) {
case ApplicationEvent.PreferencesChanged:
this.reloadPreferences();
break;
case ApplicationEvent.HighLatencySync:
this.setState({ syncTakingTooLong: true });
break;
case ApplicationEvent.CompletedFullSync:
this.setState({ syncTakingTooLong: false });
const isInErrorState = this.state.saveError;
/** if we're still dirty, don't change status, a sync is likely upcoming. */
if (!this.note.dirty && isInErrorState) {
this.showAllChangesSavedStatus();
}
break;
case ApplicationEvent.FailedSync:
/**
* Only show error status in editor if the note is dirty.
* Otherwise, it means the originating sync came from somewhere else
* and we don't want to display an error here.
*/
if (this.note.dirty) {
this.showErrorStatus();
}
break;
case ApplicationEvent.LocalDatabaseWriteError:
this.showErrorStatus({
message: "Offline Saving Issue",
desc: "Changes not saved"
});
break;
}
}
@@ -891,40 +892,40 @@ class EditorViewCtrl extends PureViewCtrl<{}, EditorState> {
async onPanelResizeFinish(width: number, left: number, isMaxWidth: boolean) {
if (isMaxWidth) {
await this.application.getPrefsService().setUserPrefValue(
WebPrefKey.EditorWidth,
await this.application.setPreference(
PrefKey.EditorWidth,
null
);
} else {
if (width !== undefined && width !== null) {
await this.application.getPrefsService().setUserPrefValue(
WebPrefKey.EditorWidth,
await this.application.setPreference(
PrefKey.EditorWidth,
width
);
this.leftPanelPuppet!.setWidth!(width);
}
}
if (left !== undefined && left !== null) {
await this.application.getPrefsService().setUserPrefValue(
WebPrefKey.EditorLeft,
await this.application.setPreference(
PrefKey.EditorLeft,
left
);
this.rightPanelPuppet!.setLeft!(left);
}
this.application.getPrefsService().syncUserPreferences();
this.application.sync();
}
async reloadPreferences() {
const monospaceFont = this.application.getPrefsService().getValue(
WebPrefKey.EditorMonospaceEnabled,
const monospaceFont = this.application.getPreference(
PrefKey.EditorMonospaceEnabled,
true
);
const spellcheck = this.application.getPrefsService().getValue(
WebPrefKey.EditorSpellcheck,
const spellcheck = this.application.getPreference(
PrefKey.EditorSpellcheck,
true
);
const marginResizersEnabled = this.application.getPrefsService().getValue(
WebPrefKey.EditorResizersEnabled,
const marginResizersEnabled = this.application.getPreference(
PrefKey.EditorResizersEnabled,
true
);
await this.setState({
@@ -945,16 +946,16 @@ class EditorViewCtrl extends PureViewCtrl<{}, EditorState> {
this.leftPanelPuppet?.ready &&
this.rightPanelPuppet?.ready
) {
const width = this.application.getPrefsService().getValue(
WebPrefKey.EditorWidth,
const width = this.application.getPreference(
PrefKey.EditorWidth,
null
);
if (width != null) {
this.leftPanelPuppet!.setWidth!(width);
this.rightPanelPuppet!.setWidth!(width);
}
const left = this.application.getPrefsService().getValue(
WebPrefKey.EditorLeft,
const left = this.application.getPreference(
PrefKey.EditorLeft,
null
);
if (left != null) {
@@ -982,24 +983,23 @@ class EditorViewCtrl extends PureViewCtrl<{}, EditorState> {
}
}
async toggleWebPrefKey(key: WebPrefKey) {
async toggleWebPrefKey(key: PrefKey) {
const currentValue = (this.state as any)[key];
await this.application.getPrefsService().setUserPrefValue(
await this.application.setPreference(
key,
!currentValue,
true
);
await this.setState({
[key]: !currentValue
})
this.reloadFont();
if (key === WebPrefKey.EditorSpellcheck) {
if (key === PrefKey.EditorSpellcheck) {
/** Allows textarea to reload */
await this.setState({ textareaUnloading: true });
await this.setState({ textareaUnloading: false });
this.reloadFont();
} else if (key === WebPrefKey.EditorResizersEnabled && this.state[key] === true) {
} else if (key === PrefKey.EditorResizersEnabled && this.state[key] === true) {
this.$timeout(() => {
this.leftPanelPuppet!.flash!();
this.rightPanelPuppet!.flash!();

View File

@@ -1,16 +1,5 @@
import { SNNote } from '@standardnotes/snjs';
export enum NoteSortKey {
CreatedAt = 'created_at',
UserUpdatedAt = 'userModifiedDate',
Title = 'title',
/** @legacy Use UserUpdatedAt instead */
UpdatedAt = 'updated_at',
/** @legacy Use UserUpdatedAt instead */
ClientUpdatedAt = 'client_updated_at',
}
export function notePassesFilter(
note: SNNote,
showArchived: boolean,

View File

@@ -6,7 +6,7 @@ import {
removeFromArray,
SNNote,
SNTag,
WebPrefKey,
PrefKey,
findInArray,
CollectionSort,
} from '@standardnotes/snjs';
@@ -17,7 +17,6 @@ import {
PANEL_NAME_NOTES
} from '@/views/constants';
import {
NoteSortKey,
notePassesFilter
} from './note_utils';
import { UuidString } from '@standardnotes/snjs';
@@ -37,13 +36,13 @@ type NotesState = {
noteFilter: { text: string }
mutable: { showMenu: boolean }
completedFullSync: boolean
[WebPrefKey.TagsPanelWidth]?: number
[WebPrefKey.NotesPanelWidth]?: number
[WebPrefKey.EditorWidth]?: number
[WebPrefKey.EditorLeft]?: number
[WebPrefKey.EditorMonospaceEnabled]?: boolean
[WebPrefKey.EditorSpellcheck]?: boolean
[WebPrefKey.EditorResizersEnabled]?: boolean
[PrefKey.TagsPanelWidth]?: number
[PrefKey.NotesPanelWidth]?: number
[PrefKey.EditorWidth]?: number
[PrefKey.EditorLeft]?: number
[PrefKey.EditorMonospaceEnabled]?: boolean
[PrefKey.EditorSpellcheck]?: boolean
[PrefKey.EditorResizersEnabled]?: boolean
}
type NoteFlag = {
@@ -147,8 +146,6 @@ class NotesViewCtrl extends PureViewCtrl<{}, NotesState> {
this.handleTagChange(this.selectedTag!);
} else if (eventName === AppStateEvent.ActiveEditorChanged) {
this.handleEditorChange();
} else if (eventName === AppStateEvent.PreferencesChanged) {
this.reloadPreferences();
} else if (eventName === AppStateEvent.EditorFocused) {
this.setShowMenuFalse();
}
@@ -166,6 +163,9 @@ class NotesViewCtrl extends PureViewCtrl<{}, NotesState> {
/** @override */
async onAppEvent(eventName: ApplicationEvent) {
switch (eventName) {
case ApplicationEvent.PreferencesChanged:
this.reloadPreferences();
break;
case ApplicationEvent.SignedIn:
this.appState.closeAllEditors();
this.selectFirstNote();
@@ -420,37 +420,37 @@ class NotesViewCtrl extends PureViewCtrl<{}, NotesState> {
async reloadPreferences() {
const viewOptions = {} as NotesState;
const prevSortValue = this.getState().sortBy;
let sortBy = this.application!.getPrefsService().getValue(
WebPrefKey.SortNotesBy,
NoteSortKey.CreatedAt
let sortBy = this.application!.getPreference(
PrefKey.SortNotesBy,
CollectionSort.CreatedAt
);
if (sortBy === NoteSortKey.UpdatedAt || sortBy === NoteSortKey.ClientUpdatedAt) {
if (sortBy === CollectionSort.UpdatedAt) {
/** Use UserUpdatedAt instead */
sortBy = NoteSortKey.UserUpdatedAt;
sortBy = CollectionSort.UpdatedAt;
}
viewOptions.sortBy = sortBy;
viewOptions.sortReverse = this.application!.getPrefsService().getValue(
WebPrefKey.SortNotesReverse,
viewOptions.sortReverse = this.application!.getPreference(
PrefKey.SortNotesReverse,
false
);
viewOptions.showArchived = this.application!.getPrefsService().getValue(
WebPrefKey.NotesShowArchived,
viewOptions.showArchived = this.application!.getPreference(
PrefKey.NotesShowArchived,
false
);
viewOptions.hidePinned = this.application!.getPrefsService().getValue(
WebPrefKey.NotesHidePinned,
viewOptions.hidePinned = this.application!.getPreference(
PrefKey.NotesHidePinned,
false
);
viewOptions.hideNotePreview = this.application!.getPrefsService().getValue(
WebPrefKey.NotesHideNotePreview,
viewOptions.hideNotePreview = this.application!.getPreference(
PrefKey.NotesHideNotePreview,
false
);
viewOptions.hideDate = this.application!.getPrefsService().getValue(
WebPrefKey.NotesHideDate,
viewOptions.hideDate = this.application!.getPreference(
PrefKey.NotesHideDate,
false
);
viewOptions.hideTags = this.application.getPrefsService().getValue(
WebPrefKey.NotesHideTags,
viewOptions.hideTags = this.application.getPreference(
PrefKey.NotesHideTags,
true,
);
const state = this.getState();
@@ -475,8 +475,8 @@ class NotesViewCtrl extends PureViewCtrl<{}, NotesState> {
}
reloadPanelWidth() {
const width = this.application!.getPrefsService().getValue(
WebPrefKey.NotesPanelWidth
const width = this.application!.getPreference(
PrefKey.NotesPanelWidth
);
if (width && this.panelPuppet!.ready) {
this.panelPuppet!.setWidth!(width);
@@ -495,10 +495,9 @@ class NotesViewCtrl extends PureViewCtrl<{}, NotesState> {
__: boolean,
isCollapsed: boolean
) {
this.application!.getPrefsService().setUserPrefValue(
WebPrefKey.NotesPanelWidth,
newWidth,
true
this.application!.setPreference(
PrefKey.NotesPanelWidth,
newWidth
);
this.application!.getAppState().panelDidResize(
PANEL_NAME_NOTES,
@@ -541,11 +540,11 @@ class NotesViewCtrl extends PureViewCtrl<{}, NotesState> {
optionsSubtitle() {
let base = "";
if (this.getState().sortBy === NoteSortKey.CreatedAt) {
if (this.getState().sortBy === CollectionSort.CreatedAt) {
base += " Date Added";
} else if (this.getState().sortBy === NoteSortKey.UserUpdatedAt) {
} else if (this.getState().sortBy === CollectionSort.UpdatedAt) {
base += " Date Modified";
} else if (this.getState().sortBy === NoteSortKey.Title) {
} else if (this.getState().sortBy === CollectionSort.Title) {
base += " Title";
}
if (this.getState().showArchived) {
@@ -709,40 +708,37 @@ class NotesViewCtrl extends PureViewCtrl<{}, NotesState> {
this.setShowMenuFalse();
}
toggleWebPrefKey(key: WebPrefKey) {
this.application!.getPrefsService().setUserPrefValue(
togglePrefKey(key: PrefKey) {
this.application!.setPreference(
key,
!this.state[key],
true
!this.state[key]
);
}
selectedSortByCreated() {
this.setSortBy(NoteSortKey.CreatedAt);
this.setSortBy(CollectionSort.CreatedAt);
}
selectedSortByUpdated() {
this.setSortBy(NoteSortKey.ClientUpdatedAt);
this.setSortBy(CollectionSort.UpdatedAt);
}
selectedSortByTitle() {
this.setSortBy(NoteSortKey.Title);
this.setSortBy(CollectionSort.Title);
}
toggleReverseSort() {
this.selectedMenuItem();
this.application!.getPrefsService().setUserPrefValue(
WebPrefKey.SortNotesReverse,
!this.getState().sortReverse,
true
this.application!.setPreference(
PrefKey.SortNotesReverse,
!this.getState().sortReverse
);
}
setSortBy(type: NoteSortKey) {
this.application!.getPrefsService().setUserPrefValue(
WebPrefKey.SortNotesBy,
type,
true
setSortBy(type: CollectionSort) {
this.application!.setPreference(
PrefKey.SortNotesBy,
type
);
}

View File

@@ -9,7 +9,7 @@ import {
SNSmartTag,
ComponentArea,
SNComponent,
WebPrefKey,
PrefKey,
UuidString,
TagMutator
} from '@standardnotes/snjs';
@@ -145,10 +145,8 @@ class TagsViewCtrl extends PureViewCtrl<{}, TagState> {
}
/** @override */
onAppStateEvent(eventName: AppStateEvent, data?: any) {
if (eventName === AppStateEvent.PreferencesChanged) {
this.loadPreferences();
} else if (eventName === AppStateEvent.TagChanged) {
onAppStateEvent(eventName: AppStateEvent) {
if (eventName === AppStateEvent.TagChanged) {
this.setState({
selectedTag: this.application.getAppState().getSelectedTag()
});
@@ -159,8 +157,13 @@ class TagsViewCtrl extends PureViewCtrl<{}, TagState> {
/** @override */
async onAppEvent(eventName: ApplicationEvent) {
super.onAppEvent(eventName);
if (eventName === ApplicationEvent.LocalDataIncrementalLoad) {
this.reloadNoteCounts();
switch (eventName) {
case ApplicationEvent.LocalDataIncrementalLoad:
this.reloadNoteCounts();
break;
case ApplicationEvent.PreferencesChanged:
this.loadPreferences();
break;
}
}
@@ -203,7 +206,7 @@ class TagsViewCtrl extends PureViewCtrl<{}, TagState> {
if (!this.panelPuppet.ready) {
return;
}
const width = this.application.getPrefsService().getValue(WebPrefKey.TagsPanelWidth);
const width = this.application.getPreference(PrefKey.TagsPanelWidth);
if (width) {
this.panelPuppet.setWidth!(width);
if (this.panelPuppet.isCollapsed!()) {
@@ -221,11 +224,10 @@ class TagsViewCtrl extends PureViewCtrl<{}, TagState> {
_isAtMaxWidth: boolean,
isCollapsed: boolean
) => {
this.application.getPrefsService().setUserPrefValue(
WebPrefKey.TagsPanelWidth,
newWidth,
true
);
this.application.setPreference(
PrefKey.TagsPanelWidth,
newWidth
).then(() => this.application.sync());
this.application.getAppState().panelDidResize(
PANEL_NAME_TAGS,
isCollapsed