From 13e871f973a07c012589372c67be78a743b8766d Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Sat, 16 Nov 2024 16:09:55 -0600 Subject: [PATCH] Init route --- assets/icons/gift1_filled_corner0_rounded.svg | 1 + .../icons/gift1_stroke2_corner0_rounded.svg | 1 + bskyweb/cmd/bskyweb/server.go | 3 + src/Navigation.tsx | 6 + src/components/icons/Gift1.tsx | 9 + src/lib/currency.ts | 302 ++++++++++++++++++ src/lib/routes/types.ts | 1 + src/routes.ts | 1 + src/screens/Subscriptions/index.tsx | 51 +++ src/view/com/home/HomeHeaderLayoutMobile.tsx | 4 + src/view/shell/Drawer.tsx | 38 +++ 11 files changed, 417 insertions(+) create mode 100644 assets/icons/gift1_filled_corner0_rounded.svg create mode 100644 assets/icons/gift1_stroke2_corner0_rounded.svg create mode 100644 src/components/icons/Gift1.tsx create mode 100644 src/lib/currency.ts create mode 100644 src/screens/Subscriptions/index.tsx diff --git a/assets/icons/gift1_filled_corner0_rounded.svg b/assets/icons/gift1_filled_corner0_rounded.svg new file mode 100644 index 0000000000..3b5c47abfc --- /dev/null +++ b/assets/icons/gift1_filled_corner0_rounded.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/gift1_stroke2_corner0_rounded.svg b/assets/icons/gift1_stroke2_corner0_rounded.svg new file mode 100644 index 0000000000..828de5df72 --- /dev/null +++ b/assets/icons/gift1_stroke2_corner0_rounded.svg @@ -0,0 +1 @@ + diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go index 19252f7654..d98ae21ee4 100644 --- a/bskyweb/cmd/bskyweb/server.go +++ b/bskyweb/cmd/bskyweb/server.go @@ -295,6 +295,9 @@ func serve(cctx *cli.Context) error { e.GET("/starter-pack-short/:code", server.WebGeneric) e.GET("/start/:handleOrDID/:rkey", server.WebStarterPack) + // subscription + e.GET("/subscriptions", server.WebGeneric) + // ipcc e.GET("/ipcc", server.WebIpCC) diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 0b162d3634..d7d9d3a295 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -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}} /> + Subscriptions} + options={{title: title(msg`Subscriptions`), requireAuth: true}} + /> ) } diff --git a/src/components/icons/Gift1.tsx b/src/components/icons/Gift1.tsx new file mode 100644 index 0000000000..874798af7d --- /dev/null +++ b/src/components/icons/Gift1.tsx @@ -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', +}) diff --git a/src/lib/currency.ts b/src/lib/currency.ts new file mode 100644 index 0000000000..d1884b797d --- /dev/null +++ b/src/lib/currency.ts @@ -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 = { + 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[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]) +} diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts index 9e3407261d..123b33ed98 100644 --- a/src/lib/routes/types.ts +++ b/src/lib/routes/types.ts @@ -56,6 +56,7 @@ export type CommonNavigatorParams = { StarterPackShort: {code: string} StarterPackWizard: undefined StarterPackEdit: {rkey?: string} + Subscriptions: undefined } export type BottomTabNavigatorParams = CommonNavigatorParams & { diff --git a/src/routes.ts b/src/routes.ts index 188665d849..494a6f7bb7 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -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', }) diff --git a/src/screens/Subscriptions/index.tsx b/src/screens/Subscriptions/index.tsx new file mode 100644 index 0000000000..1fc6dfec20 --- /dev/null +++ b/src/screens/Subscriptions/index.tsx @@ -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 ( + + + + + + Subs + + + + ) +} diff --git a/src/view/com/home/HomeHeaderLayoutMobile.tsx b/src/view/com/home/HomeHeaderLayoutMobile.tsx index 8323960924..3183ca889d 100644 --- a/src/view/com/home/HomeHeaderLayoutMobile.tsx +++ b/src/view/com/home/HomeHeaderLayoutMobile.tsx @@ -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 && ( <> + + + ): 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} /> + ) : ( <> @@ -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 ( + + ) : ( + + ) + } + label={_(msg`Support Bluesky`)} + onPress={onPress} + /> + ) +} +SubscriptionsMenuItem = React.memo(SubscriptionsMenuItem) + function MenuItem({icon, label, count, bold, onPress}: MenuItemProps) { const t = useTheme() return (