diff --git a/app/assets/icons/ic-add.svg b/app/assets/icons/ic-add.svg new file mode 100644 index 00000000000..d92c119a2c2 --- /dev/null +++ b/app/assets/icons/ic-add.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/ic-folder.svg b/app/assets/icons/ic-folder.svg new file mode 100644 index 00000000000..db3aea31e6d --- /dev/null +++ b/app/assets/icons/ic-folder.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/ic-link-off.svg b/app/assets/icons/ic-link-off.svg new file mode 100644 index 00000000000..3b701266cd7 --- /dev/null +++ b/app/assets/icons/ic-link-off.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/ic-list-bulleted.svg b/app/assets/icons/ic-list-bulleted.svg new file mode 100644 index 00000000000..98c5a43330a --- /dev/null +++ b/app/assets/icons/ic-list-bulleted.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/ic-menu-arrow-down-alt.svg b/app/assets/icons/ic-menu-arrow-down-alt.svg new file mode 100644 index 00000000000..8e150eb1590 --- /dev/null +++ b/app/assets/icons/ic-menu-arrow-down-alt.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/ic-menu-arrow-right.svg b/app/assets/icons/ic-menu-arrow-right.svg new file mode 100644 index 00000000000..ba5890ea7f5 --- /dev/null +++ b/app/assets/icons/ic-menu-arrow-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/javascripts/app.ts b/app/assets/javascripts/app.ts index 6202a935394..0392d0c7613 100644 --- a/app/assets/javascripts/app.ts +++ b/app/assets/javascripts/app.ts @@ -10,6 +10,13 @@ declare global { _plans_url?: string; // eslint-disable-next-line camelcase _dashboard_url?: string; + // eslint-disable-next-line camelcase + _default_sync_server: string; + // eslint-disable-next-line camelcase + _enable_unfinished_features: boolean; + // eslint-disable-next-line camelcase + _websocket_url: string; + startApplication?: StartApplication; } } @@ -82,6 +89,7 @@ import { ComponentViewDirective } from '@/components/ComponentView'; import { TagsListDirective } from '@/components/TagsList'; import { NotesViewDirective } from './components/NotesView'; import { PinNoteButtonDirective } from '@/components/PinNoteButton'; +import { TagsSectionDirective } from './components/Tags/TagsSection'; function reloadHiddenFirefoxTab(): boolean { /** @@ -182,7 +190,8 @@ const startApplication: StartApplication = async function startApplication( .directive('notesListOptionsMenu', NotesListOptionsDirective) .directive('icon', IconDirective) .directive('noteTagsContainer', NoteTagsContainerDirective) - .directive('tags', TagsListDirective) + .directive('tagsList', TagsListDirective) + .directive('tagsSection', TagsSectionDirective) .directive('preferences', PreferencesDirective) .directive('purchaseFlow', PurchaseFlowDirective) .directive('notesView', NotesViewDirective) @@ -216,11 +225,11 @@ const startApplication: StartApplication = async function startApplication( if (IsWebPlatform) { startApplication( - (window as any)._default_sync_server as string, + window._default_sync_server, new BrowserBridge(AppVersion), - (window as any)._enable_unfinished_features as boolean, - (window as any)._websocket_url as string + window._enable_unfinished_features, + window._websocket_url ); } else { - (window as any).startApplication = startApplication; + window.startApplication = startApplication; } diff --git a/app/assets/javascripts/components/Icon.tsx b/app/assets/javascripts/components/Icon.tsx index d83e33ab606..96eee992f8e 100644 --- a/app/assets/javascripts/components/Icon.tsx +++ b/app/assets/javascripts/components/Icon.tsx @@ -24,8 +24,10 @@ import MarkdownIcon from '../../icons/ic-markdown.svg'; import CodeIcon from '../../icons/ic-code.svg'; import AccessibilityIcon from '../../icons/ic-accessibility.svg'; +import AddIcon from '../../icons/ic-add.svg'; import HelpIcon from '../../icons/ic-help.svg'; import KeyboardIcon from '../../icons/ic-keyboard.svg'; +import ListBulleted from '../../icons/ic-list-bulleted.svg'; import ListedIcon from '../../icons/ic-listed.svg'; import SecurityIcon from '../../icons/ic-security.svg'; import SettingsIcon from '../../icons/ic-settings.svg'; @@ -53,11 +55,17 @@ import LockIcon from '../../icons/ic-lock.svg'; import ArrowsSortUpIcon from '../../icons/ic-arrows-sort-up.svg'; import ArrowsSortDownIcon from '../../icons/ic-arrows-sort-down.svg'; import WindowIcon from '../../icons/ic-window.svg'; +import LinkOffIcon from '../../icons/ic-link-off.svg'; + +import MenuArrowDownAlt from '../../icons/ic-menu-arrow-down-alt.svg'; +import MenuArrowRight from '../../icons/ic-menu-arrow-right.svg'; import { toDirective } from './utils'; import { FunctionalComponent } from 'preact'; const ICONS = { + 'menu-arrow-down-alt': MenuArrowDownAlt, + 'menu-arrow-right': MenuArrowRight, 'arrows-sort-up': ArrowsSortUpIcon, 'arrows-sort-down': ArrowsSortDownIcon, lock: LockIcon, @@ -94,8 +102,11 @@ const ICONS = { more: MoreIcon, tune: TuneIcon, accessibility: AccessibilityIcon, + add: AddIcon, help: HelpIcon, keyboard: KeyboardIcon, + 'list-bulleted': ListBulleted, + 'link-off': LinkOffIcon, listed: ListedIcon, security: SecurityIcon, settings: SettingsIcon, @@ -111,7 +122,7 @@ const ICONS = { 'menu-arrow-down': MenuArrowDownIcon, 'menu-close': MenuCloseIcon, window: WindowIcon, - 'premium-feature': PremiumFeatureIcon + 'premium-feature': PremiumFeatureIcon, }; export type IconType = keyof typeof ICONS; diff --git a/app/assets/javascripts/components/Premium/index.ts b/app/assets/javascripts/components/Premium/index.ts new file mode 100644 index 00000000000..d24a2c7daac --- /dev/null +++ b/app/assets/javascripts/components/Premium/index.ts @@ -0,0 +1 @@ +export { usePremiumModal, PremiumModalProvider } from './usePremiumModal'; diff --git a/app/assets/javascripts/components/Premium/usePremiumModal.tsx b/app/assets/javascripts/components/Premium/usePremiumModal.tsx new file mode 100644 index 00000000000..d52b1582384 --- /dev/null +++ b/app/assets/javascripts/components/Premium/usePremiumModal.tsx @@ -0,0 +1,49 @@ +import { FunctionalComponent } from 'preact'; +import { useCallback, useContext, useState } from 'preact/hooks'; +import { createContext } from 'react'; +import { PremiumFeaturesModal } from '../PremiumFeaturesModal'; + +type PremiumModalContextData = { + activate: (featureName: string) => void; +}; + +const PremiumModalContext = createContext(null); + +const PremiumModalProvider_ = PremiumModalContext.Provider; + +export const usePremiumModal = (): PremiumModalContextData => { + const value = useContext(PremiumModalContext); + + if (!value) { + throw new Error('invalid PremiumModal context'); + } + + return value; +}; + +export const PremiumModalProvider: FunctionalComponent = ({ children }) => { + const [featureName, setFeatureName] = useState(null); + + const activate = setFeatureName; + + const closeModal = useCallback(() => { + setFeatureName(null); + }, [setFeatureName]); + + const showModal = !!featureName; + + return ( + <> + {showModal && ( + + )} + + {children} + + + ); +}; diff --git a/app/assets/javascripts/components/QuickSettingsMenu/FocusModeSwitch.tsx b/app/assets/javascripts/components/QuickSettingsMenu/FocusModeSwitch.tsx index 718e2b951ca..4786dd00ec5 100644 --- a/app/assets/javascripts/components/QuickSettingsMenu/FocusModeSwitch.tsx +++ b/app/assets/javascripts/components/QuickSettingsMenu/FocusModeSwitch.tsx @@ -2,7 +2,7 @@ import { WebApplication } from '@/ui_models/application'; import { FeatureIdentifier } from '@standardnotes/features'; import { FeatureStatus } from '@standardnotes/snjs'; import { FunctionComponent } from 'preact'; -import { useState } from 'preact/hooks'; +import { useCallback, useState } from 'preact/hooks'; import { JSXInternal } from 'preact/src/jsx'; import { Icon } from '../Icon'; import { PremiumFeaturesModal } from '../PremiumFeaturesModal'; @@ -10,46 +10,48 @@ import { Switch } from '../Switch'; type Props = { application: WebApplication; - closeQuickSettingsMenu: () => void; - focusModeEnabled: boolean; - setFocusModeEnabled: (enabled: boolean) => void; + onToggle: (value: boolean) => void; + onClose: () => void; + isEnabled: boolean; }; export const FocusModeSwitch: FunctionComponent = ({ application, - closeQuickSettingsMenu, - focusModeEnabled, - setFocusModeEnabled, + onToggle, + onClose, + isEnabled, }) => { const [showUpgradeModal, setShowUpgradeModal] = useState(false); - const isEntitledToFocusMode = + const isEntitled = application.getFeatureStatus(FeatureIdentifier.FocusMode) === FeatureStatus.Entitled; - const toggleFocusMode = ( - e: JSXInternal.TargetedMouseEvent - ) => { - e.preventDefault(); - if (isEntitledToFocusMode) { - setFocusModeEnabled(!focusModeEnabled); - closeQuickSettingsMenu(); - } else { - setShowUpgradeModal(true); - } - }; + const toggle = useCallback( + (e: JSXInternal.TargetedMouseEvent) => { + e.preventDefault(); + + if (isEntitled) { + onToggle(!isEnabled); + onClose(); + } else { + setShowUpgradeModal(true); + } + }, + [isEntitled, isEnabled, onToggle, setShowUpgradeModal, onClose] + ); return ( <>