Tags fixes

This commit is contained in:
Mo Bitar
2020-04-14 18:16:46 -05:00
parent 9cf99896a5
commit d91b7bc449
8 changed files with 483 additions and 277 deletions

View File

@@ -166,7 +166,6 @@ export class AppState {
}
}
}
if (this.selectedTag) {
const matchingTag = items.find((candidate) => candidate.uuid === this.selectedTag!.uuid);
if (matchingTag) {

View File

@@ -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,

View File

@@ -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();
}

View File

@@ -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();

View File

@@ -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'
)

View File

@@ -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,