Tags fixes
This commit is contained in:
@@ -166,7 +166,6 @@ export class AppState {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.selectedTag) {
|
||||
const matchingTag = items.find((candidate) => candidate.uuid === this.selectedTag!.uuid);
|
||||
if (matchingTag) {
|
||||
|
||||
@@ -8,7 +8,7 @@ export class Editor {
|
||||
private _onNoteChange?: () => void
|
||||
private _onNoteValueChange?: (note: SNNote, source?: PayloadSource) => void
|
||||
private removeStreamObserver: () => void
|
||||
public isTemplateNote = true
|
||||
public isTemplateNote = false
|
||||
|
||||
constructor(
|
||||
application: WebApplication,
|
||||
|
||||
@@ -440,7 +440,6 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
|
||||
) {
|
||||
this.performFirefoxPinnedTabFix();
|
||||
const note = this.note;
|
||||
|
||||
if (note.deleted) {
|
||||
this.application.alertService!.alert(
|
||||
STRING_DELETED_NOTE
|
||||
@@ -448,7 +447,16 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
|
||||
return;
|
||||
}
|
||||
if (this.editor.isTemplateNote) {
|
||||
console.log("is template");
|
||||
await this.editor.insertTemplatedNote();
|
||||
if (this.appState.selectedTag) {
|
||||
await this.application.changeItem(
|
||||
this.appState.selectedTag!.uuid,
|
||||
(mutator) => {
|
||||
mutator.addItemAsRelationship(note);
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
if (!this.application.findItem(note.uuid)) {
|
||||
this.application.alertService!.alert(
|
||||
@@ -473,11 +481,9 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
|
||||
noteMutator.preview_html = undefined;
|
||||
}
|
||||
}, isUserModified)
|
||||
|
||||
if (this.saveTimeout) {
|
||||
this.$timeout.cancel(this.saveTimeout);
|
||||
}
|
||||
|
||||
const noDebounce = bypassDebouncer || this.application.noAccount();
|
||||
const syncDebouceMs = noDebounce
|
||||
? SAVE_TIMEOUT_NO_DEBOUNCE
|
||||
@@ -813,7 +819,7 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
|
||||
}
|
||||
}
|
||||
for (const tag of removeTags) {
|
||||
this.application.changeItem(tag.uuid, (mutator) => {
|
||||
await this.application.changeItem(tag.uuid, (mutator) => {
|
||||
mutator.removeItemAsRelationship(note);
|
||||
})
|
||||
}
|
||||
@@ -829,9 +835,14 @@ class EditorViewCtrl extends PureViewCtrl implements EditorViewScope {
|
||||
);
|
||||
}
|
||||
}
|
||||
this.application.changeAndSaveItems(Uuids(newRelationships), (mutator) => {
|
||||
mutator.addItemAsRelationship(note);
|
||||
})
|
||||
if (newRelationships.length > 0) {
|
||||
await this.application.changeAndSaveItems(
|
||||
Uuids(newRelationships),
|
||||
(mutator) => {
|
||||
mutator.addItemAsRelationship(note);
|
||||
}
|
||||
)
|
||||
}
|
||||
this.reloadTagsString();
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@ import {
|
||||
removeFromArray,
|
||||
SNNote,
|
||||
SNTag,
|
||||
WebPrefKey
|
||||
WebPrefKey,
|
||||
findInArray
|
||||
} from 'snjs';
|
||||
import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl';
|
||||
import { AppStateEvent } from '@/services/state';
|
||||
@@ -23,7 +24,6 @@ import { UuidString } from '@/../../../../snjs/dist/@types/types';
|
||||
|
||||
type NotesState = {
|
||||
panelTitle: string
|
||||
tag?: SNTag
|
||||
notes?: SNNote[]
|
||||
renderedNotes?: SNNote[]
|
||||
sortBy?: string
|
||||
@@ -214,15 +214,19 @@ class NotesViewCtrl extends PureViewCtrl {
|
||||
} else {
|
||||
this.selectFirstNote();
|
||||
}
|
||||
|
||||
/** Note has changed values, reset its flags */
|
||||
const notes = items.filter((item) => item.content_type === ContentType.Note) as SNNote[];
|
||||
const notes = items.filter((item) => {
|
||||
return item.content_type === ContentType.Note
|
||||
}) as SNNote[];
|
||||
for (const note of notes) {
|
||||
if (note.deleted) {
|
||||
continue;
|
||||
}
|
||||
this.loadFlagsForNote(note);
|
||||
}
|
||||
if (findInArray(items, 'uuid', this.appState.selectedTag?.uuid)) {
|
||||
this.reloadPanelTitle();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -240,8 +244,6 @@ class NotesViewCtrl extends PureViewCtrl {
|
||||
}
|
||||
|
||||
async handleTagChange(tag: SNTag) {
|
||||
await this.setNotesState({ tag });
|
||||
|
||||
this.resetScrollPosition();
|
||||
this.setShowMenuFalse();
|
||||
await this.setNoteFilterText('');
|
||||
@@ -290,7 +292,7 @@ class NotesViewCtrl extends PureViewCtrl {
|
||||
}
|
||||
|
||||
async performPeloadNotes() {
|
||||
const tag = this.getState().tag!;
|
||||
const tag = this.appState.selectedTag!;
|
||||
if (!tag) {
|
||||
return;
|
||||
}
|
||||
@@ -435,8 +437,8 @@ class NotesViewCtrl extends PureViewCtrl {
|
||||
if (this.isFiltering()) {
|
||||
const resultCount = this.getState().notes!.length;
|
||||
title = `${resultCount} search results`;
|
||||
} else if (this.getState().tag) {
|
||||
title = `${this.getState().tag!.title}`;
|
||||
} else if (this.appState.selectedTag) {
|
||||
title = `${this.appState.selectedTag.title}`;
|
||||
}
|
||||
this.setNotesState({
|
||||
panelTitle: title
|
||||
@@ -564,7 +566,7 @@ class NotesViewCtrl extends PureViewCtrl {
|
||||
const note = this.getFirstNonProtectedNote();
|
||||
if (note) {
|
||||
this.selectNote(note);
|
||||
} else if (!this.getState().tag || !this.getState().tag!.isSmartTag()) {
|
||||
} else if (!this.appState.selectedTag|| !this.appState.selectedTag.isSmartTag()) {
|
||||
this.createPlaceholderNote();
|
||||
} else {
|
||||
this.appState.closeActiveEditor();
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
ng-class="{'editing' : self.state.editingTag == tag}",
|
||||
ng-click='self.selectTag(tag)',
|
||||
ng-keyup='$event.keyCode == 13 && $event.target.blur()',
|
||||
should-focus='self.state.newTag || self.state.editingTag == tag',
|
||||
should-focus='self.state.templateTag || self.state.editingTag == tag',
|
||||
sn-autofocus='true',
|
||||
spellcheck='false'
|
||||
)
|
||||
|
||||
@@ -20,6 +20,20 @@ import { TagMutator } from '@/../../../../snjs/dist/@types/models/app/tag';
|
||||
|
||||
type NoteCounts = Partial<Record<string, number>>
|
||||
|
||||
type TagState = {
|
||||
tags: SNTag[]
|
||||
smartTags: SNSmartTag[]
|
||||
noteCounts: NoteCounts
|
||||
selectedTag: SNTag
|
||||
/** If creating a new tag, the previously selected tag will be set here, so that if new
|
||||
* tag creation is canceled, the previous tag is re-selected */
|
||||
previousTag?: SNTag
|
||||
/** If a tag is in edit state, it will be set as the editingTag */
|
||||
editingTag?: SNTag
|
||||
/** If a tag is new and not yet saved, it will be set as the template tag */
|
||||
templateTag?: SNTag
|
||||
}
|
||||
|
||||
class TagsViewCtrl extends PureViewCtrl {
|
||||
|
||||
/** Passed through template */
|
||||
@@ -27,6 +41,7 @@ class TagsViewCtrl extends PureViewCtrl {
|
||||
private readonly panelPuppet: PanelPuppet
|
||||
private unregisterComponent?: any
|
||||
component?: SNComponent
|
||||
/** The original name of the edtingTag before it began editing */
|
||||
private editingOriginalName?: string
|
||||
formData: { tagTitle?: string } = {}
|
||||
titles: Partial<Record<UuidString, string>> = {}
|
||||
@@ -55,6 +70,14 @@ class TagsViewCtrl extends PureViewCtrl {
|
||||
};
|
||||
}
|
||||
|
||||
getState() {
|
||||
return this.state as TagState;
|
||||
}
|
||||
|
||||
async setTagState(state: Partial<TagState>) {
|
||||
return this.setState(state);
|
||||
}
|
||||
|
||||
async onAppStart() {
|
||||
super.onAppStart();
|
||||
this.registerComponentHandler();
|
||||
@@ -65,7 +88,7 @@ class TagsViewCtrl extends PureViewCtrl {
|
||||
this.loadPreferences();
|
||||
this.beginStreamingItems();
|
||||
const smartTags = this.application.getSmartTags();
|
||||
this.setState({
|
||||
this.setTagState({
|
||||
smartTags: smartTags,
|
||||
});
|
||||
this.selectTag(smartTags[0]);
|
||||
@@ -92,23 +115,25 @@ class TagsViewCtrl extends PureViewCtrl {
|
||||
this.application.streamItems(
|
||||
ContentType.Tag,
|
||||
async (items) => {
|
||||
await this.setState({
|
||||
await this.setTagState({
|
||||
tags: this.getMappedTags(),
|
||||
smartTags: this.application.getSmartTags(),
|
||||
});
|
||||
this.reloadTitles(items as SNTag[]);
|
||||
this.reloadNoteCounts();
|
||||
if (this.state.selectedTag) {
|
||||
if (this.getState().selectedTag) {
|
||||
/** If the selected tag has been deleted, revert to All view. */
|
||||
const matchingTag = items.find((tag) => {
|
||||
return tag.uuid === this.state.selectedTag.uuid;
|
||||
});
|
||||
if (!matchingTag || matchingTag.deleted) {
|
||||
this.selectTag(this.state.smartTags[0]);
|
||||
} else {
|
||||
this.setState({
|
||||
selectedTag: matchingTag
|
||||
})
|
||||
return tag.uuid === this.getState().selectedTag.uuid;
|
||||
}) as SNTag;
|
||||
if (matchingTag) {
|
||||
if (matchingTag.deleted) {
|
||||
this.selectTag(this.getState().smartTags[0]);
|
||||
} else {
|
||||
this.setTagState({
|
||||
selectedTag: matchingTag
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,7 +141,7 @@ class TagsViewCtrl extends PureViewCtrl {
|
||||
}
|
||||
|
||||
reloadTitles(tags: Array<SNTag | SNSmartTag>) {
|
||||
for(const tag of tags) {
|
||||
for (const tag of tags) {
|
||||
this.titles[tag.uuid] = tag.title;
|
||||
}
|
||||
}
|
||||
@@ -126,7 +151,7 @@ class TagsViewCtrl extends PureViewCtrl {
|
||||
if (eventName === AppStateEvent.PreferencesChanged) {
|
||||
this.loadPreferences();
|
||||
} else if (eventName === AppStateEvent.TagChanged) {
|
||||
this.setState({
|
||||
this.setTagState({
|
||||
selectedTag: this.application.getAppState().getSelectedTag()
|
||||
});
|
||||
}
|
||||
@@ -149,11 +174,11 @@ class TagsViewCtrl extends PureViewCtrl {
|
||||
|
||||
reloadNoteCounts() {
|
||||
let allTags: Array<SNTag | SNSmartTag> = [];
|
||||
if (this.state.tags) {
|
||||
allTags = allTags.concat(this.state.tags);
|
||||
if (this.getState().tags) {
|
||||
allTags = allTags.concat(this.getState().tags);
|
||||
}
|
||||
if (this.state.smartTags) {
|
||||
allTags = allTags.concat(this.state.smartTags);
|
||||
if (this.getState().smartTags) {
|
||||
allTags = allTags.concat(this.getState().smartTags);
|
||||
}
|
||||
const noteCounts: NoteCounts = {};
|
||||
for (const tag of allTags) {
|
||||
@@ -168,7 +193,7 @@ class TagsViewCtrl extends PureViewCtrl {
|
||||
noteCounts[tag.uuid] = notes.length;
|
||||
}
|
||||
}
|
||||
this.setState({
|
||||
this.setTagState({
|
||||
noteCounts: noteCounts
|
||||
});
|
||||
}
|
||||
@@ -232,7 +257,7 @@ class TagsViewCtrl extends PureViewCtrl {
|
||||
});
|
||||
}
|
||||
} else if (action === ComponentAction.ClearSelection) {
|
||||
this.selectTag(this.state.smartTags[0]);
|
||||
this.selectTag(this.getState().smartTags[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -248,77 +273,91 @@ class TagsViewCtrl extends PureViewCtrl {
|
||||
}
|
||||
|
||||
async clickedAddNewTag() {
|
||||
if (this.state.editingTag) {
|
||||
if (this.getState().editingTag) {
|
||||
return;
|
||||
}
|
||||
const newTag = await this.application.createTemplateItem(
|
||||
ContentType.Tag
|
||||
);
|
||||
this.setState({
|
||||
tags: [newTag].concat(this.state.tags),
|
||||
previousTag: this.state.selectedTag,
|
||||
) as SNTag;
|
||||
this.setTagState({
|
||||
tags: [newTag].concat(this.getState().tags),
|
||||
previousTag: this.getState().selectedTag,
|
||||
selectedTag: newTag,
|
||||
editingTag: newTag,
|
||||
newTag: newTag
|
||||
templateTag: newTag
|
||||
});
|
||||
}
|
||||
|
||||
onTagTitleChange(tag: SNTag | SNSmartTag) {
|
||||
this.setState({
|
||||
this.setTagState({
|
||||
editingTag: tag
|
||||
});
|
||||
}
|
||||
|
||||
async saveTag($event: Event, tag: SNTag) {
|
||||
($event.target! as HTMLInputElement).blur();
|
||||
await this.setState({
|
||||
editingTag: null,
|
||||
});
|
||||
if (this.getState().templateTag) {
|
||||
return this.saveNewTag();
|
||||
} else {
|
||||
return this.saveTagRename(tag);
|
||||
}
|
||||
}
|
||||
|
||||
if (!tag.title || tag.title.length === 0) {
|
||||
let newSelectedTag = this.state.selectedTag;
|
||||
if (this.state.editingTag) {
|
||||
this.titles[tag.uuid] = this.editingOriginalName;
|
||||
this.editingOriginalName = undefined;
|
||||
} else if (this.state.newTag) {
|
||||
newSelectedTag = this.state.previousTag;
|
||||
}
|
||||
this.setState({
|
||||
newTag: null,
|
||||
selectedTag: newSelectedTag,
|
||||
tags: this.getMappedTags()
|
||||
});
|
||||
async saveTagRename(tag: SNTag) {
|
||||
const newTitle = this.titles[tag.uuid] || '';
|
||||
if (newTitle.length === 0) {
|
||||
this.titles[tag.uuid] = this.editingOriginalName;
|
||||
this.editingOriginalName = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
this.editingOriginalName = undefined;
|
||||
|
||||
const matchingTag = this.application.findTagByTitle(tag.title);
|
||||
const alreadyExists = matchingTag && matchingTag !== tag;
|
||||
if (this.state.newTag === tag && alreadyExists) {
|
||||
const existingTag = this.application.findTagByTitle(newTitle);
|
||||
if (existingTag && existingTag.uuid !== tag.uuid) {
|
||||
this.application.alertService!.alert(
|
||||
"A tag with this name already exists."
|
||||
);
|
||||
this.setState({
|
||||
newTag: null,
|
||||
tags: this.getMappedTags(),
|
||||
selectedTag: this.state.previousTag
|
||||
return;
|
||||
};
|
||||
await this.application.changeAndSaveItem(tag.uuid, (mutator) => {
|
||||
const tagMutator = mutator as TagMutator;
|
||||
tagMutator.title = newTitle;
|
||||
});
|
||||
await this.setTagState({
|
||||
editingTag: undefined
|
||||
});
|
||||
}
|
||||
|
||||
async saveNewTag() {
|
||||
const newTag = this.getState().templateTag!;
|
||||
const newTitle = this.titles[newTag.uuid] || '';
|
||||
if (newTitle.length === 0) {
|
||||
await this.setTagState({
|
||||
templateTag: undefined
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.application.changeAndSaveItem(tag.uuid, (mutator) => {
|
||||
const tagMutator = mutator as TagMutator;
|
||||
tagMutator.title = this.titles[tag.uuid]!;
|
||||
const existingTag = this.application.findTagByTitle(newTitle);
|
||||
if (existingTag) {
|
||||
this.application.alertService!.alert(
|
||||
"A tag with this name already exists."
|
||||
);
|
||||
return;
|
||||
};
|
||||
const insertedTag = await this.application.insertItem(newTag);
|
||||
const changedTag = await this.application.changeItem(insertedTag.uuid, (m) => {
|
||||
const mutator = m as TagMutator;
|
||||
mutator.title = newTitle
|
||||
});
|
||||
this.selectTag(tag);
|
||||
this.setState({
|
||||
newTag: null
|
||||
await this.setTagState({
|
||||
templateTag: undefined,
|
||||
editingTag: undefined
|
||||
});
|
||||
this.selectTag(changedTag as SNTag);
|
||||
await this.application.saveItem(changedTag!.uuid);
|
||||
}
|
||||
|
||||
async selectedRenameTag(tag: SNTag) {
|
||||
this.editingOriginalName = tag.title;
|
||||
await this.setState({
|
||||
await this.setTagState({
|
||||
editingTag: tag
|
||||
});
|
||||
document.getElementById('tag-' + tag.uuid)!.focus();
|
||||
@@ -337,7 +376,7 @@ class TagsViewCtrl extends PureViewCtrl {
|
||||
() => {
|
||||
/* On confirm */
|
||||
this.application.deleteItem(tag);
|
||||
this.selectTag(this.state.smartTags[0]);
|
||||
this.selectTag(this.getState().smartTags[0]);
|
||||
},
|
||||
undefined,
|
||||
true,
|
||||
|
||||
Reference in New Issue
Block a user