From 11ecea22d422138b8346dccded7dcdd70191fae4 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Tue, 10 Sep 2024 18:45:08 -0500 Subject: Add badges, clean up spacing --- src/components/dialogs/nudges/TenMillion.tsx | 456 ------------------ .../dialogs/nudges/TenMillion/icons/OnePercent.tsx | 15 + .../nudges/TenMillion/icons/PointOnePercent.tsx | 15 + .../dialogs/nudges/TenMillion/icons/TenPercent.tsx | 15 + .../nudges/TenMillion/icons/TwentyFivePercent.tsx | 15 + src/components/dialogs/nudges/TenMillion/index.tsx | 509 +++++++++++++++++++++ 6 files changed, 569 insertions(+), 456 deletions(-) delete mode 100644 src/components/dialogs/nudges/TenMillion.tsx create mode 100644 src/components/dialogs/nudges/TenMillion/icons/OnePercent.tsx create mode 100644 src/components/dialogs/nudges/TenMillion/icons/PointOnePercent.tsx create mode 100644 src/components/dialogs/nudges/TenMillion/icons/TenPercent.tsx create mode 100644 src/components/dialogs/nudges/TenMillion/icons/TwentyFivePercent.tsx create mode 100644 src/components/dialogs/nudges/TenMillion/index.tsx (limited to 'src') diff --git a/src/components/dialogs/nudges/TenMillion.tsx b/src/components/dialogs/nudges/TenMillion.tsx deleted file mode 100644 index 5aa45f214..000000000 --- a/src/components/dialogs/nudges/TenMillion.tsx +++ /dev/null @@ -1,456 +0,0 @@ -import React from 'react' -import {View} from 'react-native' -import ViewShot from 'react-native-view-shot' -import {Image} from 'expo-image' -import {moderateProfile} from '@atproto/api' -import {msg, Trans} from '@lingui/macro' -import {useLingui} from '@lingui/react' - -import {getCanvas} from '#/lib/canvas' -import {shareUrl} from '#/lib/sharing' -import {sanitizeDisplayName} from '#/lib/strings/display-names' -import {sanitizeHandle} from '#/lib/strings/handles' -import {isAndroid, isNative, isWeb} 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 {useContext} from '#/components/dialogs/nudges' -import {Divider} from '#/components/Divider' -import {GradientFill} from '#/components/GradientFill' -import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox' -import {Download_Stroke2_Corner0_Rounded as Download} from '#/components/icons/Download' -import {Image_Stroke2_Corner0_Rounded as ImageIcon} from '#/components/icons/Image' -import {Loader} from '#/components/Loader' -import {Text} from '#/components/Typography' - -const DEBUG = false -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 - } -} - -function Frame({children}: {children: React.ReactNode}) { - return ( - - {children} - - ) -} - -export function TenMillion() { - const t = useTheme() - const lightTheme = useTheme('light') - const {_, i18n} = useLingui() - const {controls} = useContext() - const {gtMobile} = useBreakpoints() - const {openComposer} = useComposerControls() - 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 [uri, setUri] = React.useState(null) - - const isLoadingData = isProfileLoading || !moderation || !profile - const isLoadingImage = !uri - - const userNumber = 10_000_000 // TODO - - const sharePost = () => { - if (uri) { - controls.tenMillion.close(() => { - setTimeout(() => { - openComposer({ - text: '10 milly, babyyy', - imageUris: [ - { - uri, - width: WIDTH, - height: HEIGHT, - }, - ], - }) - }, 1e3) - }) - } - } - - const onNativeShare = () => { - if (uri) { - controls.tenMillion.close(() => { - shareUrl(uri) - }) - } - } - - const download = async () => { - if (uri) { - const canvas = await getCanvas(uri) - const imgHref = canvas - .toDataURL('image/png') - .replace('image/png', 'image/octet-stream') - const link = document.createElement('a') - link.setAttribute('download', `Bluesky 10M Users.png`) - link.setAttribute('href', imgHref) - link.click() - } - } - - const imageRef = React.useRef(null) - // const captureInProgress = React.useRef(false) - // const [cavasRelayout, setCanvasRelayout] = React.useState('key') - // const onCanvasReady = async () => { - // if ( - // imageRef.current && - // imageRef.current.capture && - // !captureInProgress.current - // ) { - // captureInProgress.current = true - // setCanvasRelayout('updated') - // } - // } - const onCanvasLayout = async () => { - if ( - imageRef.current && - imageRef.current.capture // && - // cavasRelayout === 'updated' - ) { - console.log('LAYOUT') - const uri = await imageRef.current.capture() - setUri(uri) - } - } - - const canvas = isLoadingData ? null : ( - - - - - - - - - - - - - - {/* Centered content */} - - - - Celebrating {formatCount(i18n, 10000000)} users - {' '} - 🎉 - - - - # - - - {i18n.number(userNumber)} - - - - {/* End centered content */} - - - - {/* - - */} - - - {sanitizeDisplayName( - profile.displayName || - sanitizeHandle(profile.handle), - moderation.ui('displayName'), - )} - - - - {sanitizeHandle(profile.handle, '@')} - - - {profile.createdAt && ( - - - Joined{' '} - {i18n.date(profile.createdAt, { - dateStyle: 'long', - })} - - - )} - - - - - - - - - - - - ) - - return ( - - - - - - - {isLoadingData || isLoadingImage ? ( - - ) : ( - - )} - - - - {canvas} - - - - You're part of the next wave of the internet. - - - - Online culture is too important to be controlled by a few - corporations.{' '} - - We’re dedicated to building an open foundation for the social - internet so that we can all shape its future. - - - - - - - - Brag a little ;) - - - - - - - - - - - - ) -} diff --git a/src/components/dialogs/nudges/TenMillion/icons/OnePercent.tsx b/src/components/dialogs/nudges/TenMillion/icons/OnePercent.tsx new file mode 100644 index 000000000..9c8d47afd --- /dev/null +++ b/src/components/dialogs/nudges/TenMillion/icons/OnePercent.tsx @@ -0,0 +1,15 @@ +import React from 'react' +import Svg, {Path} from 'react-native-svg' + +export function OnePercent({fill}: {fill?: string}) { + return ( + + + + ) +} diff --git a/src/components/dialogs/nudges/TenMillion/icons/PointOnePercent.tsx b/src/components/dialogs/nudges/TenMillion/icons/PointOnePercent.tsx new file mode 100644 index 000000000..1f9467e44 --- /dev/null +++ b/src/components/dialogs/nudges/TenMillion/icons/PointOnePercent.tsx @@ -0,0 +1,15 @@ +import React from 'react' +import Svg, {Path} from 'react-native-svg' + +export function PointOnePercent({fill}: {fill?: string}) { + return ( + + + + ) +} diff --git a/src/components/dialogs/nudges/TenMillion/icons/TenPercent.tsx b/src/components/dialogs/nudges/TenMillion/icons/TenPercent.tsx new file mode 100644 index 000000000..4197be835 --- /dev/null +++ b/src/components/dialogs/nudges/TenMillion/icons/TenPercent.tsx @@ -0,0 +1,15 @@ +import React from 'react' +import Svg, {Path} from 'react-native-svg' + +export function TenPercent({fill}: {fill?: string}) { + return ( + + + + ) +} diff --git a/src/components/dialogs/nudges/TenMillion/icons/TwentyFivePercent.tsx b/src/components/dialogs/nudges/TenMillion/icons/TwentyFivePercent.tsx new file mode 100644 index 000000000..0d3797141 --- /dev/null +++ b/src/components/dialogs/nudges/TenMillion/icons/TwentyFivePercent.tsx @@ -0,0 +1,15 @@ +import React from 'react' +import Svg, {Path} from 'react-native-svg' + +export function TwentyFivePercent({fill}: {fill?: string}) { + return ( + + + + ) +} diff --git a/src/components/dialogs/nudges/TenMillion/index.tsx b/src/components/dialogs/nudges/TenMillion/index.tsx new file mode 100644 index 000000000..fdac91f4f --- /dev/null +++ b/src/components/dialogs/nudges/TenMillion/index.tsx @@ -0,0 +1,509 @@ +import React from 'react' +import {View} from 'react-native' +import ViewShot from 'react-native-view-shot' +import {Image} from 'expo-image' +import {moderateProfile} from '@atproto/api' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {getCanvas} from '#/lib/canvas' +import {shareUrl} from '#/lib/sharing' +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 {useContext} from '#/components/dialogs/nudges' +import {OnePercent} from '#/components/dialogs/nudges/TenMillion/icons/OnePercent' +import {PointOnePercent} from '#/components/dialogs/nudges/TenMillion/icons/PointOnePercent' +import {TenPercent} from '#/components/dialogs/nudges/TenMillion/icons/TenPercent' +import {Divider} from '#/components/Divider' +import {GradientFill} from '#/components/GradientFill' +import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox' +import {Download_Stroke2_Corner0_Rounded as Download} from '#/components/icons/Download' +import {Image_Stroke2_Corner0_Rounded as ImageIcon} from '#/components/icons/Image' +import {Loader} from '#/components/Loader' +import {Text} from '#/components/Typography' +// import {TwentyFivePercent} from '#/components/dialogs/nudges/TenMillion/icons/TwentyFivePercent' + +const DEBUG = false +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 + } +} + +function getPercentBadge(percent: number) { + if (percent <= 0.001) { + return PointOnePercent + } else if (percent <= 0.01) { + return OnePercent + } else if (percent <= 0.1) { + return TenPercent + } + // else if (percent <= 0.25) { + // return TwentyFivePercent + // } + return null +} + +function Frame({children}: {children: React.ReactNode}) { + return ( + + {children} + + ) +} + +export function TenMillion() { + const t = useTheme() + const lightTheme = useTheme('light') + const {_, i18n} = useLingui() + const {controls} = useContext() + const {gtMobile} = useBreakpoints() + const {openComposer} = useComposerControls() + 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 [uri, setUri] = React.useState(null) + + const isLoadingData = isProfileLoading || !moderation || !profile + const isLoadingImage = !uri + + const userNumber = 56_738 // TODO + const percent = userNumber / 10_000_000 + const Badge = getPercentBadge(percent) + + const sharePost = () => { + if (uri) { + controls.tenMillion.close(() => { + setTimeout(() => { + openComposer({ + text: '10 milly, babyyy', + imageUris: [ + { + uri, + width: WIDTH, + height: HEIGHT, + }, + ], + }) + }, 1e3) + }) + } + } + + const onNativeShare = () => { + if (uri) { + controls.tenMillion.close(() => { + shareUrl(uri) + }) + } + } + + const download = async () => { + if (uri) { + const canvas = await getCanvas(uri) + const imgHref = canvas + .toDataURL('image/png') + .replace('image/png', 'image/octet-stream') + const link = document.createElement('a') + link.setAttribute('download', `Bluesky 10M Users.png`) + link.setAttribute('href', imgHref) + link.click() + } + } + + const imageRef = React.useRef(null) + // const captureInProgress = React.useRef(false) + // const [cavasRelayout, setCanvasRelayout] = React.useState('key') + // const onCanvasReady = async () => { + // if ( + // imageRef.current && + // imageRef.current.capture && + // !captureInProgress.current + // ) { + // captureInProgress.current = true + // setCanvasRelayout('updated') + // } + // } + const onCanvasLayout = async () => { + if ( + imageRef.current && + imageRef.current.capture // && + // cavasRelayout === 'updated' + ) { + const uri = await imageRef.current.capture() + setUri(uri) + } + } + + const canvas = isLoadingData ? null : ( + + + + + + + + + + + + + + {/* Centered content */} + + + + Celebrating {formatCount(i18n, 10000000)} users + {' '} + 🎉 + + + + # + + + {i18n.number(userNumber)} + + + + {Badge && ( + + + + )} + + {/* End centered content */} + + + + {/* + + */} + + + {sanitizeDisplayName( + profile.displayName || + sanitizeHandle(profile.handle), + moderation.ui('displayName'), + )} + + + + {sanitizeHandle(profile.handle, '@')} + + + {profile.createdAt && ( + + + Joined{' '} + {i18n.date(profile.createdAt, { + dateStyle: 'long', + })} + + + )} + + + + + + + + + + + + ) + + return ( + + + + + + + {isLoadingData || isLoadingImage ? ( + + ) : ( + + )} + + + + {canvas} + + + + You're part of the next wave of the internet. + + + + + Online culture is too important to be controlled by a few + corporations. + {' '} + + + We’re dedicated to building an open foundation for the social + internet so that we can all shape its future. + + + + + + Congratulations. We're glad you're here. + + + + + + + Brag a little ;) + + + + + + + + + + + + ) +} -- cgit 1.4.1