refactor: native feature management (#2350)
This commit is contained in:
@@ -31,7 +31,7 @@ const AccountPreferences = ({ application, viewControllerManager }: Props) => {
|
||||
<Sync application={application} />
|
||||
</>
|
||||
)}
|
||||
<Subscription application={application} viewControllerManager={viewControllerManager} />
|
||||
<Subscription />
|
||||
<SubscriptionSharing application={application} viewControllerManager={viewControllerManager} />
|
||||
{application.hasAccount() && viewControllerManager.featuresController.entitledToFiles && (
|
||||
<FilesSection application={application} />
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
import { Title } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import SubscriptionInformation from './SubscriptionInformation'
|
||||
import NoSubscription from './NoSubscription'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent } from 'react'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import PreferencesGroup from '@/Components/Preferences/PreferencesComponents/PreferencesGroup'
|
||||
import PreferencesSegment from '@/Components/Preferences/PreferencesComponents/PreferencesSegment'
|
||||
import { useApplication } from '@/Components/ApplicationProvider'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
viewControllerManager: ViewControllerManager
|
||||
}
|
||||
const Subscription: FunctionComponent = () => {
|
||||
const application = useApplication()
|
||||
|
||||
const Subscription: FunctionComponent<Props> = ({ application, viewControllerManager }: Props) => {
|
||||
const subscriptionState = viewControllerManager.subscriptionController
|
||||
const { onlineSubscription } = subscriptionState
|
||||
|
||||
const now = new Date().getTime()
|
||||
const onlineSubscription = application.controllers.subscriptionController.onlineSubscription
|
||||
|
||||
return (
|
||||
<PreferencesGroup>
|
||||
@@ -25,11 +18,7 @@ const Subscription: FunctionComponent<Props> = ({ application, viewControllerMan
|
||||
<div className="flex flex-row items-center">
|
||||
<div className="flex flex-grow flex-col">
|
||||
<Title>Subscription</Title>
|
||||
{onlineSubscription && onlineSubscription.endsAt > now ? (
|
||||
<SubscriptionInformation subscriptionState={subscriptionState} application={application} />
|
||||
) : (
|
||||
<NoSubscription application={application} />
|
||||
)}
|
||||
{onlineSubscription ? <SubscriptionInformation /> : <NoSubscription application={application} />}
|
||||
</div>
|
||||
</div>
|
||||
</PreferencesSegment>
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { SubscriptionController } from '@/Controllers/Subscription/SubscriptionController'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { openSubscriptionDashboard } from '@/Utils/ManageSubscription'
|
||||
import StatusText from './StatusText'
|
||||
import SubscriptionStatusText from './SubscriptionStatusText'
|
||||
import { useApplication } from '@/Components/ApplicationProvider'
|
||||
|
||||
type Props = {
|
||||
subscriptionState: SubscriptionController
|
||||
application: WebApplication
|
||||
}
|
||||
const SubscriptionInformation = () => {
|
||||
const application = useApplication()
|
||||
|
||||
const SubscriptionInformation = ({ subscriptionState, application }: Props) => {
|
||||
const manageSubscription = async () => {
|
||||
void openSubscriptionDashboard(application)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<StatusText subscriptionState={subscriptionState} />
|
||||
<SubscriptionStatusText />
|
||||
<Button className="mt-3 mr-3 min-w-20" label="Manage subscription" onClick={manageSubscription} />
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { SubscriptionController } from '@/Controllers/Subscription/SubscriptionController'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { Text } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import { useApplication } from '@/Components/ApplicationProvider'
|
||||
|
||||
type Props = { subscriptionState: SubscriptionController }
|
||||
const SubscriptionStatusText = () => {
|
||||
const application = useApplication()
|
||||
|
||||
const StatusText = ({ subscriptionState }: Props) => {
|
||||
const {
|
||||
userSubscriptionName,
|
||||
userSubscriptionExpirationDate,
|
||||
isUserSubscriptionExpired,
|
||||
isUserSubscriptionCanceled,
|
||||
} = subscriptionState
|
||||
} = application.subscriptions
|
||||
|
||||
const expirationDateString = userSubscriptionExpirationDate?.toLocaleString()
|
||||
|
||||
if (isUserSubscriptionCanceled) {
|
||||
@@ -58,4 +59,4 @@ const StatusText = ({ subscriptionState }: Props) => {
|
||||
)
|
||||
}
|
||||
|
||||
export default observer(StatusText)
|
||||
export default observer(SubscriptionStatusText)
|
||||
@@ -4,16 +4,7 @@ import { usePremiumModal } from '@/Hooks/usePremiumModal'
|
||||
import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
|
||||
import Switch from '@/Components/Switch/Switch'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import {
|
||||
ContentType,
|
||||
FeatureIdentifier,
|
||||
PrefKey,
|
||||
GetFeatures,
|
||||
SNTheme,
|
||||
FindNativeFeature,
|
||||
FeatureStatus,
|
||||
naturalSort,
|
||||
} from '@standardnotes/snjs'
|
||||
import { FeatureIdentifier, PrefKey, FeatureStatus, naturalSort, PrefDefaults } from '@standardnotes/snjs'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent, useEffect, useState } from 'react'
|
||||
import { Subtitle, Title, Text } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
@@ -21,8 +12,8 @@ import PreferencesPane from '../PreferencesComponents/PreferencesPane'
|
||||
import PreferencesGroup from '../PreferencesComponents/PreferencesGroup'
|
||||
import PreferencesSegment from '../PreferencesComponents/PreferencesSegment'
|
||||
import { PremiumFeatureIconName } from '@/Components/Icon/PremiumFeatureIcon'
|
||||
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
import EditorAppearance from './Appearance/EditorAppearance'
|
||||
import { GetAllThemesUseCase } from '@standardnotes/ui-services'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
@@ -43,36 +34,39 @@ const Appearance: FunctionComponent<Props> = ({ application }) => {
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const themesAsItems: DropdownItem[] = application.items
|
||||
.getDisplayableComponents()
|
||||
.filter((component) => component.isTheme() && !(component as SNTheme).isLayerable())
|
||||
.filter((theme) => !FindNativeFeature(theme.identifier))
|
||||
.map((theme) => {
|
||||
return {
|
||||
label: theme.name,
|
||||
value: theme.identifier as string,
|
||||
}
|
||||
})
|
||||
const usecase = new GetAllThemesUseCase(application.items)
|
||||
const { thirdParty, native } = usecase.execute({ excludeLayerable: true })
|
||||
|
||||
GetFeatures()
|
||||
.filter((feature) => feature.content_type === ContentType.TYPES.Theme && !feature.layerable)
|
||||
.forEach((theme) => {
|
||||
themesAsItems.push({
|
||||
label: theme.name as string,
|
||||
value: theme.identifier,
|
||||
icon:
|
||||
application.features.getFeatureStatus(theme.identifier) !== FeatureStatus.Entitled
|
||||
? PremiumFeatureIconName
|
||||
: undefined,
|
||||
})
|
||||
})
|
||||
const dropdownItems: DropdownItem[] = []
|
||||
|
||||
themesAsItems.unshift({
|
||||
dropdownItems.push({
|
||||
label: 'Default',
|
||||
value: 'Default',
|
||||
})
|
||||
|
||||
setThemeItems(naturalSort(themesAsItems, 'label'))
|
||||
dropdownItems.push(
|
||||
...native.map((theme) => {
|
||||
return {
|
||||
label: theme.displayName as string,
|
||||
value: theme.featureIdentifier,
|
||||
icon:
|
||||
application.features.getFeatureStatus(theme.featureIdentifier) !== FeatureStatus.Entitled
|
||||
? PremiumFeatureIconName
|
||||
: undefined,
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
dropdownItems.push(
|
||||
...thirdParty.map((theme) => {
|
||||
return {
|
||||
label: theme.displayName,
|
||||
value: theme.featureIdentifier,
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
setThemeItems(naturalSort(dropdownItems, 'label'))
|
||||
}, [application])
|
||||
|
||||
const toggleUseDeviceSettings = () => {
|
||||
|
||||
@@ -3,8 +3,14 @@ import Dropdown from '@/Components/Dropdown/Dropdown'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
|
||||
import Switch from '@/Components/Switch/Switch'
|
||||
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
import { ApplicationEvent, EditorFontSize, EditorLineHeight, EditorLineWidth, PrefKey } from '@standardnotes/snjs'
|
||||
import {
|
||||
ApplicationEvent,
|
||||
EditorFontSize,
|
||||
EditorLineHeight,
|
||||
EditorLineWidth,
|
||||
PrefKey,
|
||||
PrefDefaults,
|
||||
} from '@standardnotes/snjs'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { Subtitle, Title, Text } from '../../PreferencesComponents/Content'
|
||||
import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup'
|
||||
@@ -78,7 +84,7 @@ const EditorDefaults = ({ application }: Props) => {
|
||||
return (
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<Title>Editor appearance</Title>
|
||||
<Title>Editor</Title>
|
||||
<div className="mt-2">
|
||||
<div className="flex justify-between gap-2 md:items-center">
|
||||
<div className="flex flex-col">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { FunctionComponent, useState } from 'react'
|
||||
import { ComponentMutator, SNComponent } from '@standardnotes/snjs'
|
||||
import { ComponentInterface, ComponentMutator, SNComponent } from '@standardnotes/snjs'
|
||||
import { SubtitleLight } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import Switch from '@/Components/Switch/Switch'
|
||||
import Button from '@/Components/Button/Button'
|
||||
@@ -39,7 +39,7 @@ const PackageEntry: FunctionComponent<PackageEntryProps> = ({ application, exten
|
||||
mutator.offlineOnly = newOfflineOnly
|
||||
})
|
||||
.then((item) => {
|
||||
const component = item as SNComponent
|
||||
const component = item as ComponentInterface
|
||||
setOfflineOnly(component.offlineOnly)
|
||||
})
|
||||
.catch((e) => {
|
||||
@@ -54,7 +54,7 @@ const PackageEntry: FunctionComponent<PackageEntryProps> = ({ application, exten
|
||||
mutator.name = newName
|
||||
})
|
||||
.then((item) => {
|
||||
const component = item as SNComponent
|
||||
const component = item as ComponentInterface
|
||||
setExtensionName(component.name)
|
||||
})
|
||||
.catch(console.error)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ButtonType, ContentType, SNComponent } from '@standardnotes/snjs'
|
||||
import { ButtonType, ContentType } from '@standardnotes/snjs'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import DecoratedInput from '@/Components/Input/DecoratedInput'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
@@ -63,7 +63,7 @@ const PackagesPreferencesSection: FunctionComponent<Props> = ({
|
||||
}
|
||||
|
||||
const submitExtensionUrl = async (url: string) => {
|
||||
const component = await application.features.downloadExternalFeature(url)
|
||||
const component = await application.features.downloadRemoteThirdPartyFeature(url)
|
||||
if (component) {
|
||||
setConfirmableExtension(component)
|
||||
}
|
||||
@@ -90,10 +90,6 @@ const PackagesPreferencesSection: FunctionComponent<Props> = ({
|
||||
return false
|
||||
}
|
||||
|
||||
if (extension instanceof SNComponent) {
|
||||
return !['modal', 'rooms'].includes(extension.area)
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { SNActionsExtension, SNComponent, SNTheme } from '@standardnotes/snjs'
|
||||
import { ComponentInterface, SNActionsExtension, ThemeInterface } from '@standardnotes/snjs'
|
||||
|
||||
export type AnyPackageType = SNComponent | SNTheme | SNActionsExtension
|
||||
export type AnyPackageType = ComponentInterface | ThemeInterface | SNActionsExtension
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PrefKey, Platform } from '@standardnotes/snjs'
|
||||
import { PrefKey, Platform, PrefDefaults } from '@standardnotes/snjs'
|
||||
import { Subtitle, Text, Title } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { FunctionComponent, useState } from 'react'
|
||||
@@ -6,7 +6,6 @@ import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
|
||||
import Switch from '@/Components/Switch/Switch'
|
||||
import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup'
|
||||
import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment'
|
||||
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import { Text, Title } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ApplicationEvent, FeatureIdentifier, FeatureStatus, FindNativeFeature, PrefKey } from '@standardnotes/snjs'
|
||||
import {
|
||||
ApplicationEvent,
|
||||
FeatureIdentifier,
|
||||
FeatureStatus,
|
||||
FindNativeFeature,
|
||||
PrefKey,
|
||||
PrefDefaults,
|
||||
} from '@standardnotes/snjs'
|
||||
import { Fragment, FunctionComponent, useCallback, useEffect, useState } from 'react'
|
||||
import { usePremiumModal } from '@/Hooks/usePremiumModal'
|
||||
import PreferencesGroup from '../../../PreferencesComponents/PreferencesGroup'
|
||||
@@ -8,7 +15,6 @@ import PreferencesSegment from '../../../PreferencesComponents/PreferencesSegmen
|
||||
import LabsFeature from './LabsFeature'
|
||||
import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
|
||||
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
|
||||
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
|
||||
type ExperimentalFeatureItem = {
|
||||
identifier: FeatureIdentifier
|
||||
|
||||
@@ -21,9 +21,9 @@ const Persistence = ({ application }: Props) => {
|
||||
setShouldPersistNoteState(shouldPersist)
|
||||
|
||||
if (shouldPersist) {
|
||||
application.getViewControllerManager().persistValues()
|
||||
application.controllers.persistValues()
|
||||
} else {
|
||||
application.getViewControllerManager().clearPersistedValues()
|
||||
application.controllers.clearPersistedValues()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import Switch from '@/Components/Switch/Switch'
|
||||
import { Subtitle, Text, Title } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { PrefKey } from '@standardnotes/snjs'
|
||||
import { PrefKey, PrefDefaults } from '@standardnotes/snjs'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent, useState } from 'react'
|
||||
import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup'
|
||||
import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment'
|
||||
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
|
||||
@@ -25,7 +25,7 @@ const HomeServerSettings = () => {
|
||||
const homeServerService = application.homeServer as HomeServerServiceInterface
|
||||
const featuresService = application.features
|
||||
const sessionsService = application.sessions
|
||||
const viewControllerManager = application.getViewControllerManager()
|
||||
const viewControllerManager = application.controllers
|
||||
|
||||
const logsTextarea = useRef<HTMLTextAreaElement>(null)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user