Merge branch 'develop' into fix/lock-app-on-background
This commit is contained in:
@@ -24,50 +24,37 @@
|
||||
ng-if="self.showLockedIcon"
|
||||
)
|
||||
| {{self.lockText}}
|
||||
#editor-title-bar.section-title-bar.flex.items-center.justify-between.w-full(
|
||||
#editor-title-bar.section-title-bar.w-full(
|
||||
ng-show='self.note && !self.note.errorDecrypting'
|
||||
)
|
||||
div.flex-grow(
|
||||
ng-class="{'locked' : self.noteLocked}"
|
||||
)
|
||||
.title
|
||||
input#note-title-editor.input(
|
||||
ng-blur='self.onTitleBlur()',
|
||||
ng-change='self.onTitleChange()',
|
||||
ng-disabled='self.noteLocked',
|
||||
ng-focus='self.onTitleFocus()',
|
||||
ng-keyup='$event.keyCode == 13 && self.onTitleEnter($event)',
|
||||
ng-model='self.editorValues.title',
|
||||
select-on-focus='true',
|
||||
spellcheck='false'
|
||||
)
|
||||
.editor-tags
|
||||
#note-tags-component-container(ng-if='self.state.tagsComponent && !self.note.errorDecrypting')
|
||||
component-view.component-view(
|
||||
component-uuid='self.state.tagsComponent.uuid',
|
||||
ng-style="self.notesLocked && {'pointer-events' : 'none'}",
|
||||
application='self.application'
|
||||
)
|
||||
input.tags-input(
|
||||
ng-blur='self.onTagsInputBlur()',
|
||||
ng-disabled='self.noteLocked',
|
||||
ng-if='!self.state.tagsComponent',
|
||||
ng-keyup='$event.keyCode == 13 && $event.target.blur();',
|
||||
ng-model='self.editorValues.tagsInputValue',
|
||||
placeholder='#tags',
|
||||
spellcheck='false',
|
||||
type='text'
|
||||
)
|
||||
div.flex.items-center
|
||||
#save-status
|
||||
.message(
|
||||
ng-class="{'warning sk-bold': self.state.syncTakingTooLong, 'danger sk-bold': self.state.saveError}"
|
||||
) {{self.state.noteStatus.message}}
|
||||
.desc(ng-show='self.state.noteStatus.desc') {{self.state.noteStatus.desc}}
|
||||
notes-options-panel(
|
||||
app-state='self.appState',
|
||||
ng-if='self.appState.notes.selectedNotesCount > 0'
|
||||
div.flex.items-center.justify-between.h-8
|
||||
div.flex-grow(
|
||||
ng-class="{'locked' : self.noteLocked}"
|
||||
)
|
||||
.title.overflow-auto
|
||||
input#note-title-editor.input(
|
||||
ng-blur='self.onTitleBlur()',
|
||||
ng-change='self.onTitleChange()',
|
||||
ng-disabled='self.noteLocked',
|
||||
ng-focus='self.onTitleFocus()',
|
||||
ng-keyup='$event.keyCode == 13 && self.onTitleEnter($event)',
|
||||
ng-model='self.editorValues.title',
|
||||
select-on-focus='true',
|
||||
spellcheck='false'
|
||||
)
|
||||
div.flex.items-center
|
||||
#save-status
|
||||
.message(
|
||||
ng-class="{'warning sk-bold': self.state.syncTakingTooLong, 'danger sk-bold': self.state.saveError}"
|
||||
) {{self.state.noteStatus.message}}
|
||||
.desc(ng-show='self.state.noteStatus.desc') {{self.state.noteStatus.desc}}
|
||||
notes-options-panel(
|
||||
app-state='self.appState',
|
||||
ng-if='self.appState.notes.selectedNotesCount > 0'
|
||||
)
|
||||
note-tags-container(
|
||||
app-state='self.appState'
|
||||
)
|
||||
.sn-component(ng-if='self.note')
|
||||
#editor-menu-bar.sk-app-bar.no-edges
|
||||
.left
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import {
|
||||
STRING_ARCHIVE_LOCKED_ATTEMPT,
|
||||
STRING_SAVING_WHILE_DOCUMENT_HIDDEN,
|
||||
STRING_UNARCHIVE_LOCKED_ATTEMPT,
|
||||
} from './../../strings';
|
||||
import { Editor } from '@/ui_models/editor';
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
@@ -14,15 +12,12 @@ import {
|
||||
ContentType,
|
||||
SNComponent,
|
||||
SNNote,
|
||||
SNTag,
|
||||
NoteMutator,
|
||||
Uuids,
|
||||
ComponentArea,
|
||||
ComponentAction,
|
||||
PrefKey,
|
||||
ComponentMutator,
|
||||
} from '@standardnotes/snjs';
|
||||
import find from 'lodash/find';
|
||||
import { isDesktopApplication } from '@/utils';
|
||||
import { KeyboardModifier, KeyboardKey } from '@/services/ioService';
|
||||
import template from './editor-view.pug';
|
||||
@@ -36,9 +31,8 @@ import {
|
||||
STRING_DELETE_LOCKED_ATTEMPT,
|
||||
STRING_EDIT_LOCKED_ATTEMPT,
|
||||
StringDeleteNote,
|
||||
StringEmptyTrash,
|
||||
} from '@/strings';
|
||||
import { alertDialog, confirmDialog } from '@/services/alertService';
|
||||
import { confirmDialog } from '@/services/alertService';
|
||||
|
||||
const NOTE_PREVIEW_CHAR_LIMIT = 80;
|
||||
const MINIMUM_STATUS_DURATION = 400;
|
||||
@@ -50,7 +44,6 @@ const ElementIds = {
|
||||
NoteTextEditor: 'note-text-editor',
|
||||
NoteTitleEditor: 'note-title-editor',
|
||||
EditorContent: 'editor-content',
|
||||
NoteTagsComponentContainer: 'note-tags-component-container',
|
||||
};
|
||||
|
||||
type NoteStatus = {
|
||||
@@ -61,10 +54,8 @@ type NoteStatus = {
|
||||
type EditorState = {
|
||||
stackComponents: SNComponent[];
|
||||
editorComponent?: SNComponent;
|
||||
tagsComponent?: SNComponent;
|
||||
saveError?: any;
|
||||
noteStatus?: NoteStatus;
|
||||
tagsAsStrings?: string;
|
||||
marginResizersEnabled?: boolean;
|
||||
monospaceFont?: boolean;
|
||||
isDesktop?: boolean;
|
||||
@@ -83,14 +74,11 @@ type EditorState = {
|
||||
/** Setting to true then false will allow the main content textarea to be destroyed
|
||||
* then re-initialized. Used when reloading spellcheck status. */
|
||||
textareaUnloading: boolean;
|
||||
/** Fields that can be directly mutated by the template */
|
||||
mutable: any;
|
||||
};
|
||||
|
||||
type EditorValues = {
|
||||
title: string;
|
||||
text: string;
|
||||
tagsInputValue?: string;
|
||||
};
|
||||
|
||||
function copyEditorValues(values: EditorValues) {
|
||||
@@ -117,13 +105,10 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
|
||||
public editorValues: EditorValues = { title: '', text: '' };
|
||||
onEditorLoad?: () => void;
|
||||
|
||||
private tags: SNTag[] = [];
|
||||
|
||||
private removeAltKeyObserver?: any;
|
||||
private removeTrashKeyObserver?: any;
|
||||
private removeTabObserver?: any;
|
||||
|
||||
private removeTagsObserver!: () => void;
|
||||
private removeComponentsObserver!: () => void;
|
||||
|
||||
prefKeyMonospace: string;
|
||||
@@ -153,9 +138,7 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
|
||||
|
||||
deinit() {
|
||||
this.editor.clearNoteChangeListener();
|
||||
this.removeTagsObserver();
|
||||
this.removeComponentsObserver();
|
||||
(this.removeTagsObserver as any) = undefined;
|
||||
(this.removeComponentsObserver as any) = undefined;
|
||||
this.removeAltKeyObserver();
|
||||
this.removeAltKeyObserver = undefined;
|
||||
@@ -172,7 +155,6 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
|
||||
this.statusTimeout = undefined;
|
||||
(this.onPanelResizeFinish as any) = undefined;
|
||||
(this.editorMenuOnSelect as any) = undefined;
|
||||
this.tags = [];
|
||||
super.deinit();
|
||||
}
|
||||
|
||||
@@ -194,7 +176,6 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
|
||||
if (isPayloadSourceRetrieved(source!)) {
|
||||
this.editorValues.title = note.title;
|
||||
this.editorValues.text = note.text;
|
||||
this.reloadTags();
|
||||
}
|
||||
if (!this.editorValues.title) {
|
||||
this.editorValues.title = note.title;
|
||||
@@ -237,9 +218,6 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
|
||||
noteStatus: undefined,
|
||||
editorUnloading: false,
|
||||
textareaUnloading: false,
|
||||
mutable: {
|
||||
tagsString: '',
|
||||
},
|
||||
} as EditorState;
|
||||
}
|
||||
|
||||
@@ -302,10 +280,8 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
|
||||
this.editorValues.title = note.title;
|
||||
this.editorValues.text = note.text;
|
||||
this.reloadEditor();
|
||||
this.reloadTags();
|
||||
this.reloadPreferences();
|
||||
this.reloadStackComponents();
|
||||
this.reloadNoteTagsComponent();
|
||||
if (note.dirty) {
|
||||
this.showSavingStatus();
|
||||
}
|
||||
@@ -335,13 +311,6 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
|
||||
}
|
||||
|
||||
streamItems() {
|
||||
this.removeTagsObserver = this.application.streamItems(
|
||||
ContentType.Tag,
|
||||
() => {
|
||||
this.reloadTags();
|
||||
}
|
||||
);
|
||||
|
||||
this.removeComponentsObserver = this.application.streamItems(
|
||||
ContentType.Component,
|
||||
async (_items, source) => {
|
||||
@@ -350,7 +319,6 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
|
||||
}
|
||||
if (!this.note) return;
|
||||
this.reloadStackComponents();
|
||||
this.reloadNoteTagsComponent();
|
||||
this.reloadEditor();
|
||||
}
|
||||
);
|
||||
@@ -487,7 +455,6 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
|
||||
const title = editorValues.title;
|
||||
const text = editorValues.text;
|
||||
const isTemplate = this.editor.isTemplateNote;
|
||||
const selectedTag = this.appState.selectedTag;
|
||||
if (document.hidden) {
|
||||
this.application.alertService.alert(STRING_SAVING_WHILE_DOCUMENT_HIDDEN);
|
||||
return;
|
||||
@@ -499,14 +466,6 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
|
||||
if (isTemplate) {
|
||||
await this.editor.insertTemplatedNote();
|
||||
}
|
||||
if (
|
||||
!selectedTag?.isSmartTag &&
|
||||
!selectedTag?.hasRelationshipWithItem(note)
|
||||
) {
|
||||
await this.application.changeItem(selectedTag!.uuid, (mutator) => {
|
||||
mutator.addItemAsRelationship(note);
|
||||
});
|
||||
}
|
||||
if (!this.application.findItem(note.uuid)) {
|
||||
this.application.alertService.alert(STRING_INVALID_NOTE);
|
||||
return;
|
||||
@@ -697,105 +656,6 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
|
||||
this.application.deleteItem(note);
|
||||
}
|
||||
|
||||
async reloadTags() {
|
||||
if (!this.note) {
|
||||
return;
|
||||
}
|
||||
const tags = this.appState.getNoteTags(this.note);
|
||||
if (tags.length !== this.tags.length) {
|
||||
this.reloadTagsString(tags);
|
||||
} else {
|
||||
/** Check that all tags are the same */
|
||||
for (let i = 0; i < tags.length; i++) {
|
||||
const localTag = this.tags[i];
|
||||
const tag = tags[i];
|
||||
if (tag.title !== localTag.title || tag.uuid !== localTag.uuid) {
|
||||
this.reloadTagsString(tags);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
private async reloadTagsString(tags: SNTag[]) {
|
||||
const string = SNTag.arrayToDisplayString(tags);
|
||||
await this.flushUI();
|
||||
this.editorValues.tagsInputValue = string;
|
||||
}
|
||||
|
||||
private addTag(tag: SNTag) {
|
||||
const tags = this.appState.getNoteTags(this.note);
|
||||
const strings = tags.map((currentTag) => {
|
||||
return currentTag.title;
|
||||
});
|
||||
strings.push(tag.title);
|
||||
this.saveTagsFromStrings(strings);
|
||||
}
|
||||
|
||||
removeTag(tag: SNTag) {
|
||||
const tags = this.appState.getNoteTags(this.note);
|
||||
const strings = tags
|
||||
.map((currentTag) => {
|
||||
return currentTag.title;
|
||||
})
|
||||
.filter((title) => {
|
||||
return title !== tag.title;
|
||||
});
|
||||
this.saveTagsFromStrings(strings);
|
||||
}
|
||||
|
||||
onTagsInputBlur() {
|
||||
this.saveTagsFromStrings();
|
||||
this.focusEditor();
|
||||
}
|
||||
|
||||
public async saveTagsFromStrings(strings?: string[]) {
|
||||
if (
|
||||
!strings &&
|
||||
this.editorValues.tagsInputValue === this.state.tagsAsStrings
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (!strings) {
|
||||
strings = this.editorValues
|
||||
.tagsInputValue!.split('#')
|
||||
.filter((string) => {
|
||||
return string.length > 0;
|
||||
})
|
||||
.map((string) => {
|
||||
return string.trim();
|
||||
});
|
||||
}
|
||||
const note = this.note;
|
||||
const currentTags = this.appState.getNoteTags(note);
|
||||
const removeTags = [];
|
||||
for (const tag of currentTags) {
|
||||
if (strings.indexOf(tag.title) === -1) {
|
||||
removeTags.push(tag);
|
||||
}
|
||||
}
|
||||
for (const tag of removeTags) {
|
||||
await this.application.changeItem(tag.uuid, (mutator) => {
|
||||
mutator.removeItemAsRelationship(note);
|
||||
});
|
||||
}
|
||||
const newRelationships: SNTag[] = [];
|
||||
for (const title of strings) {
|
||||
const existingRelationship = find(currentTags, { title: title });
|
||||
if (!existingRelationship) {
|
||||
newRelationships.push(await this.application.findOrCreateTag(title));
|
||||
}
|
||||
}
|
||||
if (newRelationships.length > 0) {
|
||||
await this.application.changeItems(Uuids(newRelationships), (mutator) => {
|
||||
mutator.addItemAsRelationship(note);
|
||||
});
|
||||
}
|
||||
this.application.sync();
|
||||
this.reloadTags();
|
||||
}
|
||||
|
||||
async onPanelResizeFinish(width: number, left: number, isMaxWidth: boolean) {
|
||||
if (isMaxWidth) {
|
||||
await this.application.setPreference(PrefKey.EditorWidth, null);
|
||||
@@ -900,7 +760,6 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
|
||||
{
|
||||
identifier: 'editor',
|
||||
areas: [
|
||||
ComponentArea.NoteTags,
|
||||
ComponentArea.EditorStack,
|
||||
ComponentArea.Editor,
|
||||
],
|
||||
@@ -908,7 +767,6 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
|
||||
const currentEditor = this.state.editorComponent;
|
||||
if (
|
||||
componentUuid === currentEditor?.uuid ||
|
||||
componentUuid === this.state.tagsComponent?.uuid ||
|
||||
Uuids(this.state.stackComponents).includes(componentUuid)
|
||||
) {
|
||||
return this.note;
|
||||
@@ -919,78 +777,10 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
|
||||
this.closeAllMenus();
|
||||
}
|
||||
},
|
||||
actionHandler: (component, action, data) => {
|
||||
if (action === ComponentAction.SetSize) {
|
||||
const setSize = (
|
||||
element: HTMLElement,
|
||||
size: { width: string | number; height: string | number }
|
||||
) => {
|
||||
const widthString =
|
||||
typeof size.width === 'string' ? size.width : `${data.width}px`;
|
||||
const heightString =
|
||||
typeof size.height === 'string'
|
||||
? size.height
|
||||
: `${data.height}px`;
|
||||
element.setAttribute(
|
||||
'style',
|
||||
`width: ${widthString}; height: ${heightString};`
|
||||
);
|
||||
};
|
||||
if (data.type === 'container') {
|
||||
if (component.area === ComponentArea.NoteTags) {
|
||||
const container = document.getElementById(
|
||||
ElementIds.NoteTagsComponentContainer
|
||||
);
|
||||
setSize(container!, {
|
||||
width: data.width!,
|
||||
height: data.height!,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (action === ComponentAction.AssociateItem) {
|
||||
if (data.item!.content_type === ContentType.Tag) {
|
||||
const tag = this.application.findItem(data.item!.uuid) as SNTag;
|
||||
this.addTag(tag);
|
||||
}
|
||||
} else if (action === ComponentAction.DeassociateItem) {
|
||||
const tag = this.application.findItem(data.item!.uuid) as SNTag;
|
||||
this.removeTag(tag);
|
||||
} else if (action === ComponentAction.SaveSuccess) {
|
||||
const savedUuid = data.item ? data.item.uuid : data.items![0].uuid;
|
||||
if (savedUuid === this.note.uuid) {
|
||||
const selectedTag = this.appState.selectedTag;
|
||||
if (
|
||||
!selectedTag?.isSmartTag &&
|
||||
!selectedTag?.hasRelationshipWithItem(this.note)
|
||||
) {
|
||||
this.application.changeAndSaveItem(
|
||||
selectedTag!.uuid,
|
||||
(mutator) => {
|
||||
mutator.addItemAsRelationship(this.note);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async reloadNoteTagsComponent() {
|
||||
const [
|
||||
tagsComponent,
|
||||
] = this.application.componentManager!.componentsForArea(
|
||||
ComponentArea.NoteTags
|
||||
);
|
||||
await this.setState({
|
||||
tagsComponent: tagsComponent?.active ? tagsComponent : undefined,
|
||||
});
|
||||
this.application.componentManager!.contextItemDidChangeInArea(
|
||||
ComponentArea.NoteTags
|
||||
);
|
||||
}
|
||||
|
||||
async reloadStackComponents() {
|
||||
const stackComponents = sortAlphabetically(
|
||||
this.application
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
import { WebDirective } from './../../types';
|
||||
import template from './editor-group-view.pug';
|
||||
import { Editor } from '@/ui_models/editor';
|
||||
@@ -15,7 +14,7 @@ class EditorGroupViewCtrl extends PureViewCtrl<unknown, {
|
||||
super($timeout);
|
||||
this.state = {
|
||||
showMultipleSelectedNotes: false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
|
||||
@@ -169,5 +169,6 @@
|
||||
default-width="300"
|
||||
hoverable="true"
|
||||
on-resize-finish="self.onPanelResize"
|
||||
on-width-event="self.onPanelWidthEvent"
|
||||
panel-id="'notes-column'"
|
||||
)
|
||||
|
||||
@@ -93,6 +93,7 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesCtrlState> {
|
||||
};
|
||||
this.onWindowResize = this.onWindowResize.bind(this);
|
||||
this.onPanelResize = this.onPanelResize.bind(this);
|
||||
this.onPanelWidthEvent = this.onPanelWidthEvent.bind(this);
|
||||
window.addEventListener('resize', this.onWindowResize, true);
|
||||
this.registerKeyboardShortcuts();
|
||||
this.autorun(async () => {
|
||||
@@ -133,6 +134,7 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesCtrlState> {
|
||||
window.removeEventListener('resize', this.onWindowResize, true);
|
||||
(this.onWindowResize as any) = undefined;
|
||||
(this.onPanelResize as any) = undefined;
|
||||
(this.onPanelWidthEvent as any) = undefined;
|
||||
this.newNoteKeyObserver();
|
||||
this.nextNoteKeyObserver();
|
||||
this.previousNoteKeyObserver();
|
||||
@@ -408,6 +410,7 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesCtrlState> {
|
||||
await this.appState.createEditor(title);
|
||||
await this.flushUI();
|
||||
await this.reloadNotes();
|
||||
await this.appState.noteTags.reloadTags();
|
||||
}
|
||||
|
||||
async handleTagChange(tag: SNTag) {
|
||||
@@ -642,10 +645,11 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesCtrlState> {
|
||||
|
||||
onPanelResize(
|
||||
newWidth: number,
|
||||
_: number,
|
||||
newLeft: number,
|
||||
__: boolean,
|
||||
isCollapsed: boolean
|
||||
) {
|
||||
this.appState.noteTags.reloadTagsContainerMaxWidth();
|
||||
this.application.setPreference(
|
||||
PrefKey.NotesPanelWidth,
|
||||
newWidth
|
||||
@@ -656,6 +660,10 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesCtrlState> {
|
||||
);
|
||||
}
|
||||
|
||||
onPanelWidthEvent(): void {
|
||||
this.appState.noteTags.reloadTagsContainerMaxWidth();
|
||||
}
|
||||
|
||||
paginate() {
|
||||
this.notesToDisplay += this.pageSize;
|
||||
this.reloadNotes();
|
||||
|
||||
Reference in New Issue
Block a user