update packages and reduce bundle size

This commit is contained in:
keyan 2023-07-24 17:50:12 -05:00
parent 356f7e072c
commit e87610c45b
10 changed files with 1987 additions and 2610 deletions

View File

@ -1,6 +1,6 @@
import { ApolloClient, InMemoryCache } from '@apollo/client' import { ApolloClient, InMemoryCache } from '@apollo/client'
import { SchemaLink } from '@apollo/client/link/schema' import { SchemaLink } from '@apollo/client/link/schema'
import { makeExecutableSchema } from 'graphql-tools' import { makeExecutableSchema } from '@graphql-tools/schema'
import { getSession } from 'next-auth/client' import { getSession } from 'next-auth/client'
import resolvers from './resolvers' import resolvers from './resolvers'
import typeDefs from './typeDefs' import typeDefs from './typeDefs'

View File

@ -9,6 +9,9 @@ import { Bar } from 'recharts/lib/cartesian/Bar'
import { Tooltip } from 'recharts/lib/component/Tooltip' import { Tooltip } from 'recharts/lib/component/Tooltip'
import { Legend } from 'recharts/lib/component/Legend' import { Legend } from 'recharts/lib/component/Legend'
import { ResponsiveContainer } from 'recharts/lib/component/ResponsiveContainer' import { ResponsiveContainer } from 'recharts/lib/component/ResponsiveContainer'
import { PieChart } from 'recharts/lib/chart/PieChart'
import { Cell } from 'recharts/lib/component/Cell'
import { Pie } from 'recharts/lib/polar/Pie'
import { abbrNum } from '../lib/format' import { abbrNum } from '../lib/format'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
@ -169,3 +172,29 @@ export function WhenComposedChart ({ data, lineNames, areaNames, barNames }) {
</ResponsiveContainer> </ResponsiveContainer>
) )
} }
export function GrowthPieChart ({ data }) {
return (
<ResponsiveContainer width='100%' height={250} minWidth={200}>
<PieChart margin={{ top: 5, right: 5, bottom: 5, left: 5 }}>
<Pie
dataKey='value'
isAnimationActive={false}
data={data}
cx='50%'
cy='50%'
outerRadius={80}
fill='var(--bs-secondary)'
label
>
{
data.map((entry, index) => (
<Cell key={`cell-${index}`} fill={COLORS[index]} />
))
}
</Pie>
<Tooltip />
</PieChart>
</ResponsiveContainer>
)
}

View File

