Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions packages/ui-services/src/UseCase/IsNativeAndroid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Result, SyncUseCaseInterface } from '@standardnotes/domain-core'
import { Environment, Platform } from '@standardnotes/models'

export class IsNativeAndroid implements SyncUseCaseInterface<boolean> {
constructor(
private environment: Environment,
private platform: Platform,
) {}

execute(): Result<boolean> {
return Result.ok(this.environment === Environment.Mobile && this.platform === Platform.Android)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface WebApplicationInterface extends ApplicationInterface {
handleReceivedLinkEvent(item: { link: string; title: string }): Promise<void>
handleOpenFilePreviewEvent(item: { id: string }): void
isNativeMobileWeb(): boolean
canShowPurchaseFlow(): boolean
handleAndroidBackButtonPressed(): void
addAndroidBackHandlerEventListener(listener: () => boolean): (() => void) | undefined
setAndroidBackHandlerFallbackListener(listener: () => boolean): void
Expand Down
1 change: 1 addition & 0 deletions packages/ui-services/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export * from './UseCase/IsGlobalSpellcheckEnabled'
export * from './UseCase/IsNativeMobileWeb'
export * from './UseCase/IsMobileDevice'
export * from './UseCase/IsNativeIOS'
export * from './UseCase/IsNativeAndroid'
export * from './UseCase/GetItemTags'

export * from './Theme/ThemeManager'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const Web_TYPES = {
IsGlobalSpellcheckEnabled: Symbol.for('IsGlobalSpellcheckEnabled'),
IsMobileDevice: Symbol.for('IsMobileDevice'),
IsNativeIOS: Symbol.for('IsNativeIOS'),
IsNativeAndroid: Symbol.for('IsNativeAndroid'),
IsNativeMobileWeb: Symbol.for('IsNativeMobileWeb'),
IsTabletOrMobileScreen: Symbol.for('IsTabletOrMobileScreen'),
LoadPurchaseFlowUrl: Symbol.for('LoadPurchaseFlowUrl'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
IsGlobalSpellcheckEnabled,
IsMobileDevice,
IsNativeIOS,
IsNativeAndroid,
IsNativeMobileWeb,
KeyboardService,
PluginsService,
Expand Down Expand Up @@ -77,6 +78,10 @@ export class WebDependencies extends DependencyContainer {
return new IsNativeIOS(application.environment, application.platform)
})

this.bind(Web_TYPES.IsNativeAndroid, () => {
return new IsNativeAndroid(application.environment, application.platform)
})

this.bind(Web_TYPES.OpenSubscriptionDashboard, () => {
return new OpenSubscriptionDashboard(application, application.legacyApi)
})
Expand Down Expand Up @@ -331,6 +336,7 @@ export class WebDependencies extends DependencyContainer {
application.mobileDevice,
this.get<LoadPurchaseFlowUrl>(Web_TYPES.LoadPurchaseFlowUrl),
this.get<IsNativeIOS>(Web_TYPES.IsNativeIOS),
this.get<IsNativeAndroid>(Web_TYPES.IsNativeAndroid),
application.events,
)
})
Expand Down
11 changes: 10 additions & 1 deletion packages/web/src/javascripts/Application/WebApplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
IsGlobalSpellcheckEnabled,
IsMobileDevice,
IsNativeIOS,
IsNativeAndroid,
IsNativeMobileWeb,
KeyboardService,
PluginsServiceInterface,
Expand Down Expand Up @@ -257,12 +258,20 @@ export class WebApplication extends SNApplication implements WebApplicationInter
return this.deps.get<IsNativeIOS>(Web_TYPES.IsNativeIOS).execute().getValue()
}

isNativeAndroid(): boolean {
return this.deps.get<IsNativeAndroid>(Web_TYPES.IsNativeAndroid).execute().getValue()
}

canShowPurchaseFlow(): boolean {
return !this.isNativeAndroid()
}

get isMobileDevice(): boolean {
return this.deps.get<IsMobileDevice>(Web_TYPES.IsMobileDevice).execute().getValue()
}

get hideOutboundSubscriptionLinks() {
return this.isNativeIOS()
return this.isNativeIOS() || this.isNativeAndroid()
}

get mobileDevice(): MobileDeviceInterface {
Expand Down
6 changes: 6 additions & 0 deletions packages/web/src/javascripts/Components/Footer/UpgradeNow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ const UpgradeNow = ({ application, featuresController, subscriptionContoller }:
const onClick = useCallback(() => {
if (hasAccount && application.isNativeIOS()) {
application.showPremiumModal()
} else if (!application.canShowPurchaseFlow() && !hasAccount) {
application.showAccountMenu()
} else {
void application.openPurchaseFlow()
}
Expand All @@ -27,6 +29,10 @@ const UpgradeNow = ({ application, featuresController, subscriptionContoller }:
return null
}

if (!application.canShowPurchaseFlow() && hasAccount) {
return null
}

return (
<div className="flex h-full items-center px-2">
<button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ const NoSubscriptionBanner = ({
<h1 className="sk-h3 m-0 text-sm font-semibold">{title}</h1>
</div>
<p className="col-start-1 col-end-3 m-0 mt-1 text-sm">{message}</p>
<Button primary small className="col-start-1 col-end-3 mt-3 justify-self-start uppercase" onClick={onClick}>
Upgrade Features
</Button>
{application.canShowPurchaseFlow() && (
<Button primary small className="col-start-1 col-end-3 mt-3 justify-self-start uppercase" onClick={onClick}>
Upgrade Features
</Button>
)}
</div>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const NoProSubscription: FunctionComponent<Props> = ({ application, text }) => {
{!application.hideOutboundSubscriptionLinks && (
<LinkButton className="mr-3 mt-3 min-w-20" label="Learn More" link={window.plansUrl as string} />
)}
{application.hasAccount() && (
{application.hasAccount() && application.canShowPurchaseFlow() && (
<Button className="mt-3 min-w-20" primary label="Upgrade" onClick={onPurchaseClick} />
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const NoSubscription: FunctionComponent<Props> = ({ application }) => {
{!application.hideOutboundSubscriptionLinks && (
<LinkButton className="mr-3 mt-3 min-w-20" label="Learn More" link={window.plansUrl as string} />
)}
{application.hasAccount() && (
{application.hasAccount() && application.canShowPurchaseFlow() && (
<Button className="mt-3 min-w-20" primary label="Subscribe" onClick={onPurchaseClick} />
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,18 @@ export const UpgradePrompt = ({
)}
</div>
</div>
<button
onClick={handleClick}
className={classNames(
'no-border cursor-pointer rounded bg-info py-2 font-bold text-info-contrast hover:brightness-125 focus:brightness-125',
preferHorizontalLayout ? 'w-full px-4 md:ml-auto md:w-auto' : 'my-2 w-full',
)}
ref={ctaRef}
>
Upgrade
</button>
{application.canShowPurchaseFlow() && (
<button
onClick={handleClick}
className={classNames(
'no-border cursor-pointer rounded bg-info py-2 font-bold text-info-contrast hover:brightness-125 focus:brightness-125',
preferHorizontalLayout ? 'w-full px-4 md:ml-auto md:w-auto' : 'my-2 w-full',
)}
ref={ctaRef}
>
Upgrade
</button>
)}
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,17 @@ const RevisionContentLocked: FunctionComponent = () => {
<div className="leading-140% mb-4 text-passive-0">
{getPremiumContentCopy(planName)}. Learn more about our other plans to upgrade your history capacity.
</div>
<Button
primary
label="Discover plans"
onClick={() => {
if (window.plansUrl) {
window.location.assign(window.plansUrl)
}
}}
/>
{application.canShowPurchaseFlow() && (
<Button
primary
label="Discover plans"
onClick={() => {
if (window.plansUrl) {
window.location.assign(window.plansUrl)
}
}}
/>
)}
</div>
</div>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { action, makeObservable, observable } from 'mobx'
import { AbstractViewController } from '../Abstract/AbstractViewController'
import { PurchaseFlowPane } from './PurchaseFlowPane'
import { LoadPurchaseFlowUrl } from '@/Application/UseCase/LoadPurchaseFlowUrl'
import { IsNativeIOS } from '@standardnotes/ui-services'
import { IsNativeIOS, IsNativeAndroid } from '@standardnotes/ui-services'

export class PurchaseFlowController extends AbstractViewController {
isOpen = false
Expand All @@ -25,6 +25,7 @@ export class PurchaseFlowController extends AbstractViewController {
private mobileDevice: MobileDeviceInterface | undefined,
private _loadPurchaseFlowUrl: LoadPurchaseFlowUrl,
private _isNativeIOS: IsNativeIOS,
private _isNativeAndroid: IsNativeAndroid,
eventBus: InternalEventBusInterface,
) {
super(eventBus)
Expand All @@ -44,6 +45,10 @@ export class PurchaseFlowController extends AbstractViewController {
}

openPurchaseFlow = async (plan = AppleIAPProductId.ProPlanYearly) => {
if (this._isNativeAndroid.execute().getValue()) {
return
}

const user = this.sessions.getUser()
if (!user) {
this.isOpen = true
Expand All @@ -58,6 +63,10 @@ export class PurchaseFlowController extends AbstractViewController {
}

openPurchaseWebpage = async () => {
if (this._isNativeAndroid.execute().getValue()) {
return
}

const result = await this._loadPurchaseFlowUrl.execute()
if (result.isFailed()) {
console.error(result.getError())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ describe('ApplicationEventObserver', () => {

beforeEach(() => {
application = {} as jest.Mocked<WebApplication>
application.canShowPurchaseFlow = jest.fn().mockReturnValue(true)

routeService = {} as jest.Mocked<RouteServiceInterface>
routeService.getRoute = jest.fn().mockReturnValue({
Expand Down Expand Up @@ -109,6 +110,17 @@ describe('ApplicationEventObserver', () => {
expect(purchaseFlowController.openPurchaseFlow).toHaveBeenCalled()
})

it('should not open the purchase flow on Android', async () => {
application.canShowPurchaseFlow = jest.fn().mockReturnValue(false)
routeService.getRoute = jest.fn().mockReturnValue({
type: RouteType.Purchase,
} as jest.Mocked<RouteParserInterface>)

await createObserver().handle(ApplicationEvent.Launched)

expect(purchaseFlowController.openPurchaseFlow).not.toHaveBeenCalled()
})

it('should open up settings if user is logged in', async () => {
routeService.getRoute = jest.fn().mockReturnValue({
type: RouteType.Settings,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ export class ApplicationEventObserver implements EventObserverInterface {
const route = this.routeService.getRoute()
switch (route.type) {
case RouteType.Purchase:
void this.purchaseFlowController.openPurchaseFlow()
if (this.application.canShowPurchaseFlow()) {
void this.purchaseFlowController.openPurchaseFlow()
}

break
case RouteType.Settings: {
Expand Down
Loading