feat: select multiple notes in list

This commit is contained in:
Baptiste Grob
2021-04-06 18:09:40 +02:00
parent 9599f30ad4
commit 0f53361689
11 changed files with 354 additions and 273 deletions

View File

@@ -1,5 +1,7 @@
import { ApplicationEvent } from '@standardnotes/snjs';
import { WebApplication } from '@/ui_models/application';
import { AppState } from '@/ui_models/app_state';
import { autorun, IReactionDisposer, IReactionPublic } from 'mobx';
export type CtrlState = Partial<Record<string, any>>
export type CtrlProps = Partial<Record<string, any>>
@@ -17,6 +19,7 @@ export class PureViewCtrl<P = CtrlProps, S = CtrlState> {
* no Angular handlebars/syntax render in the UI before display data is ready.
*/
protected templateReady = false
private reactionDisposers: IReactionDisposer[] = [];
/* @ngInject */
constructor(
@@ -26,7 +29,7 @@ export class PureViewCtrl<P = CtrlProps, S = CtrlState> {
this.$timeout = $timeout;
}
$onInit() {
$onInit(): void {
this.state = {
...this.getInitialState(),
...this.state,
@@ -36,9 +39,13 @@ export class PureViewCtrl<P = CtrlProps, S = CtrlState> {
this.templateReady = true;
}
deinit() {
deinit(): void {
this.unsubApp();
this.unsubState();
for (const disposer of this.reactionDisposers) {
disposer();
}
this.reactionDisposers.length = 0;
this.unsubApp = undefined;
this.unsubState = undefined;
if (this.stateTimeout) {
@@ -46,16 +53,16 @@ export class PureViewCtrl<P = CtrlProps, S = CtrlState> {
}
}
$onDestroy() {
$onDestroy(): void {
this.deinit();
}
public get appState() {
return this.application!.getAppState();
public get appState(): AppState {
return this.application.getAppState();
}
/** @private */
async resetState() {
async resetState(): Promise<void> {
this.state = this.getInitialState();
await this.setState(this.state);
}
@@ -65,7 +72,7 @@ export class PureViewCtrl<P = CtrlProps, S = CtrlState> {
return {} as any;
}
async setState(state: Partial<S>) {
async setState(state: Partial<S>): Promise<void> {
if (!this.$timeout) {
return;
}
@@ -88,17 +95,21 @@ export class PureViewCtrl<P = CtrlProps, S = CtrlState> {
}
/** @returns a promise that resolves after the UI has been updated. */
flushUI() {
flushUI(): angular.IPromise<void> {
return this.$timeout();
}
initProps(props: CtrlProps) {
initProps(props: CtrlProps): void {
if (Object.keys(this.props).length > 0) {
throw 'Already init-ed props.';
}
this.props = Object.freeze(Object.assign({}, this.props, props));
}
autorun(view: (r: IReactionPublic) => void): void {
this.reactionDisposers.push(autorun(view));
}
addAppStateObserver() {
this.unsubState = this.application!.getAppState().addObserver(
async (eventName, data) => {

View File

@@ -24,7 +24,7 @@ import {
} from '@standardnotes/snjs';
import find from 'lodash/find';
import { isDesktopApplication } from '@/utils';
import { KeyboardModifier, KeyboardKey } from '@/services/keyboardManager';
import { KeyboardModifier, KeyboardKey } from '@/services/ioService';
import template from './editor-view.pug';
import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl';
import { EventSource } from '@/ui_models/app_state';
@@ -1106,7 +1106,7 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
registerKeyboardShortcuts() {
this.removeAltKeyObserver = this.application
.getKeyboardService()
.io
.addKeyObserver({
modifiers: [KeyboardModifier.Alt],
onKeyDown: () => {
@@ -1122,7 +1122,7 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
});
this.removeTrashKeyObserver = this.application
.getKeyboardService()
.io
.addKeyObserver({
key: KeyboardKey.Backspace,
notElementIds: [ElementIds.NoteTextEditor, ElementIds.NoteTitleEditor],
@@ -1147,7 +1147,7 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
ElementIds.NoteTextEditor
)! as HTMLInputElement;
this.removeTabObserver = this.application
.getKeyboardService()
.io
.addKeyObserver({
element: editor,
key: KeyboardKey.Tab,

View File

@@ -127,7 +127,7 @@
)
.note(
ng-repeat='note in self.state.renderedNotes track by note.uuid'
ng-class="{'selected' : self.activeEditorNote.uuid == note.uuid}"
ng-class="{'selected' : self.isNoteSelected(note.uuid) }"
ng-click='self.selectNote(note)'
)
.note-flags(ng-show='self.noteFlags[note.uuid].length > 0')

View File

@@ -14,17 +14,17 @@ import {
} from '@standardnotes/snjs';
import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl';
import { AppStateEvent } from '@/ui_models/app_state';
import { KeyboardModifier, KeyboardKey } from '@/services/keyboardManager';
import { KeyboardKey, KeyboardModifier } from '@/services/ioService';
import {
PANEL_NAME_NOTES
} from '@/views/constants';
import { autorun, IReactionDisposer } from 'mobx';
type NotesState = {
type NotesCtrlState = {
panelTitle: string
notes: SNNote[]
renderedNotes: SNNote[]
renderedNotesTags: string[],
selectedNotes: Record<UuidString, SNNote>,
sortBy?: string
sortReverse?: boolean
showArchived?: boolean
@@ -65,7 +65,7 @@ const DEFAULT_LIST_NUM_NOTES = 20;
const ELEMENT_ID_SEARCH_BAR = 'search-bar';
const ELEMENT_ID_SCROLL_CONTAINER = 'notes-scrollable';
class NotesViewCtrl extends PureViewCtrl<unknown, NotesState> {
class NotesViewCtrl extends PureViewCtrl<unknown, NotesCtrlState> {
private panelPuppet?: PanelPuppet
private reloadNotesPromise?: any
@@ -78,7 +78,6 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesState> {
private searchKeyObserver: any
private noteFlags: Partial<Record<UuidString, NoteFlag[]>> = {}
private removeObservers: Array<() => void> = [];
private appStateObserver?: IReactionDisposer;
/* @ngInject */
constructor($timeout: ng.ITimeoutService,) {
@@ -95,7 +94,7 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesState> {
this.onPanelResize = this.onPanelResize.bind(this);
window.addEventListener('resize', this.onWindowResize, true);
this.registerKeyboardShortcuts();
this.appStateObserver = autorun(async () => {
this.autorun(async () => {
const {
includeProtectedContents,
includeArchived,
@@ -113,6 +112,11 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesState> {
this.reloadNotes();
}
});
this.autorun(() => {
this.setState({
selectedNotes: this.appState.notes.selectedNotes,
});
});
}
onWindowResize() {
@@ -131,7 +135,6 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesState> {
this.nextNoteKeyObserver();
this.previousNoteKeyObserver();
this.searchKeyObserver();
this.appStateObserver?.();
this.newNoteKeyObserver = undefined;
this.nextNoteKeyObserver = undefined;
this.previousNoteKeyObserver = undefined;
@@ -139,15 +142,16 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesState> {
super.deinit();
}
async setNotesState(state: Partial<NotesState>) {
async setNotesState(state: Partial<NotesCtrlState>) {
return this.setState(state);
}
getInitialState(): NotesState {
getInitialState(): NotesCtrlState {
return {
notes: [],
renderedNotes: [],
renderedNotesTags: [],
selectedNotes: {},
mutable: { showMenu: false },
noteFilter: {
text: '',
@@ -180,9 +184,13 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesState> {
}
}
private get activeEditorNote() {
return this.appState.notes.activeEditor?.note;
}
/** @template */
public get activeEditorNote() {
return this.appState?.getActiveEditor()?.note;
public isNoteSelected(uuid: UuidString) {
return !!this.state.selectedNotes[uuid];
}
public get editorNotes() {
@@ -288,12 +296,8 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesState> {
));
}
async selectNote(note: SNNote) {
await this.appState.openEditor(note.uuid);
if (note.waitingForKey) {
this.application.presentKeyRecoveryWizard();
}
this.reloadNotes();
selectNote(note: SNNote): Promise<void> {
return this.appState.notes.selectNote(note);
}
async createNewNote() {
@@ -461,7 +465,7 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesState> {
}
async reloadPreferences() {
const viewOptions = {} as NotesState;
const viewOptions = {} as NotesCtrlState;
const prevSortValue = this.state.sortBy;
let sortBy = this.application.getPreference(
PrefKey.SortNotesBy,
@@ -673,7 +677,7 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesState> {
selectNextNote() {
const displayableNotes = this.state.notes;
const currentIndex = displayableNotes.findIndex((candidate) => {
return candidate.uuid === this.activeEditorNote.uuid;
return candidate.uuid === this.activeEditorNote?.uuid;
});
if (currentIndex + 1 < displayableNotes.length) {
this.selectNote(displayableNotes[currentIndex + 1]);
@@ -791,7 +795,7 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesState> {
* use Control modifier as well. These rules don't apply to desktop, but
* probably better to be consistent.
*/
this.newNoteKeyObserver = this.application.getKeyboardService().addKeyObserver({
this.newNoteKeyObserver = this.application.io.addKeyObserver({
key: 'n',
modifiers: [
KeyboardModifier.Meta,
@@ -803,7 +807,7 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesState> {
}
});
this.nextNoteKeyObserver = this.application.getKeyboardService().addKeyObserver({
this.nextNoteKeyObserver = this.application.io.addKeyObserver({
key: KeyboardKey.Down,
elements: [
document.body,
@@ -818,7 +822,7 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesState> {
}
});
this.previousNoteKeyObserver = this.application.getKeyboardService().addKeyObserver({
this.previousNoteKeyObserver = this.application.io.addKeyObserver({
key: KeyboardKey.Up,
element: document.body,
onKeyDown: () => {
@@ -826,7 +830,7 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesState> {
}
});
this.searchKeyObserver = this.application.getKeyboardService().addKeyObserver({
this.searchKeyObserver = this.application.io.addKeyObserver({
key: "f",
modifiers: [
KeyboardModifier.Meta,