@ -46,9 +46,11 @@ export default function FeeButton ({ parentId, hasImgLink, baseFee, ChildButton,
const { data } = useQuery(query, { pollInterval: 1000, nextFetchPolicy: 'cache-and-network' }) const { data } = useQuery(query, { pollInterval: 1000, nextFetchPolicy: 'cache-and-network' })
const repetition = data?.itemRepetition || 0 const repetition = data?.itemRepetition || 0
const formik = useFormikContext() const formik = useFormikContext()
const boost = formik?.values?.boost || 0 const boost = Number(formik?.values?.boost) || 0
const cost = baseFee * (hasImgLink ? 10 : 1) * Math.pow(10, repetition) + Number(boost) const cost = baseFee * (hasImgLink ? 10 : 1) * Math.pow(10, repetition) + Number(boost)
console.log('cost', cost, 'baseFee', baseFee, repetition, hasImgLink, boost)
const show = alwaysShow || !formik?.isSubmitting const show = alwaysShow || !formik?.isSubmitting
return ( return (
<div className='d-flex align-items-center'> <div className='d-flex align-items-center'>

View File

@ -1,4 +1,4 @@
import * as Yup from 'yup' import { string } from 'yup'
import Toc from './table-of-contents' import Toc from './table-of-contents'
import Badge from 'react-bootstrap/Badge' import Badge from 'react-bootstrap/Badge'
import Button from 'react-bootstrap/Button' import Button from 'react-bootstrap/Button'
@ -12,7 +12,7 @@ import Share from './share'
import CowboyHat from './cowboy-hat' import CowboyHat from './cowboy-hat'
export default function ItemJob ({ item, toc, rank, children }) { export default function ItemJob ({ item, toc, rank, children }) {
const isEmail = Yup.string().email().isValidSync(item.url) const isEmail = string().email().isValidSync(item.url)
return ( return (
<> <>

View File

@ -1,4 +1,4 @@
import * as Yup from 'yup' import { string, ValidationError, number, object, array, addMethod } from 'yup'
import { BOOST_MIN, MAX_POLL_CHOICE_LENGTH, MAX_TITLE_LENGTH, MAX_POLL_NUM_CHOICES, MIN_POLL_NUM_CHOICES, SUBS_NO_JOBS } from './constants' import { BOOST_MIN, MAX_POLL_CHOICE_LENGTH, MAX_TITLE_LENGTH, MAX_POLL_NUM_CHOICES, MIN_POLL_NUM_CHOICES, SUBS_NO_JOBS } from './constants'
import { NAME_QUERY } from '../fragments/users' import { NAME_QUERY } from '../fragments/users'
import { URL_REGEXP, WS_REGEXP } from './url' import { URL_REGEXP, WS_REGEXP } from './url'
@ -13,14 +13,14 @@ export async function ssValidate (schema, data, ...args) {
await schema.validate(data) await schema.validate(data)
} }
} catch (e) { } catch (e) {
if (e instanceof Yup.ValidationError) { if (e instanceof ValidationError) {
throw new Error(`${e.path}: ${e.message}`) throw new Error(`${e.path}: ${e.message}`)
} }
throw e throw e
} }
} }
Yup.addMethod(Yup.string, 'or', function (schemas, msg) { addMethod(string, 'or', function (schemas, msg) {
return this.test({ return this.test({
name: 'or', name: 'or',
message: msg, message: msg,
@ -36,12 +36,12 @@ Yup.addMethod(Yup.string, 'or', function (schemas, msg) {
}) })
}) })
const titleValidator = Yup.string().required('required').trim().max( const titleValidator = string().required('required').trim().max(
MAX_TITLE_LENGTH, MAX_TITLE_LENGTH,
({ max, value }) => `${Math.abs(max - value.length)} too many` ({ max, value }) => `${Math.abs(max - value.length)} too many`
) )
const intValidator = Yup.number().typeError('must be a number').integer('must be whole') const intValidator = number().typeError('must be a number').integer('must be whole')
async function usernameExists (client, name) { async function usernameExists (client, name) {
if (!client) { if (!client) {
@ -70,7 +70,7 @@ export function advPostSchemaMembers (client) {
}, },
message: `must be divisble be ${BOOST_MIN}` message: `must be divisble be ${BOOST_MIN}`
}), }),
forward: Yup.string() forward: string()
.test({ .test({
name: 'name', name: 'name',
test: async name => { test: async name => {
@ -84,12 +84,12 @@ export function advPostSchemaMembers (client) {
export function subSelectSchemaMembers (client) { export function subSelectSchemaMembers (client) {
return { return {
sub: Yup.string().required('required').oneOf(SUBS_NO_JOBS, 'required') sub: string().required('required').oneOf(SUBS_NO_JOBS, 'required')
} }
} }
export function bountySchema (client) { export function bountySchema (client) {
return Yup.object({ return object({
title: titleValidator, title: titleValidator,
bounty: intValidator bounty: intValidator
.min(1000, 'must be at least 1000') .min(1000, 'must be at least 1000')
@ -100,7 +100,7 @@ export function bountySchema (client) {
} }
export function discussionSchema (client) { export function discussionSchema (client) {
return Yup.object({ return object({
title: titleValidator, title: titleValidator,
...advPostSchemaMembers(client), ...advPostSchemaMembers(client),
...subSelectSchemaMembers() ...subSelectSchemaMembers()
@ -108,19 +108,19 @@ export function discussionSchema (client) {
} }
export function linkSchema (client) { export function linkSchema (client) {
return Yup.object({ return object({
title: titleValidator, title: titleValidator,
url: Yup.string().matches(URL_REGEXP, 'invalid url').required('required'), url: string().matches(URL_REGEXP, 'invalid url').required('required'),
...advPostSchemaMembers(client), ...advPostSchemaMembers(client),
...subSelectSchemaMembers() ...subSelectSchemaMembers()
}) })
} }
export function pollSchema (client, numExistingChoices = 0) { export function pollSchema (client, numExistingChoices = 0) {
return Yup.object({ return object({
title: titleValidator, title: titleValidator,
options: Yup.array().of( options: array().of(
Yup.string().trim().test('my-test', 'required', function (value) { string().trim().test('my-test', 'required', function (value) {
return (this.path !== 'options[0]' && this.path !== 'options[1]') || value return (this.path !== 'options[0]' && this.path !== 'options[1]') || value
}).max(MAX_POLL_CHOICE_LENGTH, }).max(MAX_POLL_CHOICE_LENGTH,
({ max, value }) => `${Math.abs(max - value.length)} too many characters` ({ max, value }) => `${Math.abs(max - value.length)} too many characters`
@ -138,8 +138,8 @@ export function pollSchema (client, numExistingChoices = 0) {
} }
export function userSchema (client) { export function userSchema (client) {
return Yup.object({ return object({
name: Yup.string() name: string()
.required('required') .required('required')
.matches(/^[\w_]+$/, 'only letters, numbers, and _') .matches(/^[\w_]+$/, 'only letters, numbers, and _')
.max(32, 'too long') .max(32, 'too long')
@ -154,85 +154,85 @@ export function userSchema (client) {
}) })
} }
export const commentSchema = Yup.object({ export const commentSchema = object({
text: Yup.string().required('required').trim() text: string().required('required').trim()
}) })
export const jobSchema = Yup.object({ export const jobSchema = object({
title: titleValidator, title: titleValidator,
company: Yup.string().required('required').trim(), company: string().required('required').trim(),
text: Yup.string().required('required').trim(), text: string().required('required').trim(),
url: Yup.string() url: string()
.or([Yup.string().email(), Yup.string().url()], 'invalid url or email') .or([string().email(), string().url()], 'invalid url or email')
.required('required'), .required('required'),
maxBid: intValidator.min(0, 'must be at least 0').required('required'), maxBid: intValidator.min(0, 'must be at least 0').required('required'),
location: Yup.string().test( location: string().test(
'no-remote', 'no-remote',
"don't write remote, just check the box", "don't write remote, just check the box",
v => !v?.match(/\bremote\b/gi)) v => !v?.match(/\bremote\b/gi))
.when('remote', { .when('remote', {
is: (value) => !value, is: (value) => !value,
then: Yup.string().required('required').trim() then: string().required('required').trim()
}) })
}) })
export const emailSchema = Yup.object({ export const emailSchema = object({
email: Yup.string().email('email is no good').required('required') email: string().email('email is no good').required('required')
}) })
export const urlSchema = Yup.object({ export const urlSchema = object({
url: Yup.string().matches(URL_REGEXP, 'invalid url').required('required') url: string().matches(URL_REGEXP, 'invalid url').required('required')
}) })
export const namedUrlSchema = Yup.object({ export const namedUrlSchema = object({
text: Yup.string().required('required').trim(), text: string().required('required').trim(),
url: Yup.string().matches(URL_REGEXP, 'invalid url').required('required') url: string().matches(URL_REGEXP, 'invalid url').required('required')
}) })
export const amountSchema = Yup.object({ export const amountSchema = object({
amount: intValidator.required('required').positive('must be positive') amount: intValidator.required('required').positive('must be positive')
}) })
export const settingsSchema = Yup.object({ export const settingsSchema = object({
tipDefault: intValidator.required('required').positive('must be positive'), tipDefault: intValidator.required('required').positive('must be positive'),
fiatCurrency: Yup.string().required('required').oneOf(SUPPORTED_CURRENCIES), fiatCurrency: string().required('required').oneOf(SUPPORTED_CURRENCIES),
nostrPubkey: Yup.string().nullable() nostrPubkey: string().nullable()
.or([ .or([
Yup.string().nullable().matches(NOSTR_PUBKEY_HEX, 'must be 64 hex chars'), string().nullable().matches(NOSTR_PUBKEY_HEX, 'must be 64 hex chars'),
Yup.string().nullable().matches(NOSTR_PUBKEY_BECH32, 'invalid bech32 encoding')], 'invalid pubkey'), string().nullable().matches(NOSTR_PUBKEY_BECH32, 'invalid bech32 encoding')], 'invalid pubkey'),
nostrRelays: Yup.array().of( nostrRelays: array().of(
Yup.string().matches(WS_REGEXP, 'invalid web socket address') string().matches(WS_REGEXP, 'invalid web socket address')
).max(NOSTR_MAX_RELAY_NUM, ).max(NOSTR_MAX_RELAY_NUM,
({ max, value }) => `${Math.abs(max - value.length)} too many`) ({ max, value }) => `${Math.abs(max - value.length)} too many`)
}) })
const warningMessage = 'If I logout, even accidentally, I will never be able to access my account again' const warningMessage = 'If I logout, even accidentally, I will never be able to access my account again'
export const lastAuthRemovalSchema = Yup.object({ export const lastAuthRemovalSchema = object({
warning: Yup.string().matches(warningMessage, 'does not match').required('required') warning: string().matches(warningMessage, 'does not match').required('required')
}) })
export const withdrawlSchema = Yup.object({ export const withdrawlSchema = object({
invoice: Yup.string().required('required').trim(), invoice: string().required('required').trim(),
maxFee: intValidator.required('required').min(0, 'must be at least 0') maxFee: intValidator.required('required').min(0, 'must be at least 0')
}) })
export const lnAddrSchema = Yup.object({ export const lnAddrSchema = object({
addr: Yup.string().email('address is no good').required('required'), addr: string().email('address is no good').required('required'),
amount: intValidator.required('required').positive('must be positive'), amount: intValidator.required('required').positive('must be positive'),
maxFee: intValidator.required('required').min(0, 'must be at least 0') maxFee: intValidator.required('required').min(0, 'must be at least 0')
}) })
export const bioSchema = Yup.object({ export const bioSchema = object({
bio: Yup.string().required('required').trim() bio: string().required('required').trim()
}) })
export const inviteSchema = Yup.object({ export const inviteSchema = object({
gift: intValidator.positive('must be greater than 0').required('required'), gift: intValidator.positive('must be greater than 0').required('required'),
limit: intValidator.positive('must be positive') limit: intValidator.positive('must be positive')
}) })
export const pushSubscriptionSchema = Yup.object({ export const pushSubscriptionSchema = object({
endpoint: Yup.string().url().required('required').trim(), endpoint: string().url().required('required').trim(),
p256dh: Yup.string().required('required').trim(), p256dh: string().required('required').trim(),
auth: Yup.string().required('required').trim() auth: string().required('required').trim()
}) })

4331
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -10,79 +10,75 @@
}, },
"dependencies": { "dependencies": {
"@apollo/client": "^3.7.17", "@apollo/client": "^3.7.17",
"@apollo/server": "^4.7.5", "@apollo/server": "^4.8.1",
"@as-integrations/next": "^2.0.1", "@as-integrations/next": "^2.0.1",
"@graphql-tools/schema": "^10.0.0",
"@noble/secp256k1": "^1.7.1", "@noble/secp256k1": "^1.7.1",
"@opensearch-project/opensearch": "^1.1.0", "@opensearch-project/opensearch": "^2.3.1",
"@prisma/client": "^2.30.3", "@prisma/client": "^2.30.3",
"@synonymdev/slashtags-auth": "^1.0.0-alpha.5", "@synonymdev/slashtags-auth": "^1.0.0-alpha.5",
"@synonymdev/slashtags-sdk": "^1.0.0-alpha.36", "@synonymdev/slashtags-sdk": "^1.0.0-alpha.36",
"acorn": "^8.10.0", "acorn": "^8.10.0",
"ajv": "^8.12.0", "ajv": "^8.12.0",
"async-retry": "^1.3.1", "async-retry": "^1.3.1",
"aws-sdk": "^2.1248.0", "aws-sdk": "^2.1421.0",
"babel-plugin-inline-react-svg": "^2.0.1", "babel-plugin-inline-react-svg": "^2.0.2",
"bech32": "^2.0.0", "bech32": "^2.0.0",
"bolt11": "^1.4.0", "bolt11": "^1.4.1",
"bootstrap": "^5.3.0", "bootstrap": "^5.3.0",
"browserslist": "^4.21.4", "browserslist": "^4.21.4",
"canonical-json": "0.0.4", "canonical-json": "0.0.4",
"clipboard-copy": "^4.0.1", "clipboard-copy": "^4.0.1",
"cross-fetch": "^3.1.5", "cross-fetch": "^3.1.8",
"domino": "^2.1.6", "domino": "^2.1.6",
"eslint-config-next": "^13.4.10", "eslint-config-next": "^13.4.12",
"formik": "^2.2.6", "formik": "^2.4.2",
"github-slugger": "^1.5.0", "github-slugger": "^1.5.0",
"graphql": "^16.7.1", "graphql": "^16.7.1",
"graphql-tag": "^2.12.6", "graphql-tag": "^2.12.6",
"graphql-tools": "^8.3.10",
"graphql-type-json": "^0.3.2", "graphql-type-json": "^0.3.2",
"jquery": "^3.6.1", "ln-service": "^56.9.0",
"ln-service": "^54.2.6", "mathjs": "^11.9.1",
"mathjs": "^11.8.2",
"mdast-util-find-and-replace": "^1.1.1", "mdast-util-find-and-replace": "^1.1.1",
"mdast-util-from-markdown": "^1.2.0", "mdast-util-from-markdown": "^1.3.1",
"mdast-util-to-string": "^3.1.0", "mdast-util-to-string": "^3.2.0",
"next": "^13.4.10", "next": "^13.4.12",
"next-auth": "^3.29.10", "next-auth": "^3.29.10",
"next-plausible": "^3.10.1", "next-plausible": "^3.10.1",
"next-seo": "^4.29.0", "next-seo": "^4.29.0",
"nextjs-progressbar": "0.0.16", "nextjs-progressbar": "0.0.16",
"node-s3-url-encode": "^0.0.4", "node-s3-url-encode": "^0.0.4",
"nostr": "^0.2.7", "nostr": "^0.2.8",
"opentimestamps": "^0.4.9", "opentimestamps": "^0.4.9",
"page-metadata-parser": "^1.1.4", "page-metadata-parser": "^1.1.4",
"pageres": "^7.1.0", "pageres": "^7.1.0",
"pg-boss": "^7.4.0", "pg-boss": "^9.0.3",
"popper.js": "^1.16.1",
"prisma": "^2.30.3", "prisma": "^2.30.3",
"qrcode.react": "^3.1.0", "qrcode.react": "^3.1.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-avatar-editor": "^13.0.0", "react-avatar-editor": "^13.0.0",
"react-bootstrap": "^2.8.0", "react-bootstrap": "^2.8.0",
"react-countdown": "^2.3.3", "react-countdown": "^2.3.5",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-longpressable": "^1.1.1", "react-longpressable": "^1.1.1",
"react-markdown": "^8.0.7", "react-markdown": "^8.0.7",
"react-string-replace": "^0.4.4", "react-string-replace": "^0.4.4",
"react-syntax-highlighter": "^15.5.0", "react-syntax-highlighter": "^15.5.0",
"react-textarea-autosize": "^8.3.4", "react-textarea-autosize": "^8.5.2",
"react-twitter-embed": "^4.0.4", "react-twitter-embed": "^4.0.4",
"react-youtube": "^7.14.0", "react-youtube": "^7.14.0",
"recharts": "^2.1.16", "recharts": "^2.7.2",
"remark-directive": "^2.0.1", "remark-directive": "^2.0.1",
"remark-gfm": "^3.0.1", "remark-gfm": "^3.0.1",
"remove-markdown": "^0.3.0", "remove-markdown": "^0.3.0",
"sass": "^1.56.0", "sass": "^1.64.1",
"secp256k1": "^4.0.3", "secp256k1": "^4.0.3",
"swr": "^1.3.0", "tldts": "^5.7.112",
"tldts": "^5.7.104",
"typescript": "^5.1.6", "typescript": "^5.1.6",
"unist-util-visit": "^4.1.1", "unist-util-visit": "^4.1.2",
"url-unshort": "^6.1.0", "url-unshort": "^6.1.0",
"uuid": "^8.3.2",
"web-push": "^3.6.2", "web-push": "^3.6.2",
"webln": "^0.2.2", "webln": "^0.3.2",
"webpack": "^5.88.2", "webpack": "^5.88.2",
"workbox-precaching": "^7.0.0", "workbox-precaching": "^7.0.0",
"workbox-recipes": "^7.0.0", "workbox-recipes": "^7.0.0",
@ -90,7 +86,7 @@
"workbox-strategies": "^7.0.0", "workbox-strategies": "^7.0.0",
"workbox-webpack-plugin": "^7.0.0", "workbox-webpack-plugin": "^7.0.0",
"workbox-window": "^7.0.0", "workbox-window": "^7.0.0",
"yup": "^0.32.11" "yup": "^1.2.0"
}, },
"engines": { "engines": {
"node": "16.16.0" "node": "16.16.0"
@ -108,10 +104,10 @@
} }
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.2", "@babel/core": "^7.22.9",
"@babel/eslint-parser": "^7.19.1", "@babel/eslint-parser": "^7.22.9",
"eslint": "^7.32.0", "eslint": "^8.45.0",
"eslint-plugin-compat": "^4.1.4", "eslint-plugin-compat": "^4.1.4",
"standard": "^16.0.4" "standard": "^17.1.0"
} }
} }

View File

@ -5,10 +5,14 @@ import { getGetServerSideProps } from '../../api/ssrApollo'
import { CopyInput, Form, Select } from '../../components/form' import { CopyInput, Form, Select } from '../../components/form'
import { CenterLayout } from '../../components/layout' import { CenterLayout } from '../../components/layout'
import { useMe } from '../../components/me' import { useMe } from '../../components/me'
import { WhenComposedChart } from '../../components/when-charts'
import { useQuery } from '@apollo/client' import { useQuery } from '@apollo/client'
import PageLoading from '../../components/page-loading' import PageLoading from '../../components/page-loading'
import { WHENS } from '../../lib/constants' import { WHENS } from '../../lib/constants'
import dynamic from 'next/dynamic'
const WhenComposedChart = dynamic(() => import('../../components/charts').then(mod => mod.WhenComposedChart), {
loading: () => <div>Loading...</div>
})
const REFERRALS = gql` const REFERRALS = gql`
query Referrals($when: String!) query Referrals($when: String!)

View File

@ -17,6 +17,11 @@ import Countdown from 'react-countdown'
import { abbrNum } from '../lib/format' import { abbrNum } from '../lib/format'
import PageLoading from '../components/page-loading' import PageLoading from '../components/page-loading'
import { useShowModal } from '../components/modal' import { useShowModal } from '../components/modal'
import dynamic from 'next/dynamic'
const GrowthPieChart = dynamic(() => import('../components/charts').then(mod => mod.GrowthPieChart), {
loading: () => <div>Loading...</div>
})
const REWARDS = gql` const REWARDS = gql`
{ {
@ -97,32 +102,6 @@ const COLORS = [
'var(--bs-grey)' 'var(--bs-grey)'
] ]
function GrowthPieChart ({ data }) {
return (
<ResponsiveContainer width='100%' height={250} minWidth={200}>
<PieChart margin={{ top: 5, right: 5, bottom: 5, left: 5 }}>
<Pie
dataKey='value'
isAnimationActive={false}
data={data}
cx='50%'
cy='50%'
outerRadius={80}
fill='var(--bs-secondary)'
label
>
{
data.map((entry, index) => (
<Cell key={`cell-${index}`} fill={COLORS[index]} />
))
}
</Pie>
<Tooltip />
</PieChart>
</ResponsiveContainer>
)
}
export function DonateButton () { export function DonateButton () {
const showModal = useShowModal() const showModal = useShowModal()
const [donateToRewards] = useMutation( const [donateToRewards] = useMutation(

View File

@ -4,8 +4,15 @@ import Layout from '../../components/layout'
import Col from 'react-bootstrap/Col' import Col from 'react-bootstrap/Col'
import Row from 'react-bootstrap/Row' import Row from 'react-bootstrap/Row'
import { UsageHeader } from '../../components/usage-header' import { UsageHeader } from '../../components/usage-header'
import { WhenLineChart, WhenAreaChart } from '../../components/when-charts'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import dynamic from "next/dynamic"
const WhenAreaChart = dynamic(() => import('../../components/charts').then(mod => mod.WhenAreaChart), {
loading: () => <div>Loading...</div>
});
const WhenLineChart = dynamic(() => import('../../components/charts').then(mod => mod.WhenLineChart), {
loading: () => <div>Loading...</div>
});
export const getServerSideProps = getGetServerSideProps( export const getServerSideProps = getGetServerSideProps(
gql` gql`
@ -61,6 +68,7 @@ export default function Growth ({
const router = useRouter() const router = useRouter()
const { when } = router.query const { when } = router.query
const avg = ['year', 'forever'].includes(when) ? 'avg daily ' : '' const avg = ['year', 'forever'].includes(when) ? 'avg daily ' : ''
return ( return (
<Layout> <Layout>
<UsageHeader /> <UsageHeader />