refactor: native feature management (#2350)

This commit is contained in:
Mo
2023-07-12 12:56:08 -05:00
committed by GitHub
parent 49f7581cd8
commit 078ef3772c
223 changed files with 3996 additions and 3438 deletions

View File

@@ -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} />

View File

@@ -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>

View File

@@ -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} />
</>
)

View File

@@ -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)

View File

@@ -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 = () => {

View File

@@ -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">

View File

@@ -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)

View File

@@ -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
})

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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()
}
}

View File

@@ -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

View File

@@ -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)