fix(mobile): iOS IAP related adjustments

This commit is contained in:
Mo
2022-11-14 07:50:50 -06:00
parent eba8dbc38c
commit 1c23bc1747
7 changed files with 170 additions and 86 deletions

View File

@@ -205,6 +205,10 @@ export class WebApplication extends SNApplication implements WebApplicationInter
return this.isNativeMobileWeb() && this.platform === Platform.Ios
}
get hideOutboundSubscriptionLinks() {
return this.isNativeIOS()
}
mobileDevice(): MobileDeviceInterface {
if (!this.isNativeMobileWeb()) {
throw Error('Attempting to access device as mobile device on non mobile platform')

View File

@@ -29,7 +29,9 @@ const NoSubscription: FunctionComponent<Props> = ({ application }) => {
{isLoadingPurchaseFlow && <Text>Redirecting you to the subscription page...</Text>}
{purchaseFlowError && <Text className="text-danger">{purchaseFlowError}</Text>}
<div className="flex">
<LinkButton className="mt-3 mr-3 min-w-20" label="Learn More" link={window.plansUrl as string} />
{!application.hideOutboundSubscriptionLinks && (
<LinkButton className="mt-3 mr-3 min-w-20" label="Learn More" link={window.plansUrl as string} />
)}
{application.hasAccount() && (
<Button className="mt-3 min-w-20" primary label="Subscribe" onClick={onPurchaseClick} />
)}

View File

@@ -33,7 +33,9 @@ const NoProSubscription: FunctionComponent<Props> = ({ application }) => {
{purchaseFlowError && <Text className="text-danger">{purchaseFlowError}</Text>}
<div className="flex">
<LinkButton className="mt-3 mr-3 min-w-20" label="Learn More" link={window.plansUrl as string} />
{!application.hideOutboundSubscriptionLinks && (
<LinkButton className="mt-3 mr-3 min-w-20" label="Learn More" link={window.plansUrl as string} />
)}
{application.hasAccount() && (
<Button className="mt-3 min-w-20" primary label="Upgrade" onClick={onPurchaseClick} />
)}

View File

@@ -41,6 +41,14 @@ const HelpAndFeedback = ({ application }: { application: WebApplication }) => {
Privacy Manifesto.
</a>
</Text>
{application.isNativeIOS() && (
<LinkButton
className="mt-3"
label="Privacy Policy"
link="https://standardnotes.com/privacy"
onClick={handleClick}
/>
)}
</PreferencesSegment>
<HorizontalSeparator classes="my-4" />
<PreferencesSegment>

View File

@@ -1,11 +1,10 @@
import { AlertDialog, AlertDialogDescription, AlertDialogLabel } from '@reach/alert-dialog'
import { FunctionComponent, useCallback, useRef } from 'react'
import Icon from '@/Components/Icon/Icon'
import { AlertDialog } from '@reach/alert-dialog'
import { FunctionComponent, useRef } from 'react'
import { WebApplication } from '@/Application/Application'
import { openSubscriptionDashboard } from '@/Utils/ManageSubscription'
import { PremiumFeatureIconClass, PremiumFeatureIconName } from '../Icon/PremiumFeatureIcon'
import { PremiumFeatureModalType } from './PremiumFeatureModalType'
import { FeatureName } from '@/Controllers/FeatureName'
import { SuccessPrompt } from './Subviews/SuccessPrompt'
import { UpgradePrompt } from './Subviews/UpgradePrompt'
type Props = {
application: WebApplication
@@ -26,92 +25,32 @@ const PremiumFeaturesModal: FunctionComponent<Props> = ({
showModal,
type = PremiumFeatureModalType.UpgradePrompt,
}) => {
const plansButtonRef = useRef<HTMLButtonElement>(null)
const ctaButtonRef = useRef<HTMLButtonElement>(null)
const handleClick = useCallback(() => {
if (hasSubscription) {
void openSubscriptionDashboard(application)
} else if (hasAccount) {
void application.openPurchaseFlow()
} else if (window.plansUrl) {
window.location.assign(window.plansUrl)
}
onClose()
}, [application, hasSubscription, hasAccount, onClose])
if (!showModal) {
return null
}
const UpgradePrompt = (
<>
<AlertDialogDescription className="mb-2 px-4.5 text-center text-sm text-passive-1">
To take advantage of <span className="font-semibold">{featureName}</span> and other advanced features, upgrade
your current plan.
</AlertDialogDescription>
<div className="p-4">
<button
onClick={handleClick}
className="no-border w-full cursor-pointer rounded bg-info py-2 font-bold text-info-contrast hover:brightness-125 focus:brightness-125"
ref={plansButtonRef}
>
Upgrade
</button>
</div>
</>
)
const SuccessPrompt = (
<>
<AlertDialogDescription className="mb-2 px-4.5 text-center text-sm text-passive-1">
Enjoy your new powered up experience.
</AlertDialogDescription>
<div className="p-4">
<button
onClick={onClose}
className="no-border w-full cursor-pointer rounded bg-info py-2 font-bold text-info-contrast hover:brightness-125 focus:brightness-125"
ref={plansButtonRef}
>
Continue
</button>
</div>
</>
)
const title =
type === PremiumFeatureModalType.UpgradePrompt ? 'Enable Advanced Features' : 'Your purchase was successful!'
const iconName = type === PremiumFeatureModalType.UpgradePrompt ? PremiumFeatureIconName : '🎉'
const iconClass =
type === PremiumFeatureModalType.UpgradePrompt
? `h-12 w-12 ${PremiumFeatureIconClass}`
: 'px-7 py-2 h-24 w-24 text-[50px]'
return showModal ? (
<AlertDialog leastDestructiveRef={plansButtonRef} className="p-0">
return (
<AlertDialog leastDestructiveRef={ctaButtonRef} className="p-0">
<div tabIndex={-1} className="sn-component">
<div tabIndex={0} className="max-w-89 rounded bg-default p-4 shadow-main">
<AlertDialogLabel>
<div className="flex justify-end p-1">
<button
className="flex cursor-pointer border-0 bg-transparent p-0"
onClick={onClose}
aria-label="Close modal"
>
<Icon className="text-neutral" type="close" />
</button>
</div>
<div
className="mx-auto mb-5 flex h-24 w-24 items-center justify-center rounded-[50%] bg-contrast"
aria-hidden={true}
>
<Icon className={iconClass} size={'custom'} type={iconName} />
</div>
<div className="mb-1 text-center text-lg font-bold">{title}</div>
</AlertDialogLabel>
{type === PremiumFeatureModalType.UpgradePrompt ? UpgradePrompt : SuccessPrompt}
{type === PremiumFeatureModalType.UpgradePrompt && (
<UpgradePrompt
featureName={featureName}
ctaRef={ctaButtonRef}
application={application}
hasAccount={hasAccount}
hasSubscription={hasSubscription}
onClose={onClose}
/>
)}
{type === PremiumFeatureModalType.UpgradeSuccess && <SuccessPrompt ctaRef={ctaButtonRef} onClose={onClose} />}
</div>
</div>
</AlertDialog>
) : null
)
}
export default PremiumFeaturesModal

View File

@@ -0,0 +1,47 @@
import Icon from '@/Components/Icon/Icon'
import { AlertDialogDescription, AlertDialogLabel } from '@reach/alert-dialog'
export const SuccessPrompt = ({
ctaRef,
onClose,
}: {
ctaRef: React.RefObject<HTMLButtonElement>
onClose: () => void
}) => {
return (
<>
<AlertDialogLabel>
<div className="flex justify-end p-1">
<button
className="flex cursor-pointer border-0 bg-transparent p-0"
onClick={onClose}
aria-label="Close modal"
>
<Icon className="text-neutral" type="close" />
</button>
</div>
<div
className="mx-auto mb-5 flex h-24 w-24 items-center justify-center rounded-[50%] bg-contrast"
aria-hidden={true}
>
<Icon className={'h-24 w-24 px-7 py-2 text-[50px]'} size={'custom'} type={'🎉'} />
</div>
<div className="mb-1 text-center text-lg font-bold">Your purchase was successful!</div>
</AlertDialogLabel>
<AlertDialogDescription className="mb-2 px-4.5 text-center text-sm text-passive-1">
Enjoy your new powered up experience.
</AlertDialogDescription>
<div className="p-4">
<button
onClick={onClose}
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}
>
Continue
</button>
</div>
</>
)
}

View File

@@ -0,0 +1,82 @@
import { AlertDialogDescription, AlertDialogLabel } from '@reach/alert-dialog'
import { useCallback } from 'react'
import { WebApplication } from '@/Application/Application'
import { openSubscriptionDashboard } from '@/Utils/ManageSubscription'
import Icon from '@/Components/Icon/Icon'
import { PremiumFeatureIconClass, PremiumFeatureIconName } from '@/Components/Icon/PremiumFeatureIcon'
export const UpgradePrompt = ({
featureName,
ctaRef,
application,
hasSubscription,
hasAccount,
onClose,
}: {
featureName: string
ctaRef: React.RefObject<HTMLButtonElement>
application: WebApplication
hasSubscription: boolean
hasAccount: boolean
onClose: () => void
}) => {
const handleClick = useCallback(() => {
if (hasSubscription) {
void openSubscriptionDashboard(application)
} else if (hasAccount) {
void application.openPurchaseFlow()
} else if (window.plansUrl) {
window.location.assign(window.plansUrl)
}
onClose()
}, [application, hasSubscription, hasAccount, onClose])
return (
<>
<AlertDialogLabel>
<div className="flex justify-end p-1">
<button
className="flex cursor-pointer border-0 bg-transparent p-0"
onClick={onClose}
aria-label="Close modal"
>
<Icon className="text-neutral" type="close" />
</button>
</div>
<div
className="mx-auto mb-5 flex h-24 w-24 items-center justify-center rounded-[50%] bg-contrast"
aria-hidden={true}
>
<Icon className={`h-12 w-12 ${PremiumFeatureIconClass}`} size={'custom'} type={PremiumFeatureIconName} />
</div>
<div className="mb-1 text-center text-lg font-bold">Enable Advanced Features</div>
</AlertDialogLabel>
<AlertDialogDescription className="mb-2 px-4.5 text-center text-sm text-passive-1">
To take advantage of <span className="font-semibold">{featureName}</span> and other advanced features, upgrade
your current plan.
{application.isNativeIOS() && (
<div className="mt-2">
<div className="mb-2 font-bold">The Professional Plan costs $99.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>Note history going back indefinitely</li>
<li>Nested folders for your tags</li>
<li>Premium support</li>
</ul>
</div>
)}
</AlertDialogDescription>
<div className="p-4">
<button
onClick={handleClick}
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
</button>
</div>
</>
)
}