feat: enable folders by default and remove from experimental features

This commit is contained in:
Mo
2022-02-01 12:31:37 -06:00
parent a2d775235e
commit 70f4dd6353
16 changed files with 127 additions and 265 deletions

View File

@@ -21,8 +21,6 @@ type Props = {
export const Navigation: FunctionComponent<Props> = observer(
({ application }) => {
const appState = useMemo(() => application.getAppState(), [application]);
const enableNativeSmartTagsFeature =
appState.features.enableNativeSmartTagsFeature;
const [ref, setRef] = useState<HTMLDivElement | null>();
const [panelWidth, setPanelWidth] = useState<number>(0);
@@ -39,10 +37,6 @@ export const Navigation: FunctionComponent<Props> = observer(
};
}, [application]);
const onCreateNewTag = useCallback(() => {
appState.tags.createNewTemplate();
}, [appState]);
const panelResizeFinishCallback: ResizeFinishCallback = useCallback(
(width, _lastLeft, _isMaxWidth, isCollapsed) => {
application.setPreference(PrefKey.TagsPanelWidth, width);
@@ -70,17 +64,6 @@ export const Navigation: FunctionComponent<Props> = observer(
<div className="sk-h3 title">
<span className="sk-bold">Views</span>
</div>
{!enableNativeSmartTagsFeature && (
<div
className="sk-button sk-secondary-contrast wide"
onClick={onCreateNewTag}
title="Create a new tag"
>
<div className="sk-label">
<i className="icon ion-plus add-button" />
</div>
</div>
)}
</div>
</div>
<div className="scrollable">

View File

@@ -164,7 +164,7 @@ export const NotesView: FunctionComponent<Props> = observer(
>
<div className="content">
<div id="notes-title-bar" className="section-title-bar">
<div className="p-4">
<div id="notes-title-bar-container">
<div className="section-title-bar-header">
<div className="sk-h2 font-semibold title">{panelTitle}</div>
<button

View File

@@ -51,7 +51,7 @@ export const PremiumFeaturesModal: FunctionalComponent<Props> = ({
<PremiumIllustration className="mb-2" />
</div>
<div className="text-lg text-center font-bold mb-1">
Enable premium features
Enable Premium Features
</div>
</AlertDialogLabel>
<AlertDialogDescription className="text-sm text-center color-grey-1 px-4.5 mb-2">
@@ -65,7 +65,7 @@ export const PremiumFeaturesModal: FunctionalComponent<Props> = ({
className="w-full rounded no-border py-2 font-bold bg-info color-info-contrast hover:brightness-130 focus:brightness-130 cursor-pointer"
ref={plansButtonRef}
>
See our plans
See Plans
</button>
</div>
</div>

View File

@@ -11,45 +11,38 @@ type Props = {
featuresState: FeaturesState;
};
export const RootTagDropZone: React.FC<Props> = observer(
({ tagsState, featuresState }) => {
const premiumModal = usePremiumModal();
const isNativeFoldersEnabled = featuresState.enableNativeFoldersFeature;
export const RootTagDropZone: React.FC<Props> = observer(({ tagsState }) => {
const premiumModal = usePremiumModal();
const [{ isOver, canDrop }, dropRef] = useDrop<DropItem, void, DropProps>(
() => ({
accept: ItemTypes.TAG,
canDrop: (item) => {
return tagsState.hasParent(item.uuid);
},
drop: (item) => {
tagsState.assignParent(item.uuid, undefined);
},
collect: (monitor) => ({
isOver: !!monitor.isOver(),
canDrop: !!monitor.canDrop(),
}),
const [{ isOver, canDrop }, dropRef] = useDrop<DropItem, void, DropProps>(
() => ({
accept: ItemTypes.TAG,
canDrop: (item) => {
return tagsState.hasParent(item.uuid);
},
drop: (item) => {
tagsState.assignParent(item.uuid, undefined);
},
collect: (monitor) => ({
isOver: !!monitor.isOver(),
canDrop: !!monitor.canDrop(),
}),
[tagsState, premiumModal]
);
}),
[tagsState, premiumModal]
);
if (!isNativeFoldersEnabled) {
return null;
}
return (
<div
ref={dropRef}
className={`root-drop ${canDrop ? 'active' : ''} ${
isOver ? 'is-drag-over' : ''
}`}
>
<Icon className="color-neutral" type="link-off" />
<p className="content">
Move the tag here to <br />
remove it from its folder.
</p>
</div>
);
}
);
return (
<div
ref={dropRef}
className={`root-drop ${canDrop ? 'active' : ''} ${
isOver ? 'is-drag-over' : ''
}`}
>
<Icon className="color-neutral" type="link-off" />
<p className="content">
Move the tag here to <br />
remove it from its folder.
</p>
</div>
);
});

View File

@@ -37,7 +37,6 @@ export const SmartTagsListItem: FunctionComponent<Props> = observer(
const level = 0;
const isSelected = tagsState.selected === tag;
const isEditing = tagsState.editingTag === tag;
const isSmartTagsEnabled = features.enableNativeSmartTagsFeature;
useEffect(() => {
setTitle(tag.title || '');
@@ -88,7 +87,7 @@ export const SmartTagsListItem: FunctionComponent<Props> = observer(
tagsState.remove(tag);
}, [tagsState, tag]);
const isFaded = !isSmartTagsEnabled && !tag.isAllTag;
const isFaded = !tag.isAllTag;
const iconType = smartTagIconType(tag);
return (
@@ -104,14 +103,12 @@ export const SmartTagsListItem: FunctionComponent<Props> = observer(
>
{!tag.errorDecrypting ? (
<div className="tag-info">
{isSmartTagsEnabled && (
<div className={`tag-icon mr-1`}>
<Icon
type={iconType}
className={`${isSelected ? 'color-info' : 'color-neutral'}`}
/>
</div>
)}
<div className={`tag-icon mr-1`}>
<Icon
type={iconType}
className={`${isSelected ? 'color-info' : 'color-neutral'}`}
/>
</div>
<input
className={`title ${isEditing ? 'editing' : ''}`}
id={`react-tag-${tag.uuid}`}

View File

@@ -22,7 +22,7 @@ export const TagsList: FunctionComponent<Props> = observer(({ appState }) => {
<DndProvider backend={backend}>
{allTags.length === 0 ? (
<div className="no-tags-placeholder">
No tags. Create one using the add button above.
No tags or folders. Create one using the add button above.
</div>
) : (
<>

View File

@@ -37,7 +37,6 @@ export const TagsListItem: FunctionComponent<Props> = observer(
const hasChildren = childrenTags.length > 0;
const hasFolders = features.hasFolders;
const isNativeFoldersEnabled = features.enableNativeFoldersFeature;
const hasAtLeastOneFolder = tagsState.hasAtLeastOneFolder;
const premiumModal = usePremiumModal();
@@ -114,13 +113,13 @@ export const TagsListItem: FunctionComponent<Props> = observer(
type: ItemTypes.TAG,
item: { uuid: tag.uuid },
canDrag: () => {
return isNativeFoldersEnabled;
return true;
},
collect: (monitor) => ({
isDragging: !!monitor.isDragging(),
}),
}),
[tag, isNativeFoldersEnabled]
[tag]
);
const [{ isOver, canDrop }, dropRef] = useDrop<DropItem, void, DropProps>(
@@ -160,7 +159,7 @@ export const TagsListItem: FunctionComponent<Props> = observer(
>
{!tag.errorDecrypting ? (
<div className="tag-info" title={title} ref={dropRef}>
{isNativeFoldersEnabled && hasAtLeastOneFolder && (
{hasAtLeastOneFolder && (
<div
className={`tag-fold ${showChildren ? 'opened' : 'closed'}`}
onClick={hasChildren ? toggleChildren : undefined}
@@ -173,12 +172,7 @@ export const TagsListItem: FunctionComponent<Props> = observer(
/>
</div>
)}
<div
className={`tag-icon ${
isNativeFoldersEnabled ? 'draggable' : ''
} mr-1`}
ref={dragRef}
>
<div className={`tag-icon ${'draggable'} mr-1`} ref={dragRef}>
<Icon
type="hashtag"
className={`${isSelected ? 'color-info' : 'color-neutral'}`}

View File

@@ -1,7 +1,9 @@
import { TagsList } from '@/components/Tags/TagsList';
import { AppState } from '@/ui_models/app_state';
import { ApplicationEvent } from '@/__mocks__/@standardnotes/snjs';
import { observer } from 'mobx-react-lite';
import { FunctionComponent } from 'preact';
import { useCallback, useEffect, useState } from 'preact/hooks';
import { TagsSectionAddButton } from './TagsSectionAddButton';
import { TagsSectionTitle } from './TagsSectionTitle';
@@ -11,11 +13,51 @@ type Props = {
export const TagsSection: FunctionComponent<Props> = observer(
({ appState }) => {
const [hasMigration, setHasMigration] = useState<boolean>(false);
const checkIfMigrationNeeded = useCallback(() => {
setHasMigration(appState.application.hasTagsNeedingFoldersMigration());
}, [appState.application]);
useEffect(() => {
appState.application.addEventObserver(async (event) => {
const events = [
ApplicationEvent.CompletedInitialSync,
ApplicationEvent.SignedIn,
];
if (events.includes(event)) {
checkIfMigrationNeeded();
}
});
}, [appState.application, checkIfMigrationNeeded]);
const runMigration = useCallback(async () => {
if (
await appState.application.alertService.confirm(
'<i>Introducing native, built-in nested tags without requiring the legacy Folders component.</i><br/></br> ' +
" To get started, we'll need to migrate any tags containing a dot character to the new system.<br/></br> " +
' This migration will convert any tags with dots appearing in their name into a natural' +
' hierarchy that is compatible with the new nested tags feature.' +
' Running this migration will remove any "." characters appearing in tag names.',
'New: Folders to Nested Tags',
'Run Migration'
)
) {
appState.application.migrateTagsToFolders().then(() => {
checkIfMigrationNeeded();
});
}
}, [appState.application, checkIfMigrationNeeded]);
return (
<section>
<div className="section-title-bar">
<div className="section-title-bar-header">
<TagsSectionTitle features={appState.features} />
<TagsSectionTitle
features={appState.features}
hasMigration={hasMigration}
onClickMigration={runMigration}
/>
<TagsSectionAddButton
tags={appState.tags}
features={appState.features}

View File

@@ -10,13 +10,7 @@ type Props = {
};
export const TagsSectionAddButton: FunctionComponent<Props> = observer(
({ tags, features }) => {
const isNativeFoldersEnabled = features.enableNativeFoldersFeature;
if (!isNativeFoldersEnabled) {
return null;
}
({ tags }) => {
return (
<IconButton
focusable={true}

View File

@@ -11,33 +11,32 @@ import { useCallback } from 'preact/hooks';
type Props = {
features: FeaturesState;
hasMigration: boolean;
onClickMigration: () => void;
};
export const TagsSectionTitle: FunctionComponent<Props> = observer(
({ features }) => {
const isNativeFoldersEnabled = features.enableNativeFoldersFeature;
const hasFolders = features.hasFolders;
({ features, hasMigration, onClickMigration }) => {
const entitledToFolders = features.hasFolders;
const modal = usePremiumModal();
const showPremiumAlert = useCallback(() => {
modal.activate(TAG_FOLDERS_FEATURE_NAME);
}, [modal]);
if (!isNativeFoldersEnabled) {
return (
<>
<div className="sk-h3 title">
<span className="sk-bold">Tags</span>
</div>
</>
);
}
if (hasFolders) {
if (entitledToFolders) {
return (
<>
<div className="sk-h3 title">
<span className="sk-bold">Folders</span>
{hasMigration && (
<label
className="ml-1 sk-bold color-info cursor-pointer"
onClick={onClickMigration}
>
Migration Available
</label>
)}
</div>
</>
);
@@ -52,7 +51,7 @@ export const TagsSectionTitle: FunctionComponent<Props> = observer(
className="ml-1 sk-bold color-grey-2 cursor-pointer"
onClick={showPremiumAlert}
>
&middot; Folders
Folders
</label>
</Tooltip>
</div>