Init route

This commit is contained in:
Eric Bailey
2024-11-16 16:09:55 -06:00
parent 0ee20731d4
commit 13e871f973
11 changed files with 417 additions and 0 deletions
+6
View File
@@ -86,6 +86,7 @@ import {
StarterPackScreenShort,
} from '#/screens/StarterPack/StarterPackScreen'
import {Wizard} from '#/screens/StarterPack/Wizard'
import {Subscriptions} from '#/screens/Subscriptions'
import {useTheme} from '#/alf'
import {router} from '#/routes'
import {Referrer} from '../modules/expo-bluesky-swiss-army'
@@ -416,6 +417,11 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) {
getComponent={() => Wizard}
options={{title: title(msg`Edit your starter pack`), requireAuth: true}}
/>
<Stack.Screen
name="Subscriptions"
getComponent={() => Subscriptions}
options={{title: title(msg`Subscriptions`), requireAuth: true}}
/>
</>
)
}
+9
View File
@@ -0,0 +1,9 @@
import {createSinglePathSVG} from './TEMPLATE'
export const Gift1_Stroke2_Corner0_Rounded = createSinglePathSVG({
path: 'M6 4.667A2.667 2.667 0 0 1 8.667 2c1.34 0 2.538.608 3.333 1.564A4.324 4.324 0 0 1 15.333 2 2.667 2.667 0 0 1 18 4.667c0 .859-.25 1.66-.681 2.333H20a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1v7a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-7a1 1 0 0 1-1-1V8a1 1 0 0 1 1-1h2.681A4.313 4.313 0 0 1 6 4.667ZM10.333 7H11v-.667A2.333 2.333 0 0 0 8.667 4 .667.667 0 0 0 8 4.667 2.333 2.333 0 0 0 10.333 7ZM13 6.333V7h.667A2.333 2.333 0 0 0 16 4.667.667.667 0 0 0 15.333 4 2.333 2.333 0 0 0 13 6.333ZM11 9H5v2h6V9Zm2 2V9h6v2h-6Zm-2 2H6v6h5v-6Zm2 6v-6h5v6h-5Z',
})
export const Gift1_Filled_Corner0_Rounded = createSinglePathSVG({
path: 'M6 4.667A2.667 2.667 0 0 1 8.667 2c1.34 0 2.538.608 3.333 1.564A4.324 4.324 0 0 1 15.333 2 2.667 2.667 0 0 1 18 4.667c0 .859-.25 1.66-.681 2.333H20a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-7V7h.667A2.333 2.333 0 0 0 16 4.667.667.667 0 0 0 15.333 4 2.333 2.333 0 0 0 13 6.333V7h-2v-.667A2.333 2.333 0 0 0 8.667 4 .667.667 0 0 0 8 4.667 2.333 2.333 0 0 0 10.333 7H11v4H4a1 1 0 0 1-1-1V8a1 1 0 0 1 1-1h2.681A4.313 4.313 0 0 1 6 4.667ZM11 13H4v7a1 1 0 0 0 1 1h6v-8Zm9 0h-7v8h6a1 1 0 0 0 1-1v-7Z',
})
+302
View File
@@ -0,0 +1,302 @@
import React from 'react'
import {deviceLocales} from '#/locale/deviceLocales'
import {useGeolocation} from '#/state/geolocation'
import {useLanguagePrefs} from '#/state/preferences'
/**
* From react-native-localize
*
* MIT License
* Copyright (c) 2017-present, Mathieu Acthernoene
*
* @see https://github.com/zoontek/react-native-localize/blob/master/LICENSE
* @see https://github.com/zoontek/react-native-localize/blob/ee5bf25e0bb8f3b8e4f3fd055f67ad46269c81ea/src/constants.ts
*/
export const countryCodeToCurrency: Record<string, string> = {
ad: 'eur',
ae: 'aed',
af: 'afn',
ag: 'xcd',
ai: 'xcd',
al: 'all',
am: 'amd',
an: 'ang',
ao: 'aoa',
ar: 'ars',
as: 'usd',
at: 'eur',
au: 'aud',
aw: 'awg',
ax: 'eur',
az: 'azn',
ba: 'bam',
bb: 'bbd',
bd: 'bdt',
be: 'eur',
bf: 'xof',
bg: 'bgn',
bh: 'bhd',
bi: 'bif',
bj: 'xof',
bl: 'eur',
bm: 'bmd',
bn: 'bnd',
bo: 'bob',
bq: 'usd',
br: 'brl',
bs: 'bsd',
bt: 'btn',
bv: 'nok',
bw: 'bwp',
by: 'byn',
bz: 'bzd',
ca: 'cad',
cc: 'aud',
cd: 'cdf',
cf: 'xaf',
cg: 'xaf',
ch: 'chf',
ci: 'xof',
ck: 'nzd',
cl: 'clp',
cm: 'xaf',
cn: 'cny',
co: 'cop',
cr: 'crc',
cu: 'cup',
cv: 'cve',
cw: 'ang',
cx: 'aud',
cy: 'eur',
cz: 'czk',
de: 'eur',
dj: 'djf',
dk: 'dkk',
dm: 'xcd',
do: 'dop',
dz: 'dzd',
ec: 'usd',
ee: 'eur',
eg: 'egp',
eh: 'mad',
er: 'ern',
es: 'eur',
et: 'etb',
fi: 'eur',
fj: 'fjd',
fk: 'fkp',
fm: 'usd',
fo: 'dkk',
fr: 'eur',
ga: 'xaf',
gb: 'gbp',
gd: 'xcd',
ge: 'gel',
gf: 'eur',
gg: 'gbp',
gh: 'ghs',
gi: 'gip',
gl: 'dkk',
gm: 'gmd',
gn: 'gnf',
gp: 'eur',
gq: 'xaf',
gr: 'eur',
gs: 'gbp',
gt: 'gtq',
gu: 'usd',
gw: 'xof',
gy: 'gyd',
hk: 'hkd',
hm: 'aud',
hn: 'hnl',
hr: 'hrk',
ht: 'htg',
hu: 'huf',
id: 'idr',
ie: 'eur',
il: 'ils',
im: 'gbp',
in: 'inr',
io: 'usd',
iq: 'iqd',
ir: 'irr',
is: 'isk',
it: 'eur',
je: 'gbp',
jm: 'jmd',
jo: 'jod',
jp: 'jpy',
ke: 'kes',
kg: 'kgs',
kh: 'khr',
ki: 'aud',
km: 'kmf',
kn: 'xcd',
kp: 'kpw',
kr: 'krw',
kw: 'kwd',
ky: 'kyd',
kz: 'kzt',
la: 'lak',
lb: 'lbp',
lc: 'xcd',
li: 'chf',
lk: 'lkr',
lr: 'lrd',
ls: 'lsl',
lt: 'eur',
lu: 'eur',
lv: 'eur',
ly: 'lyd',
ma: 'mad',
mc: 'eur',
md: 'mdl',
me: 'eur',
mf: 'eur',
mg: 'mga',
mh: 'usd',
mk: 'mkd',
ml: 'xof',
mm: 'mmk',
mn: 'mnt',
mo: 'mop',
mp: 'usd',
mq: 'eur',
mr: 'mro',
ms: 'xcd',
mt: 'eur',
mu: 'mur',
mv: 'mvr',
mw: 'mwk',
mx: 'mxn',
my: 'myr',
mz: 'mzn',
na: 'nad',
nc: 'xpf',
ne: 'xof',
nf: 'aud',
ng: 'ngn',
ni: 'nio',
nl: 'eur',
no: 'nok',
np: 'npr',
nr: 'aud',
nu: 'nzd',
nz: 'nzd',
om: 'omr',
pa: 'pab',
pe: 'pen',
pf: 'xpf',
pg: 'pgk',
ph: 'php',
pk: 'pkr',
pl: 'pln',
pm: 'eur',
pn: 'nzd',
pr: 'usd',
ps: 'ils',
pt: 'eur',
pw: 'usd',
py: 'pyg',
qa: 'qar',
re: 'eur',
ro: 'ron',
rs: 'rsd',
ru: 'rub',
rw: 'rwf',
sa: 'sar',
sb: 'sbd',
sc: 'scr',
sd: 'sdg',
se: 'sek',
sg: 'sgd',
sh: 'shp',
si: 'eur',
sj: 'nok',
sk: 'eur',
sl: 'sll',
sm: 'eur',
sn: 'xof',
so: 'sos',
sr: 'srd',
ss: 'ssp',
st: 'std',
sv: 'svc',
sx: 'ang',
sy: 'syp',
sz: 'szl',
tc: 'usd',
td: 'xaf',
tf: 'eur',
tg: 'xof',
th: 'thb',
tj: 'tjs',
tk: 'nzd',
tl: 'usd',
tm: 'tmt',
tn: 'tnd',
to: 'top',
tr: 'try',
tt: 'ttd',
tv: 'aud',
tw: 'twd',
tz: 'tzs',
ua: 'uah',
ug: 'ugx',
um: 'usd',
us: 'usd',
uy: 'uyu',
uz: 'uzs',
va: 'eur',
vc: 'xcd',
ve: 'vef',
vg: 'usd',
vi: 'usd',
vn: 'vnd',
vu: 'vuv',
wf: 'xpf',
ws: 'wst',
ye: 'yer',
yt: 'eur',
za: 'zar',
zm: 'zmw',
zw: 'zwl',
}
/**
* Best-guess currency formatting.
*
* Attempts to use `getLocales` from `expo-localization` if available,
* otherwise falls back to the `persisted.appLanguage` setting, and geolocation
* API for region.
*/
export function useCurrencyFormatter(
options?: Parameters<typeof Intl.NumberFormat>[1],
) {
const {geolocation} = useGeolocation()
const {appLanguage} = useLanguagePrefs()
return React.useMemo(() => {
const locale = deviceLocales.at(0)
const languageTag = locale?.languageTag || appLanguage || 'en-US'
const countryCode = (
locale?.regionCode ||
geolocation?.countryCode ||
'us'
).toLowerCase()
const currency = countryCodeToCurrency[countryCode]
const format = new Intl.NumberFormat(languageTag, {
...(options || {}),
style: 'currency',
currency: currency,
}).format
return {
format,
currency,
countryCode,
languageTag,
}
}, [geolocation, appLanguage, options])
}
+1
View File
@@ -56,6 +56,7 @@ export type CommonNavigatorParams = {
StarterPackShort: {code: string}
StarterPackWizard: undefined
StarterPackEdit: {rkey?: string}
Subscriptions: undefined
}
export type BottomTabNavigatorParams = CommonNavigatorParams & {
+1
View File
@@ -63,4 +63,5 @@ export const router = new Router({
StarterPack: '/starter-pack/:name/:rkey',
StarterPackShort: '/starter-pack-short/:code',
StarterPackWizard: '/starter-pack/create',
Subscriptions: '/subscriptions',
})
+51
View File
@@ -0,0 +1,51 @@
import React from 'react'
import {View} from 'react-native'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {NativeStackScreenProps} from '@react-navigation/native-stack'
// import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
// import {isAndroid, isIOS} from '#/platform/detection'
import {CommonNavigatorParams} from '#/lib/routes/types'
// import {useEntitlements} from '#/state/purchases/subscriptions/useEntitlements'
import {ViewHeader} from '#/view/com/util/ViewHeader'
import {CenteredView} from '#/view/com/util/Views'
import {atoms as a} from '#/alf'
// import {Button, ButtonText, ButtonIcon} from '#/components/Button'
import * as Layout from '#/components/Layout'
// import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography'
// import {GradientFill} from '#/components/GradientFill'
// import {Divider} from '#/components/Divider'
// import {CheckThick_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
// import * as Dialog from '#/components/Dialog'
// import * as Toggle from '#/components/forms/Toggle'
// import {Admonition} from '#/components/Admonition'
export type ScreenProps = NativeStackScreenProps<
CommonNavigatorParams,
'Subscriptions'
>
export function Subscriptions(_props: ScreenProps) {
const {_} = useLingui()
// const currentPlatform = isAndroid ? 'android' : isIOS ? 'ios' : 'web'
// const activeSubscriptionPlatform =
// activeSubscription?.subscription?.state?.platform
// const isManageable =
// !activeSubscriptionPlatform ||
// activeSubscriptionPlatform === currentPlatform
return (
<Layout.Screen>
<CenteredView sideBorders={true}>
<ViewHeader title={_(msg`Subscriptions`)} />
<View style={[a.px_xl, a.py_xl, a.gap_lg]}>
<Text style={[a.text_2xl]}>Subs</Text>
</View>
</CenteredView>
</Layout.Screen>
)
}
@@ -16,6 +16,7 @@ import {atoms} from '#/alf'
import {useTheme} from '#/alf'
import {atoms as a} from '#/alf'
import {ColorPalette_Stroke2_Corner0_Rounded as ColorPalette} from '#/components/icons/ColorPalette'
import {Gift1_Stroke2_Corner0_Rounded as Gift} from '#/components/icons/Gift1'
import {Hashtag_Stroke2_Corner0_Rounded as FeedsIcon} from '#/components/icons/Hashtag'
import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
import {Link} from '#/components/Link'
@@ -72,6 +73,9 @@ export function HomeHeaderLayoutMobile({
]}>
{IS_DEV && (
<>
<Link label="Jump to subscriptions" to="/subscriptions">
<Gift size="md" />
</Link>
<Link
label="View storybook"
to="/sys/debug"
+38
View File
@@ -30,6 +30,10 @@ import {
Bell_Stroke2_Corner0_Rounded as Bell,
} from '#/components/icons/Bell'
import {BulletList_Stroke2_Corner0_Rounded as List} from '#/components/icons/BulletList'
import {
Gift1_Filled_Corner0_Rounded as GiftFilled,
Gift1_Stroke2_Corner0_Rounded as Gift,
} from '#/components/icons/Gift1'
import {
Hashtag_Filled_Corner0_Rounded as HashtagFilled,
Hashtag_Stroke2_Corner0_Rounded as Hashtag,
@@ -203,6 +207,11 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
setDrawerOpen(false)
}, [navigation, setDrawerOpen])
const onPressSubscriptions = React.useCallback(() => {
navigation.navigate('Subscriptions')
setDrawerOpen(false)
}, [navigation, setDrawerOpen])
const onPressFeedback = React.useCallback(() => {
Linking.openURL(
FEEDBACK_FORM_URL({
@@ -264,6 +273,10 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
onPress={onPressProfile}
/>
<SettingsMenuItem onPress={onPressSettings} />
<SubscriptionsMenuItem
isActive={false}
onPress={onPressSubscriptions}
/>
</>
) : (
<>
@@ -533,6 +546,31 @@ let SettingsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => {
}
SettingsMenuItem = React.memo(SettingsMenuItem)
let SubscriptionsMenuItem = ({
isActive,
onPress,
}: {
isActive: boolean
onPress: () => void
}): React.ReactNode => {
const {_} = useLingui()
const t = useTheme()
return (
<MenuItem
icon={
isActive ? (
<GiftFilled style={[t.atoms.text]} width={iconWidth} />
) : (
<Gift style={[t.atoms.text]} width={iconWidth} />
)
}
label={_(msg`Support Bluesky`)}
onPress={onPress}
/>
)
}
SubscriptionsMenuItem = React.memo(SubscriptionsMenuItem)
function MenuItem({icon, label, count, bold, onPress}: MenuItemProps) {
const t = useTheme()
return (