Skip to content

Commit 108b190

Browse files
committed
[Landing] Cache pricing plans on a json file to avoid making unnecessary api requests
1 parent e55509b commit 108b190

25 files changed

Lines changed: 253 additions & 146 deletions

landing/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ dist
77
node_modules
88
npm-debug.log*
99
out
10+
scripts/out
1011
yarn-debug.log*
11-
yarn-error.log*
12+
yarn-error.log*

landing/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"clean": "shx rm -f *.tsbuildinfo && shx rm -rf dist && mkdirp dist",
88
"compile": "cd .",
99
"deploy": "yarn now",
10+
"export": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' ts-node ./src/scripts/refresh-cached-public-plans && next build && next export",
1011
"format": "prettier --write '{.,src/**}/*.{js,jsx,ts,tsx}'",
1112
"lint": "tslint -p .",
1213
"now": "now",
@@ -39,8 +40,10 @@
3940
"@types/stripe-v3": "3.1.9",
4041
"@types/styled-jsx": "2.2.8",
4142
"mkdirp": "0.5.1",
43+
"node-fetch": "2.6.0",
4244
"now": "16.7.1",
4345
"prettier": "1.18.2",
46+
"ts-node": "8.6.2",
4447
"tslint": "5.20.1",
4548
"tslint-config-airbnb": "5.11.2",
4649
"tslint-config-prettier": "1.18.0",

landing/pages/pricing.tsx

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,11 @@
11
import { NextPage } from 'next'
22

3-
import { setPublicPlansCache } from '../src/context/PlansContext'
43
import PricingPage from '../src/pages/PricingPage'
54

65
export interface PricingPageProps {}
76

87
const Pricing: NextPage<PricingPageProps> = props => {
9-
console.log('xxx pricing page', props)
108
return <PricingPage />
119
}
1210

1311
export default Pricing
14-
15-
export function unstable_getStaticProps() {
16-
setPublicPlansCache(value => ({
17-
...value,
18-
freeTrialDays: 1234,
19-
}))
20-
21-
return {
22-
props: {
23-
test: 1234,
24-
},
25-
}
26-
}

