diff options
author | Eric Bailey <git@esb.lol> | 2024-09-10 14:23:30 -0500 |
---|---|---|
committer | Eric Bailey <git@esb.lol> | 2024-09-11 19:58:16 -0500 |
commit | 3c8b3b47823475b93a92dcf82a4cabbda625c323 (patch) | |
tree | 3d65f28f89142148579c6caa005c212327fc315b /src | |
parent | 76c584d981f195a580e132b786e101b3d0d32380 (diff) | |
download | voidsky-3c8b3b47823475b93a92dcf82a4cabbda625c323.tar.zst |
Progress on desktoip
Diffstat (limited to 'src')
-rw-r--r-- | src/alf/index.tsx | 18 | ||||
-rw-r--r-- | src/components/dialogs/nudges/TenMillion.tsx | 330 | ||||
-rw-r--r-- | src/view/icons/Logomark.tsx | 29 |
3 files changed, 331 insertions, 46 deletions
diff --git a/src/alf/index.tsx b/src/alf/index.tsx index 5fa7d3b1a..d699de6a5 100644 --- a/src/alf/index.tsx +++ b/src/alf/index.tsx @@ -18,9 +18,17 @@ export * from '#/alf/util/themeSelector' export const Context = React.createContext<{ themeName: ThemeName theme: Theme + themes: ReturnType<typeof createThemes> }>({ themeName: 'light', theme: defaultTheme, + themes: createThemes({ + hues: { + primary: BLUE_HUE, + negative: RED_HUE, + positive: GREEN_HUE, + }, + }), }) export function ThemeProvider({ @@ -42,18 +50,22 @@ export function ThemeProvider({ <Context.Provider value={React.useMemo( () => ({ + themes, themeName: themeName, theme: theme, }), - [theme, themeName], + [theme, themeName, themes], )}> {children} </Context.Provider> ) } -export function useTheme() { - return React.useContext(Context).theme +export function useTheme(theme?: ThemeName) { + const ctx = React.useContext(Context) + return React.useMemo(() => { + return theme ? ctx.themes[theme] : ctx.theme + }, [theme, ctx]) } export function useBreakpoints() { diff --git a/src/components/dialogs/nudges/TenMillion.tsx b/src/components/dialogs/nudges/TenMillion.tsx index 9b5d5eae6..869056977 100644 --- a/src/components/dialogs/nudges/TenMillion.tsx +++ b/src/components/dialogs/nudges/TenMillion.tsx @@ -1,25 +1,74 @@ import React from 'react' -import {useLingui} from '@lingui/react' -import {msg} from '@lingui/macro' import {View} from 'react-native' import ViewShot from 'react-native-view-shot' +import {moderateProfile} from '@atproto/api' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' -import {atoms as a, useBreakpoints, tokens} from '#/alf' +import {sanitizeDisplayName} from '#/lib/strings/display-names' +import {sanitizeHandle} from '#/lib/strings/handles' +import {isNative} from '#/platform/detection' +import {useModerationOpts} from '#/state/preferences/moderation-opts' +import {useProfileQuery} from '#/state/queries/profile' +import {useSession} from '#/state/session' +import {useComposerControls} from 'state/shell' +import {formatCount} from '#/view/com/util/numeric/format' +import {UserAvatar} from '#/view/com/util/UserAvatar' +import {Logomark} from '#/view/icons/Logomark' +import { + atoms as a, + ThemeProvider, + tokens, + useBreakpoints, + useTheme, +} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' -import {Text} from '#/components/Typography' +import {useContext} from '#/components/dialogs/nudges' +import {Divider} from '#/components/Divider' import {GradientFill} from '#/components/GradientFill' -import {Button, ButtonText} from '#/components/Button' -import {useComposerControls} from 'state/shell' +import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox' +import {Image_Stroke2_Corner0_Rounded as ImageIcon} from '#/components/icons/Image' +import {Loader} from '#/components/Loader' +import {Text} from '#/components/Typography' -import {useContext} from '#/components/dialogs/nudges' +const RATIO = 8 / 10 +const WIDTH = 2000 +const HEIGHT = WIDTH * RATIO + +function getFontSize(count: number) { + const length = count.toString().length + if (length < 7) { + return 80 + } else if (length < 5) { + return 100 + } else { + return 70 + } +} export function TenMillion() { - const {_} = useLingui() + const t = useTheme() + const lightTheme = useTheme('light') + const {_, i18n} = useLingui() const {controls} = useContext() const {gtMobile} = useBreakpoints() const {openComposer} = useComposerControls() - const imageRef = React.useRef<ViewShot>(null) + const {currentAccount} = useSession() + const {isLoading: isProfileLoading, data: profile} = useProfileQuery({ + did: currentAccount!.did, + }) // TODO PWI + const moderationOpts = useModerationOpts() + const moderation = React.useMemo(() => { + return profile && moderationOpts + ? moderateProfile(profile, moderationOpts) + : undefined + }, [profile, moderationOpts]) + + const isLoading = isProfileLoading || !moderation || !profile + + const userNumber = 56738 const share = () => { if (imageRef.current && imageRef.current.capture) { @@ -31,8 +80,8 @@ export function TenMillion() { imageUris: [ { uri, - width: 1000, - height: 1000, + width: WIDTH, + height: HEIGHT, }, ], }) @@ -48,52 +97,247 @@ export function TenMillion() { <Dialog.ScrollableInner label={_(msg`Ten Million`)} - style={ - [ - // gtMobile ? {width: 'auto', maxWidth: 400, minWidth: 200} : a.w_full, - ] - }> + style={[ + { + padding: 0, + }, + // gtMobile ? {width: 'auto', maxWidth: 400, minWidth: 200} : a.w_full, + ]}> <View style={[ - a.relative, - a.w_full, + a.rounded_md, a.overflow_hidden, - { - paddingTop: '100%', + isNative && { + borderTopLeftRadius: 40, + borderTopRightRadius: 40, }, ]}> - <ViewShot - ref={imageRef} - options={{width: 2e3, height: 2e3}} - style={[a.absolute, a.inset_0]}> + <ThemeProvider theme="light"> <View style={[ - a.absolute, - a.inset_0, - a.align_center, - a.justify_center, + a.relative, + a.w_full, + a.overflow_hidden, + { + paddingTop: '80%', + }, + ]}> + <ViewShot + ref={imageRef} + options={{width: WIDTH, height: HEIGHT}} + style={[a.absolute, a.inset_0]}> + <View + style={[ + a.absolute, + a.inset_0, + a.align_center, + a.justify_center, + { + top: -1, + bottom: -1, + left: -1, + right: -1, + paddingVertical: 32, + paddingHorizontal: 48, + }, + ]}> + <GradientFill gradient={tokens.gradients.bonfire} /> + + {isLoading ? ( + <Loader size="xl" fill="white" /> + ) : ( + <View + style={[ + a.flex_1, + a.w_full, + a.align_center, + a.justify_center, + a.rounded_md, + { + backgroundColor: 'white', + shadowRadius: 32, + shadowOpacity: 0.1, + elevation: 24, + shadowColor: tokens.gradients.bonfire.values[0][1], + }, + ]}> + <View + style={[ + a.absolute, + a.px_xl, + a.py_xl, + { + top: 0, + left: 0, + }, + ]}> + <Logomark fill={t.palette.primary_500} width={36} /> + </View> + + {/* Centered content */} + <View + style={[ + { + paddingBottom: 48, + }, + ]}> + <Text + style={[ + a.text_md, + a.font_bold, + a.text_center, + a.pb_xs, + lightTheme.atoms.text_contrast_medium, + ]}> + <Trans> + Celebrating {formatCount(i18n, 10000000)} users + </Trans>{' '} + 🎉 + </Text> + <Text + style={[ + a.relative, + a.text_center, + { + fontStyle: 'italic', + fontSize: getFontSize(userNumber), + fontWeight: '900', + letterSpacing: -2, + }, + ]}> + <Text + style={[ + a.absolute, + { + color: t.palette.primary_500, + fontSize: 32, + left: -18, + top: 8, + }, + ]}> + # + </Text> + {i18n.number(userNumber)} + </Text> + </View> + {/* End centered content */} + + <View + style={[ + a.absolute, + a.px_xl, + a.py_xl, + { + bottom: 0, + left: 0, + right: 0, + }, + ]}> + <View style={[a.flex_row, a.align_center, a.gap_sm]}> + <UserAvatar + size={36} + avatar={profile.avatar} + moderation={moderation.ui('avatar')} + /> + <View style={[a.gap_2xs, a.flex_1]}> + <Text style={[a.text_sm, a.font_bold]}> + {sanitizeDisplayName( + profile.displayName || + sanitizeHandle(profile.handle), + moderation.ui('displayName'), + )} + </Text> + <View style={[a.flex_row, a.justify_between]}> + <Text + style={[ + a.text_sm, + a.font_semibold, + lightTheme.atoms.text_contrast_medium, + ]}> + {sanitizeHandle(profile.handle, '@')} + </Text> + + {profile.createdAt && ( + <Text + style={[ + a.text_sm, + a.font_semibold, + lightTheme.atoms.text_contrast_low, + ]}> + {i18n.date(profile.createdAt, { + dateStyle: 'long', + })} + </Text> + )} + </View> + </View> + </View> + </View> + </View> + )} + </View> + </ViewShot> + </View> + </ThemeProvider> + + <View style={[gtMobile ? a.p_2xl : a.p_xl]}> + <Text + style={[ + a.text_5xl, + a.pb_lg, { - top: -1, - bottom: -1, - left: -1, - right: -1, + fontWeight: '900', }, ]}> - <GradientFill gradient={tokens.gradients.midnight} /> + You're part of the next wave of the internet. + </Text> + + <Text style={[a.leading_snug, a.text_lg, a.pb_xl]}> + Online culture is too important to be controlled by a few + corporations.{' '} + <Text style={[a.leading_snug, a.text_lg, a.italic]}> + We’re dedicated to building an open foundation for the social + internet so that we can all shape its future. + </Text> + </Text> + + <Divider /> + + <View + style={[ + a.flex_row, + a.align_center, + a.justify_end, + a.gap_md, + a.pt_xl, + ]}> + <Text style={[a.text_md, a.italic, t.atoms.text_contrast_medium]}> + Brag a little ;) + </Text> - <Text>10 milly, babyyy</Text> + <Button + label={_(msg`Share image externally`)} + size="large" + variant="solid" + color="secondary" + shape="square" + onPress={share}> + <ButtonIcon icon={Share} /> + </Button> + <Button + label={_(msg`Share image in post`)} + size="large" + variant="solid" + color="primary" + onPress={share}> + <ButtonText>{_(msg`Share post`)}</ButtonText> + <ButtonIcon position="right" icon={ImageIcon} /> + </Button> </View> - </ViewShot> + </View> </View> - <Button - label={_(msg`Generate`)} - size="medium" - variant="solid" - color="primary" - onPress={share}> - <ButtonText>{_(msg`Generate`)}</ButtonText> - </Button> + <Dialog.Close /> </Dialog.ScrollableInner> </Dialog.Outer> ) diff --git a/src/view/icons/Logomark.tsx b/src/view/icons/Logomark.tsx new file mode 100644 index 000000000..5715a1a40 --- /dev/null +++ b/src/view/icons/Logomark.tsx @@ -0,0 +1,29 @@ +import React from 'react' +import Svg, {Path, PathProps, SvgProps} from 'react-native-svg' + +import {usePalette} from '#/lib/hooks/usePalette' + +const ratio = 54 / 61 + +export function Logomark({ + fill, + ...rest +}: {fill?: PathProps['fill']} & SvgProps) { + const pal = usePalette('default') + // @ts-ignore it's fiiiiine + const size = parseInt(rest.width || 32) + + return ( + <Svg + fill="none" + viewBox="0 0 61 54" + {...rest} + width={size} + height={Number(size) * ratio}> + <Path + fill={fill || pal.text.color} + d="M13.223 3.602C20.215 8.832 27.738 19.439 30.5 25.13c2.762-5.691 10.284-16.297 17.278-21.528C52.824-.172 61-3.093 61 6.2c0 1.856-1.068 15.59-1.694 17.82-2.178 7.752-10.112 9.73-17.17 8.532 12.337 2.092 15.475 9.021 8.697 15.95-12.872 13.159-18.5-3.302-19.943-7.52-.264-.773-.388-1.135-.39-.827-.002-.308-.126.054-.39.827-1.442 4.218-7.071 20.679-19.943 7.52-6.778-6.929-3.64-13.858 8.697-15.95-7.058 1.197-14.992-.78-17.17-8.532C1.068 21.79 0 8.056 0 6.2 0-3.093 8.176-.172 13.223 3.602Z" + /> + </Svg> + ) +} |