From b6cd4bf6bf158d98f74f0221ab7d1271d1791982 Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Tue, 21 Jun 2022 02:05:10 +0530 Subject: [PATCH] feat: add "Files Navigation" experimental feature to Labs (#1125) --- .../javascripts/Application/Application.ts | 3 +- .../Preferences/Panes/General/General.tsx | 2 +- .../Panes/General/{ => Labs}/Labs.tsx | 47 ++++++++++++++----- .../Panes/General/Labs/LabsFeature.tsx | 23 +++++++++ .../src/javascripts/Services/LocalStorage.ts | 20 ++++++++ 5 files changed, 80 insertions(+), 15 deletions(-) rename packages/web/src/javascripts/Components/Preferences/Panes/General/{ => Labs}/Labs.tsx (63%) create mode 100644 packages/web/src/javascripts/Components/Preferences/Panes/General/Labs/LabsFeature.tsx diff --git a/packages/web/src/javascripts/Application/Application.ts b/packages/web/src/javascripts/Application/Application.ts index e3e09011b..76dac1fde 100644 --- a/packages/web/src/javascripts/Application/Application.ts +++ b/packages/web/src/javascripts/Application/Application.ts @@ -26,6 +26,7 @@ import { makeObservable, observable } from 'mobx' import { PanelResizedData } from '@/Types/PanelResizedData' import { WebAppEvent } from './WebAppEvent' import { isDesktopApplication } from '@/Utils' +import { storage, StorageKey } from '@/Services/LocalStorage' type WebServices = { viewControllerManager: ViewControllerManager @@ -62,7 +63,7 @@ export class WebApplication extends SNApplication { defaultHost: defaultSyncServerHost, appVersion: deviceInterface.appVersion, webSocketUrl: webSocketUrl, - supportsFileNavigation: window.enabledUnfinishedFeatures || false, + supportsFileNavigation: storage.get(StorageKey.FilesNavigationEnabled) || false, }) makeObservable(this, { diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/General/General.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/General/General.tsx index 9f530e480..280a0777a 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/General/General.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/General/General.tsx @@ -5,7 +5,7 @@ import { ExtensionsLatestVersions } from '@/Components/Preferences/Panes/Extensi import { observer } from 'mobx-react-lite' import Tools from './Tools' import Defaults from './Defaults' -import LabsPane from './Labs' +import LabsPane from './Labs/Labs' import Advanced from '@/Components/Preferences/Panes/Account/Advanced' import PreferencesPane from '../../PreferencesComponents/PreferencesPane' diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/General/Labs.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/General/Labs/Labs.tsx similarity index 63% rename from packages/web/src/javascripts/Components/Preferences/Panes/General/Labs.tsx rename to packages/web/src/javascripts/Components/Preferences/Panes/General/Labs/Labs.tsx index 93eb66bba..ee77c0bcc 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/General/Labs.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/General/Labs/Labs.tsx @@ -1,12 +1,13 @@ -import Switch from '@/Components/Switch/Switch' -import { Subtitle, Text, Title } from '@/Components/Preferences/PreferencesComponents/Content' +import { Text, Title } from '@/Components/Preferences/PreferencesComponents/Content' import { WebApplication } from '@/Application/Application' import { FeatureIdentifier, FeatureStatus, FindNativeFeature } from '@standardnotes/snjs' import { Fragment, FunctionComponent, useCallback, useEffect, useState } from 'react' import { usePremiumModal } from '@/Hooks/usePremiumModal' +import PreferencesGroup from '../../../PreferencesComponents/PreferencesGroup' +import PreferencesSegment from '../../../PreferencesComponents/PreferencesSegment' +import LabsFeature from './LabsFeature' +import { StorageKey, useLocalStorageItem } from '@/Services/LocalStorage' import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator' -import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup' -import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment' type ExperimentalFeatureItem = { identifier: FeatureIdentifier @@ -17,11 +18,14 @@ type ExperimentalFeatureItem = { } type Props = { - application: WebApplication + application: { + features: WebApplication['features'] + } } const LabsPane: FunctionComponent = ({ application }) => { const [experimentalFeatures, setExperimentalFeatures] = useState([]) + const [isFilesNavigationEnabled, setFilesNavigation] = useLocalStorageItem(StorageKey.FilesNavigationEnabled) const reloadExperimentalFeatures = useCallback(() => { const experimentalFeatures = application.features.getExperimentalFeatures().map((featureIdentifier) => { @@ -43,12 +47,23 @@ const LabsPane: FunctionComponent = ({ application }) => { const premiumModal = usePremiumModal() + const toggleFilesNavigation = useCallback(() => { + const isEntitled = application.features.getFeatureStatus(FeatureIdentifier.Files) === FeatureStatus.Entitled + + if (!isEntitled) { + premiumModal.activate('Files navigation') + return + } + + setFilesNavigation(!isFilesNavigationEnabled) + }, [application.features, isFilesNavigationEnabled, premiumModal, setFilesNavigation]) + return ( Labs
- {experimentalFeatures.map(({ identifier, name, description, isEnabled, isEntitled }, index: number) => { + {experimentalFeatures.map(({ identifier, name, description, isEnabled, isEntitled }, index) => { const toggleFeature = () => { if (!isEntitled) { premiumModal.activate(name) @@ -63,17 +78,23 @@ const LabsPane: FunctionComponent = ({ application }) => { return ( -
-
- {name} - {description} -
- -
+ {showHorizontalSeparator && }
) })} + + {experimentalFeatures.length === 0 && (
diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/General/Labs/LabsFeature.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/General/Labs/LabsFeature.tsx new file mode 100644 index 000000000..776c5fb3b --- /dev/null +++ b/packages/web/src/javascripts/Components/Preferences/Panes/General/Labs/LabsFeature.tsx @@ -0,0 +1,23 @@ +import { Subtitle, Text } from '@/Components/Preferences/PreferencesComponents/Content' +import Switch from '@/Components/Switch/Switch' + +type Props = { + name: string + description: string + toggleFeature: () => void + isEnabled: boolean +} + +const LabsFeature = ({ name, description, toggleFeature, isEnabled }: Props) => { + return ( +
+
+ {name} + {description} +
+ +
+ ) +} + +export default LabsFeature diff --git a/packages/web/src/javascripts/Services/LocalStorage.ts b/packages/web/src/javascripts/Services/LocalStorage.ts index 0bb11fd19..bf7859cf3 100644 --- a/packages/web/src/javascripts/Services/LocalStorage.ts +++ b/packages/web/src/javascripts/Services/LocalStorage.ts @@ -1,13 +1,17 @@ +import { useCallback, useState } from 'react' + export enum StorageKey { AnonymousUserId = 'AnonymousUserId', ShowBetaWarning = 'ShowBetaWarning', ShowNoAccountWarning = 'ShowNoAccountWarning', + FilesNavigationEnabled = 'FilesNavigationEnabled', } export type StorageValue = { [StorageKey.AnonymousUserId]: string [StorageKey.ShowBetaWarning]: boolean [StorageKey.ShowNoAccountWarning]: boolean + [StorageKey.FilesNavigationEnabled]: boolean } export const storage = { @@ -22,3 +26,19 @@ export const storage = { localStorage.removeItem(key) }, } + +type LocalStorageHookReturnType = [StorageValue[Key] | null, (value: StorageValue[Key]) => void] + +export const useLocalStorageItem = (key: Key): LocalStorageHookReturnType => { + const [value, setValue] = useState(() => storage.get(key)) + + const set = useCallback( + (value: StorageValue[Key]) => { + storage.set(key, value) + setValue(value) + }, + [key], + ) + + return [value, set] +}