refactor: offline roles (#2169)
This commit is contained in:
@@ -13,6 +13,7 @@ type Props = {
|
||||
const UpgradeNow = ({ application, featuresController, subscriptionContoller }: Props) => {
|
||||
const shouldShowCTA = !featuresController.hasFolders
|
||||
const hasAccount = subscriptionContoller.hasAccount
|
||||
const hasAccessToFeatures = subscriptionContoller.hasFirstPartySubscription
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
if (hasAccount && application.isNativeIOS()) {
|
||||
@@ -22,16 +23,20 @@ const UpgradeNow = ({ application, featuresController, subscriptionContoller }:
|
||||
}
|
||||
}, [application, hasAccount])
|
||||
|
||||
return shouldShowCTA ? (
|
||||
if (!shouldShowCTA || hasAccessToFeatures) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-full items-center px-2">
|
||||
<button
|
||||
className="rounded bg-info py-0.5 px-1.5 text-sm font-bold uppercase text-info-contrast hover:brightness-125 lg:text-xs"
|
||||
onClick={onClick}
|
||||
>
|
||||
{hasAccount ? 'Unlock features' : 'Sign up to sync'}
|
||||
{!hasAccount ? 'Sign up to sync' : 'Unlock features'}
|
||||
</button>
|
||||
</div>
|
||||
) : null
|
||||
)
|
||||
}
|
||||
|
||||
export default observer(UpgradeNow)
|
||||
|
||||
@@ -99,7 +99,7 @@ const Email: FunctionComponent<Props> = ({ application }: Props) => {
|
||||
<Subtitle>Disable sign-in notification emails</Subtitle>
|
||||
<Text>
|
||||
Disables email notifications when a new sign-in occurs on your account. (Email notifications are
|
||||
available to paid subscribers).
|
||||
available only to paid subscribers).
|
||||
</Text>
|
||||
</div>
|
||||
{isLoading ? (
|
||||
|
||||
@@ -15,7 +15,7 @@ type Props = {
|
||||
|
||||
const Subscription: FunctionComponent<Props> = ({ application, viewControllerManager }: Props) => {
|
||||
const subscriptionState = viewControllerManager.subscriptionController
|
||||
const { userSubscription } = subscriptionState
|
||||
const { onlineSubscription } = subscriptionState
|
||||
|
||||
const now = new Date().getTime()
|
||||
|
||||
@@ -25,7 +25,7 @@ const Subscription: FunctionComponent<Props> = ({ application, viewControllerMan
|
||||
<div className="flex flex-row items-center">
|
||||
<div className="flex flex-grow flex-col">
|
||||
<Title>Subscription</Title>
|
||||
{userSubscription && userSubscription.endsAt > now ? (
|
||||
{onlineSubscription && onlineSubscription.endsAt > now ? (
|
||||
<SubscriptionInformation subscriptionState={subscriptionState} application={application} />
|
||||
) : (
|
||||
<NoSubscription application={application} />
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
} from '@standardnotes/snjs'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import { isDev, openInNewTab } from '@/Utils'
|
||||
import { openInNewTab } from '@/Utils'
|
||||
import { Subtitle } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import { KeyboardKey } from '@standardnotes/ui-services'
|
||||
|
||||
@@ -61,7 +61,7 @@ const CloudBackupProvider: FunctionComponent<Props> = ({ application, providerNa
|
||||
}
|
||||
event.stopPropagation()
|
||||
|
||||
const authUrl = application.getCloudProviderIntegrationUrl(providerName, isDev)
|
||||
const authUrl = application.getCloudProviderIntegrationUrl(providerName)
|
||||
openInNewTab(authUrl)
|
||||
setAuthBegan(true)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ export class PackageProvider {
|
||||
static async load(application: WebApplication): Promise<PackageProvider | undefined> {
|
||||
const response = await application.getAvailableSubscriptions()
|
||||
|
||||
if (response instanceof ClientDisplayableError) {
|
||||
if (!response || response instanceof ClientDisplayableError) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,10 @@ export const UpgradePrompt = ({
|
||||
<div className="mb-2 font-bold">The Professional Plan costs $119.99/year and includes benefits like</div>
|
||||
<ul className="list-inside list-[circle]">
|
||||
<li>100GB encrypted file storage</li>
|
||||
<li>Access to all note types, including markdown, rich text, authenticator, tasks, and spreadsheets</li>
|
||||
<li>
|
||||
Access to all note types, including Super, markdown, rich text, authenticator, tasks, and spreadsheets
|
||||
</li>
|
||||
<li>Access to Daily Notebooks and Moments journals</li>
|
||||
<li>Note history going back indefinitely</li>
|
||||
<li>Nested folders for your tags</li>
|
||||
<li>Premium support</li>
|
||||
@@ -79,7 +82,7 @@ export const UpgradePrompt = ({
|
||||
className="no-border w-full cursor-pointer rounded bg-info py-2 font-bold text-info-contrast hover:brightness-125 focus:brightness-125"
|
||||
ref={ctaRef}
|
||||
>
|
||||
Upgrade
|
||||
{application.isNativeIOS() ? 'Start Free Trial' : 'Upgrade'}
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -65,6 +65,7 @@ export class FeaturesController extends AbstractViewController {
|
||||
break
|
||||
case ApplicationEvent.FeaturesUpdated:
|
||||
case ApplicationEvent.Launched:
|
||||
case ApplicationEvent.LocalDataLoaded:
|
||||
runInAction(() => {
|
||||
this.hasFolders = this.isEntitledToFolders()
|
||||
this.hasSmartViews = this.isEntitledToSmartViews()
|
||||
|
||||
@@ -75,7 +75,7 @@ export class LinkingController extends AbstractViewController {
|
||||
}
|
||||
|
||||
get isEntitledToNoteLinking() {
|
||||
return !!this.subscriptionController.userSubscription
|
||||
return !!this.subscriptionController.onlineSubscription
|
||||
}
|
||||
|
||||
setIsLinkingPanelOpen = (open: boolean) => {
|
||||
|
||||
@@ -17,14 +17,15 @@ import { Subscription } from './SubscriptionType'
|
||||
export class SubscriptionController extends AbstractViewController {
|
||||
private readonly ALLOWED_SUBSCRIPTION_INVITATIONS = 5
|
||||
|
||||
userSubscription: Subscription | undefined = undefined
|
||||
onlineSubscription: Subscription | undefined = undefined
|
||||
availableSubscriptions: AvailableSubscriptions | undefined = undefined
|
||||
subscriptionInvitations: Invitation[] | undefined = undefined
|
||||
hasAccount: boolean
|
||||
hasFirstPartySubscription: boolean
|
||||
|
||||
override deinit() {
|
||||
super.deinit()
|
||||
;(this.userSubscription as unknown) = undefined
|
||||
;(this.onlineSubscription as unknown) = undefined
|
||||
;(this.availableSubscriptions as unknown) = undefined
|
||||
;(this.subscriptionInvitations as unknown) = undefined
|
||||
|
||||
@@ -38,12 +39,14 @@ export class SubscriptionController extends AbstractViewController {
|
||||
) {
|
||||
super(application, eventBus)
|
||||
this.hasAccount = application.hasAccount()
|
||||
this.hasFirstPartySubscription = application.features.hasFirstPartySubscription()
|
||||
|
||||
makeObservable(this, {
|
||||
userSubscription: observable,
|
||||
onlineSubscription: observable,
|
||||
availableSubscriptions: observable,
|
||||
subscriptionInvitations: observable,
|
||||
hasAccount: observable,
|
||||
hasFirstPartySubscription: observable,
|
||||
|
||||
userSubscriptionName: computed,
|
||||
userSubscriptionExpirationDate: computed,
|
||||
@@ -64,11 +67,20 @@ export class SubscriptionController extends AbstractViewController {
|
||||
this.reloadSubscriptionInvitations().catch(console.error)
|
||||
}
|
||||
runInAction(() => {
|
||||
this.hasFirstPartySubscription = application.features.hasFirstPartySubscription()
|
||||
this.hasAccount = application.hasAccount()
|
||||
})
|
||||
}, ApplicationEvent.Launched),
|
||||
)
|
||||
|
||||
this.disposers.push(
|
||||
application.addEventObserver(async () => {
|
||||
runInAction(() => {
|
||||
this.hasFirstPartySubscription = application.features.hasFirstPartySubscription()
|
||||
})
|
||||
}, ApplicationEvent.LocalDataLoaded),
|
||||
)
|
||||
|
||||
this.disposers.push(
|
||||
application.addEventObserver(async () => {
|
||||
this.getSubscriptionInfo().catch(console.error)
|
||||
@@ -83,6 +95,9 @@ export class SubscriptionController extends AbstractViewController {
|
||||
application.addEventObserver(async () => {
|
||||
this.getSubscriptionInfo().catch(console.error)
|
||||
this.reloadSubscriptionInvitations().catch(console.error)
|
||||
runInAction(() => {
|
||||
this.hasFirstPartySubscription = application.features.hasFirstPartySubscription()
|
||||
})
|
||||
}, ApplicationEvent.UserRolesChanged),
|
||||
)
|
||||
}
|
||||
@@ -90,20 +105,20 @@ export class SubscriptionController extends AbstractViewController {
|
||||
get userSubscriptionName(): string {
|
||||
if (
|
||||
this.availableSubscriptions &&
|
||||
this.userSubscription &&
|
||||
this.availableSubscriptions[this.userSubscription.planName]
|
||||
this.onlineSubscription &&
|
||||
this.availableSubscriptions[this.onlineSubscription.planName]
|
||||
) {
|
||||
return this.availableSubscriptions[this.userSubscription.planName].name
|
||||
return this.availableSubscriptions[this.onlineSubscription.planName].name
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
get userSubscriptionExpirationDate(): Date | undefined {
|
||||
if (!this.userSubscription) {
|
||||
if (!this.onlineSubscription) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return new Date(convertTimestampToMilliseconds(this.userSubscription.endsAt))
|
||||
return new Date(convertTimestampToMilliseconds(this.onlineSubscription.endsAt))
|
||||
}
|
||||
|
||||
get isUserSubscriptionExpired(): boolean {
|
||||
@@ -115,11 +130,11 @@ export class SubscriptionController extends AbstractViewController {
|
||||
}
|
||||
|
||||
get isUserSubscriptionCanceled(): boolean {
|
||||
return Boolean(this.userSubscription?.cancelled)
|
||||
return Boolean(this.onlineSubscription?.cancelled)
|
||||
}
|
||||
|
||||
hasValidSubscription(): boolean {
|
||||
return this.userSubscription != undefined && !this.isUserSubscriptionExpired && !this.isUserSubscriptionCanceled
|
||||
return this.onlineSubscription != undefined && !this.isUserSubscriptionExpired && !this.isUserSubscriptionCanceled
|
||||
}
|
||||
|
||||
get usedInvitationsCount(): number {
|
||||
@@ -139,7 +154,7 @@ export class SubscriptionController extends AbstractViewController {
|
||||
}
|
||||
|
||||
public setUserSubscription(subscription: Subscription): void {
|
||||
this.userSubscription = subscription
|
||||
this.onlineSubscription = subscription
|
||||
}
|
||||
|
||||
public setAvailableSubscriptions(subscriptions: AvailableSubscriptions): void {
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
ComponentArea,
|
||||
FeatureDescription,
|
||||
GetFeatures,
|
||||
FindNativeFeature,
|
||||
NoteType,
|
||||
FeatureIdentifier,
|
||||
} from '@standardnotes/snjs'
|
||||
@@ -149,8 +150,7 @@ const createBaselineMap = (application: WebApplication): NoteTypeToEditorRowsMap
|
||||
isEntitled: application.features.getFeatureStatus(FeatureIdentifier.SuperEditor) === FeatureStatus.Entitled,
|
||||
noteType: NoteType.Super,
|
||||
isLabs: true,
|
||||
description:
|
||||
'A new way to edit notes. Type / to bring up the block selection menu, or @ to embed images or link other tags and notes. Type - then space to start a list, or [] then space to start a checklist. Drag and drop an image or file to embed it in your note.',
|
||||
description: FindNativeFeature(FeatureIdentifier.SuperEditor)?.description,
|
||||
},
|
||||
],
|
||||
[NoteType.RichText]: [],
|
||||
|
||||
Reference in New Issue
Block a user