Revert "feat: allows duplicate names in tags folder & smart tags (#792)"
This reverts commit a165fa9b4e.
This commit is contained in:
@@ -21,9 +21,6 @@ export const AutocompleteTagResult = observer(
|
|||||||
|
|
||||||
const tagResultRef = useRef<HTMLButtonElement>(null);
|
const tagResultRef = useRef<HTMLButtonElement>(null);
|
||||||
|
|
||||||
const title = tagResult.title;
|
|
||||||
const prefixTitle = appState.noteTags.getPrefixTitle(tagResult);
|
|
||||||
|
|
||||||
const onTagOptionClick = async (tag: SNTag) => {
|
const onTagOptionClick = async (tag: SNTag) => {
|
||||||
await appState.noteTags.addTagToActiveNote(tag);
|
await appState.noteTags.addTagToActiveNote(tag);
|
||||||
appState.noteTags.clearAutocompleteSearch();
|
appState.noteTags.clearAutocompleteSearch();
|
||||||
@@ -89,10 +86,9 @@ export const AutocompleteTagResult = observer(
|
|||||||
>
|
>
|
||||||
<Icon type="hashtag" className="color-neutral mr-2 min-h-5 min-w-5" />
|
<Icon type="hashtag" className="color-neutral mr-2 min-h-5 min-w-5" />
|
||||||
<span className="whitespace-nowrap overflow-hidden overflow-ellipsis">
|
<span className="whitespace-nowrap overflow-hidden overflow-ellipsis">
|
||||||
{prefixTitle && <span className="grey-2">{prefixTitle}</span>}
|
|
||||||
{autocompleteSearchQuery === ''
|
{autocompleteSearchQuery === ''
|
||||||
? title
|
? tagResult.title
|
||||||
: title
|
: tagResult.title
|
||||||
.split(new RegExp(`(${autocompleteSearchQuery})`, 'gi'))
|
.split(new RegExp(`(${autocompleteSearchQuery})`, 'gi'))
|
||||||
.map((substring, index) => (
|
.map((substring, index) => (
|
||||||
<span
|
<span
|
||||||
|
|||||||
@@ -10,9 +10,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const NoteTag = observer(({ appState, tag }: Props) => {
|
export const NoteTag = observer(({ appState, tag }: Props) => {
|
||||||
const noteTags = appState.noteTags;
|
const { autocompleteInputFocused, focusedTagUuid, tags } = appState.noteTags;
|
||||||
|
|
||||||
const { autocompleteInputFocused, focusedTagUuid, tags } = noteTags;
|
|
||||||
|
|
||||||
const [showDeleteButton, setShowDeleteButton] = useState(false);
|
const [showDeleteButton, setShowDeleteButton] = useState(false);
|
||||||
const [tagClicked, setTagClicked] = useState(false);
|
const [tagClicked, setTagClicked] = useState(false);
|
||||||
@@ -20,10 +18,6 @@ export const NoteTag = observer(({ appState, tag }: Props) => {
|
|||||||
|
|
||||||
const tagRef = useRef<HTMLButtonElement>(null);
|
const tagRef = useRef<HTMLButtonElement>(null);
|
||||||
|
|
||||||
const title = tag.title;
|
|
||||||
const prefixTitle = noteTags.getPrefixTitle(tag);
|
|
||||||
const longTitle = noteTags.getLongTitle(tag);
|
|
||||||
|
|
||||||
const deleteTag = () => {
|
const deleteTag = () => {
|
||||||
appState.noteTags.focusPreviousTag(tag);
|
appState.noteTags.focusPreviousTag(tag);
|
||||||
appState.noteTags.removeTagFromActiveNote(tag);
|
appState.noteTags.removeTagFromActiveNote(tag);
|
||||||
@@ -103,12 +97,10 @@ export const NoteTag = observer(({ appState, tag }: Props) => {
|
|||||||
onFocus={onFocus}
|
onFocus={onFocus}
|
||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
tabIndex={getTabIndex()}
|
tabIndex={getTabIndex()}
|
||||||
title={longTitle}
|
|
||||||
>
|
>
|
||||||
<Icon type="hashtag" className="sn-icon--small color-info mr-1" />
|
<Icon type="hashtag" className="sn-icon--small color-info mr-1" />
|
||||||
<span className="whitespace-nowrap overflow-hidden overflow-ellipsis max-w-290px">
|
<span className="whitespace-nowrap overflow-hidden overflow-ellipsis max-w-290px">
|
||||||
{prefixTitle && <span className="color-grey-1">{prefixTitle}</span>}
|
{tag.title}
|
||||||
{title}
|
|
||||||
</span>
|
</span>
|
||||||
{showDeleteButton && (
|
{showDeleteButton && (
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ContentType, SNNote, SNTag, UuidString } from '@standardnotes/snjs';
|
import { SNNote, ContentType, SNTag, UuidString } from '@standardnotes/snjs';
|
||||||
import { action, computed, makeObservable, observable } from 'mobx';
|
import { action, computed, makeObservable, observable } from 'mobx';
|
||||||
import { WebApplication } from '../application';
|
import { WebApplication } from '../application';
|
||||||
import { AppState } from './app_state';
|
import { AppState } from './app_state';
|
||||||
@@ -194,41 +194,4 @@ export class NoteTagsState {
|
|||||||
this.reloadTags();
|
this.reloadTags();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getSortedTagsForNote(note: SNNote): SNTag[] {
|
|
||||||
const tags = this.application.getSortedTagsForNote(note);
|
|
||||||
|
|
||||||
const sortFunction = (tagA: SNTag, tagB: SNTag): number => {
|
|
||||||
const a = this.getLongTitle(tagA);
|
|
||||||
const b = this.getLongTitle(tagB);
|
|
||||||
|
|
||||||
if (a < b) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (b > a) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
return tags.sort(sortFunction);
|
|
||||||
}
|
|
||||||
|
|
||||||
getPrefixTitle(tag: SNTag): string | undefined {
|
|
||||||
const hierarchy = this.application.getTagParentChain(tag);
|
|
||||||
|
|
||||||
if (hierarchy.length === 0) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const prefixTitle = hierarchy.map((tag) => tag.title).join('/');
|
|
||||||
return `${prefixTitle}/`;
|
|
||||||
}
|
|
||||||
|
|
||||||
getLongTitle(tag: SNTag): string {
|
|
||||||
const hierarchy = this.application.getTagParentChain(tag);
|
|
||||||
const tags = [...hierarchy, tag];
|
|
||||||
const longTitle = tags.map((tag) => tag.title).join('/');
|
|
||||||
return longTitle;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
ComponentAction,
|
ComponentAction,
|
||||||
ContentType,
|
ContentType,
|
||||||
MessageData,
|
MessageData,
|
||||||
SNApplication,
|
|
||||||
SNSmartTag,
|
SNSmartTag,
|
||||||
SNTag,
|
SNTag,
|
||||||
TagMutator,
|
TagMutator,
|
||||||
@@ -23,48 +22,6 @@ import { FeaturesState, SMART_TAGS_FEATURE_NAME } from './features_state';
|
|||||||
|
|
||||||
type AnyTag = SNTag | SNSmartTag;
|
type AnyTag = SNTag | SNSmartTag;
|
||||||
|
|
||||||
const rootTags = (application: SNApplication): SNTag[] => {
|
|
||||||
const hasNoParent = (tag: SNTag) => !application.getTagParent(tag);
|
|
||||||
|
|
||||||
const allTags = application.getDisplayableItems(ContentType.Tag) as SNTag[];
|
|
||||||
const rootTags = allTags.filter(hasNoParent);
|
|
||||||
|
|
||||||
return rootTags;
|
|
||||||
};
|
|
||||||
|
|
||||||
const tagSiblings = (application: SNApplication, tag: SNTag): SNTag[] => {
|
|
||||||
const withoutCurrentTag = (tags: SNTag[]) =>
|
|
||||||
tags.filter((other) => other.uuid !== tag.uuid);
|
|
||||||
|
|
||||||
const isTemplateTag = application.isTemplateItem(tag);
|
|
||||||
const parentTag = !isTemplateTag && application.getTagParent(tag);
|
|
||||||
|
|
||||||
if (parentTag) {
|
|
||||||
const siblingsAndTag = application.getTagChildren(parentTag);
|
|
||||||
return withoutCurrentTag(siblingsAndTag);
|
|
||||||
}
|
|
||||||
|
|
||||||
return withoutCurrentTag(rootTags(application));
|
|
||||||
};
|
|
||||||
|
|
||||||
const isValidFutureSiblings = (
|
|
||||||
application: SNApplication,
|
|
||||||
futureSiblings: SNTag[],
|
|
||||||
tag: SNTag
|
|
||||||
): boolean => {
|
|
||||||
const siblingWithSameName = futureSiblings.find(
|
|
||||||
(otherTag) => otherTag.title === tag.title
|
|
||||||
);
|
|
||||||
|
|
||||||
if (siblingWithSameName) {
|
|
||||||
application.alertService?.alert(
|
|
||||||
`A tag with the name ${tag.title} already exists at this destination. Please rename this tag before moving and try again.`
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class TagsState {
|
export class TagsState {
|
||||||
tags: SNTag[] = [];
|
tags: SNTag[] = [];
|
||||||
smartTags: SNSmartTag[] = [];
|
smartTags: SNSmartTag[] = [];
|
||||||
@@ -187,27 +144,12 @@ export class TagsState {
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const tag = this.application.findItem(tagUuid) as SNTag;
|
const tag = this.application.findItem(tagUuid) as SNTag;
|
||||||
|
|
||||||
const currentParent = this.application.getTagParent(tag);
|
|
||||||
const currentParentUuid = currentParent?.parentId;
|
|
||||||
|
|
||||||
if (currentParentUuid === parentUuid) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parent =
|
const parent =
|
||||||
parentUuid && (this.application.findItem(parentUuid) as SNTag);
|
parentUuid && (this.application.findItem(parentUuid) as SNTag);
|
||||||
|
|
||||||
if (!parent) {
|
if (!parent) {
|
||||||
const futureSiblings = rootTags(this.application);
|
|
||||||
if (!isValidFutureSiblings(this.application, futureSiblings, tag)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await this.application.unsetTagParent(tag);
|
await this.application.unsetTagParent(tag);
|
||||||
} else {
|
} else {
|
||||||
const futureSiblings = this.application.getTagChildren(parent);
|
|
||||||
if (!isValidFutureSiblings(this.application, futureSiblings, tag)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await this.application.setTagParent(parent, tag);
|
await this.application.setTagParent(parent, tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,11 +249,7 @@ export class TagsState {
|
|||||||
const hasEmptyTitle = newTitle.length === 0;
|
const hasEmptyTitle = newTitle.length === 0;
|
||||||
const hasNotChangedTitle = newTitle === tag.title;
|
const hasNotChangedTitle = newTitle === tag.title;
|
||||||
const isTemplateChange = this.application.isTemplateItem(tag);
|
const isTemplateChange = this.application.isTemplateItem(tag);
|
||||||
|
const hasDuplicatedTitle = !!this.application.findTagByTitle(newTitle);
|
||||||
const siblings = tagSiblings(this.application, tag);
|
|
||||||
const hasDuplicatedTitle = siblings.some(
|
|
||||||
(other) => other.title.toLowerCase() === newTitle.toLowerCase()
|
|
||||||
);
|
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.editing_ = undefined;
|
this.editing_ = undefined;
|
||||||
|
|||||||
Reference in New Issue
Block a user