landing/src/components/sections/CTAButtons.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export default function CTAButtons(props: CTAButtonsProps) {
4747
) : os ? (
4848
<>
4949
{paidPlans.length === 1 &&
50+
paidPlans[0] &&
5051
!paidPlans[0].interval &&
5152
paidPlans[0].amount ? (
5253
<Button
@@ -82,13 +83,13 @@ export default function CTAButtons(props: CTAButtonsProps) {
8283
target="_top"
8384
className="mb-2 mr-2"
8485
>
85-
{paidPlans[0].trialPeriodDays
86+
{paidPlans[0] && paidPlans[0].trialPeriodDays
8687
? 'Start free trial'
8788
: 'See pricing'}
8889
</Button>
8990
)}
9091

91-
{!!(freeTrialDays && plans.some(plan => !plan.amount)) && (
92+
{!!(freeTrialDays && plans.some(plan => !!plan && !plan.amount)) && (
9293
<Button
9394
type="neutral"
9495
href="/download?autostart"

landing/src/components/sections/header/Header.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export default function Header(props: HeaderProps) {
9393
</HeaderLink> */}
9494

9595
{!!(
96-
(freeTrialDays && plans.some(plan => !plan.amount)) ||
96+
(freeTrialDays && plans.some(plan => !!plan && !plan.amount)) ||
9797
(authData &&
9898
authData.appToken &&
9999
(freeTrialDays || (authData.plan && authData.plan.amount)))

landing/src/components/sections/pricing/PricingPlanBlock.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import qs from 'qs'
99
import React from 'react'
1010

1111
import { useAuth } from '../../../context/AuthContext'
12+
import { usePlans } from '../../../context/PlansContext'
1213
import { getPurchaseOrSubscribeRoute } from '../../../helpers'
1314
import { useLocalizedPlanDetails } from '../../../hooks/use-localized-plan-details'
1415
import Button from '../../common/buttons/Button'
1516
import CheckLabel from '../../common/CheckLabel'
16-
import { usePlans } from '../../../context/PlansContext'
1717

1818
export interface PricingPlanBlockProps {
1919
banner?: string | boolean
@@ -37,14 +37,17 @@ export function PricingPlanBlock(props: PricingPlanBlockProps) {
3737
const { authData } = useAuth()
3838
const { plans } = usePlans()
3939
const localizedPlan = useLocalizedPlanDetails(plan)
40+
41+
if (!localizedPlan) return null
42+
4043
const userPlan = authData && authData.plan
4144
const userPlanIsActive =
42-
userPlan && userPlan.id && plans.find(p => p.id === userPlan!.id)
43-
const isMyPlan = userPlan && userPlan.id === localizedPlan.id
45+
userPlan && userPlan.id && plans.find(p => p && p.id === userPlan!.id)
46+
const isMyPlan = !!(userPlan && userPlan.id === localizedPlan.id)
4447

4548
const banner = userPlanIsActive
4649
? isMyPlan
47-
? localizedPlan.interval
50+
? localizedPlan && localizedPlan.interval
4851
? 'Current localizedPlan'
4952
: 'You bought this'
5053
: _banner || true
@@ -101,7 +104,7 @@ export function PricingPlanBlock(props: PricingPlanBlockProps) {
101104
`Free for ${localizedPlan.trialPeriodDays} days`
102105
} else if (
103106
localizedPlan.amount &&
104-
plans.some(p => !p.amount && p.trialPeriodDays)
107+
plans.some(p => p && !p.amount && p.trialPeriodDays)
105108
) {
106109
//
107110
} else if (localizedPlan.trialPeriodDays) {

landing/src/components/sections/pricing/PricingPlans.tsx

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ export function PricingPlans(_props: PricingPlansProps) {
1515
const { plans } = usePlans()
1616

1717
const shouldShowPlanTypeTabs =
18-
plans.some(plan => plan.type !== 'team') &&
19-
plans.some(plan => plan.type === 'team')
18+
plans.some(plan => plan && plan.type !== 'team') &&
19+
plans.some(plan => plan && plan.type === 'team')
2020

2121
const [_tab, setTab] = useState<PlanType | undefined>(undefined)
2222
const tab =
@@ -39,7 +39,7 @@ export function PricingPlans(_props: PricingPlansProps) {
3939
)
4040

4141
return filteredPlans.map(plan =>
42-
plan.amount > 0 ? (
42+
plan && plan.amount > 0 ? (
4343
<PricingPlanBlock
4444
key={`pricing-plan-${plan.id}`}
4545
banner={plan.banner}
@@ -51,7 +51,7 @@ export function PricingPlans(_props: PricingPlansProps) {
5151
plan={plan}
5252
totalNumberOfVisiblePlans={filteredPlans.length}
5353
/>
54-
) : (
54+
) : plan ? (
5555
<PricingPlanBlock
5656
key={`pricing-plan-${plan.cannonicalId}`}
5757
banner
@@ -60,7 +60,7 @@ export function PricingPlans(_props: PricingPlansProps) {
6060
plan={plan}
6161
totalNumberOfVisiblePlans={filteredPlans.length}
6262
/>
63-
),
63+
) : null,
6464
)
6565
}, [tab])
6666

@@ -81,17 +81,19 @@ export function PricingPlans(_props: PricingPlansProps) {
8181
)}
8282

8383
<div className="flex flex-row lg:justify-center items-stretch -ml-8 sm:ml-0 -mr-8 sm:mr-0 pl-8 sm:pl-0 pr-8 sm:pr-0 overflow-x-scroll md:overflow-x-auto">
84-
{pricingPlanComponents.map((component, index) => (
85-
<Fragment key={`${component.key}-container`}>
86-
{component}
84+
{pricingPlanComponents.map((component, index) =>
85+
component ? (
86+
<Fragment key={`${component.key}-container`}>
87+
{component}
8788

88-
{index < pricingPlanComponents.length - 1 ? (
89-
<div className="pr-2 sm:pr-6" />
90-
) : (
91-
<div className="pr-2" />
92-
)}
93-
</Fragment>
94-
))}
89+
{index < pricingPlanComponents.length - 1 ? (
90+
<div className="pr-2 sm:pr-6" />
91+
) : (
92+
<div className="pr-2" />
93+
)}
94+
</Fragment>
95+
) : null,
96+
)}
9597
</div>
9698

9799
<p className="block sm:hidden mb-4" />

landing/src/components/sections/subscribe/SubscribeForm.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export const SubscribeForm = injectStripe<SubscribeFormProps>(
3838
const Router = useRouter()
3939
const { Paddle } = usePaddleLoader()
4040

41-
const localizedPlan = useLocalizedPlanDetails(plan)
41+
const localizedPlan = useLocalizedPlanDetails(plan)!
4242

4343
const isMountedRef = useIsMountedRef()
4444

landing/src/context/PlansContext.tsx

Lines changed: 18 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,17 @@
1-
import { constants, Plan } from '@brunolemos/devhub-core'
21
import React, { useContext, useEffect, useState } from 'react'
32

4-
import { getDefaultDevHubHeaders } from '../helpers'
5-
import { useIsMountedRef } from '../hooks/use-is-mounted-ref'
3+
import {
4+
fetchPlansState,
5+
getCachedPublicPlans,
6+
PlansState,
7+
} from '../helpers/plans'
8+
import { useDynamicRef } from '../hooks/use-dynamic-ref'
69
import { useAuth } from './AuthContext'
710

811
export interface PlansProps {
912
children: React.ReactNode
1013
}
1114

12-
export interface PlansState {
13-
freePlan: Plan | undefined
14-
freeTrialDays: number
15-
freeTrialPlan: Plan | undefined
16-
paidPlans: Plan[]
17-
plans: Plan[]
18-
userPlanInfo: Plan | undefined
19-
}
20-
2115
const initialState: PlansState = {
2216
freePlan: undefined,
2317
freeTrialDays: 0,
@@ -31,44 +25,27 @@ export const PlansContext = React.createContext(initialState)
3125
PlansContext.displayName = 'PlansContext'
3226

3327
export function PlansProvider(props: PlansProps) {
34-
const isMountedRef = useIsMountedRef()
35-
36-
const [state, setState] = useState(initialState)
28+
const [state, setState] = useState(getCachedPublicPlans() || initialState)
3729

3830
const { authData } = useAuth()
3931

32+
const stateRef = useDynamicRef(state)
4033
useEffect(() => {
34+
if (stateRef.current === getCachedPublicPlans() && !authData.appToken)
35+
return
4136
;(async () => {
4237
try {
43-
const response = await fetch(`${constants.API_BASE_URL}/plans`, {
44-
method: 'GET',
45-
headers: {
46-
...getDefaultDevHubHeaders({ appToken: authData.appToken }),
47-
'Content-Type': 'application/json',
48-
},
49-
})
50-
51-
if (!isMountedRef.current) return
52-
53-
const data = (await response.json()) as PlansState
54-
55-
if (!(data && data.plans)) {
56-
throw new Error('Something went wrong')
57-
}
58-
59-
setState({
60-
freePlan: data.freePlan,
61-
freeTrialDays: data.freeTrialDays,
62-
freeTrialPlan: data.freeTrialPlan,
63-
paidPlans: data.plans.filter(plan => plan.amount > 0),
64-
plans: data.plans,
65-
userPlanInfo: data.userPlanInfo,
66-
})
38+
const s = await fetchPlansState(authData.appToken)
39+
setState(s)
6740
} catch (error) {
68-
if (!isMountedRef.current) return
41+
//
6942
}
7043
})()
71-
}, [authData.appToken, authData.plan && authData.plan.id])
44+
}, [
45+
!!authData.appToken,
46+
authData.github.login,
47+
authData.plan && authData.plan.id,
48+
])
7249

7350
return (
7451
<PlansContext.Provider value={state}>

landing/src/helpers/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,9 @@ export function toKebabCase(str: string) {
136136
return matches.map(s => s.toLowerCase()).join('-')
137137
}
138138

139-
export function getPurchaseOrSubscribeRoute(activePlans: Plan[]) {
139+
export function getPurchaseOrSubscribeRoute(
140+
activePlans: Array<Plan | undefined>,
141+
) {
140142
return activePlans.some(p => !!(p && p.amount > 0 && p.interval))
141143
? 'subscribe'
142144
: 'purchase'

0 commit comments

Comments
 (0)