From a9765fd23f59d31d60f5aaedd95e751f65adc969 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Thu, 26 Sep 2024 14:09:02 +0100 Subject: [PATCH 01/16] Fix banner height in edit profile modal (#5494) * fix banner height * fix user banner, it's not edit profile's fault --- src/view/com/modals/EditProfile.tsx | 8 ++++---- src/view/com/util/UserBanner.tsx | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/view/com/modals/EditProfile.tsx b/src/view/com/modals/EditProfile.tsx index beea3ca1a8..b4cb8e013c 100644 --- a/src/view/com/modals/EditProfile.tsx +++ b/src/view/com/modals/EditProfile.tsx @@ -27,11 +27,11 @@ import {logger} from '#/logger' import {isWeb} from '#/platform/detection' import {useModalControls} from '#/state/modals' import {useProfileUpdateMutation} from '#/state/queries/profile' +import {Text} from '#/view/com/util/text/Text' +import * as Toast from '#/view/com/util/Toast' +import {EditableUserAvatar} from '#/view/com/util/UserAvatar' +import {UserBanner} from '#/view/com/util/UserBanner' import {ErrorMessage} from '../util/error/ErrorMessage' -import {Text} from '../util/text/Text' -import * as Toast from '../util/Toast' -import {EditableUserAvatar} from '../util/UserAvatar' -import {UserBanner} from '../util/UserBanner' const AnimatedTouchableOpacity = Animated.createAnimatedComponent(TouchableOpacity) diff --git a/src/view/com/util/UserBanner.tsx b/src/view/com/util/UserBanner.tsx index 0e07a57454..13f4081fce 100644 --- a/src/view/com/util/UserBanner.tsx +++ b/src/view/com/util/UserBanner.tsx @@ -202,7 +202,7 @@ const styles = StyleSheet.create({ }, bannerImage: { width: '100%', - height: '100%', + height: 150, }, defaultBanner: { backgroundColor: '#0070ff', From f2a69c4528d318bfff9fff084ef75719fd5f41fb Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Thu, 26 Sep 2024 14:18:48 +0100 Subject: [PATCH 02/16] add emoji prop to composer reply to text (#5495) --- src/view/com/composer/ComposerReplyTo.tsx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/view/com/composer/ComposerReplyTo.tsx b/src/view/com/composer/ComposerReplyTo.tsx index 74ca615070..7f4bb85f23 100644 --- a/src/view/com/composer/ComposerReplyTo.tsx +++ b/src/view/com/composer/ComposerReplyTo.tsx @@ -10,12 +10,12 @@ import { import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {sanitizeDisplayName} from 'lib/strings/display-names' -import {sanitizeHandle} from 'lib/strings/handles' -import {ComposerOptsPostRef} from 'state/shell/composer' -import {QuoteEmbed} from 'view/com/util/post-embeds/QuoteEmbed' -import {Text} from 'view/com/util/text/Text' -import {PreviewableUserAvatar} from 'view/com/util/UserAvatar' +import {sanitizeDisplayName} from '#/lib/strings/display-names' +import {sanitizeHandle} from '#/lib/strings/handles' +import {ComposerOptsPostRef} from '#/state/shell/composer' +import {QuoteEmbed} from '#/view/com/util/post-embeds/QuoteEmbed' +import {Text} from '#/view/com/util/text/Text' +import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' import {atoms as a, useTheme} from '#/alf' export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) { @@ -91,7 +91,7 @@ export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) { type={replyTo.author.associated?.labeler ? 'labeler' : 'user'} /> - + {sanitizeDisplayName( replyTo.author.displayName || sanitizeHandle(replyTo.author.handle), )} @@ -101,7 +101,8 @@ export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) { + numberOfLines={!showFull ? 6 : undefined} + emoji> {replyTo.text} From 625d7460d8af868ef0ea755cb3ac931dac4aad77 Mon Sep 17 00:00:00 2001 From: Igor Adrov Date: Thu, 26 Sep 2024 16:48:12 +0200 Subject: [PATCH 03/16] Make the counter more rounded (#5083) --- src/view/shell/desktop/LeftNav.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx index 6cceaccd92..c36c4ae758 100644 --- a/src/view/shell/desktop/LeftNav.tsx +++ b/src/view/shell/desktop/LeftNav.tsx @@ -470,7 +470,7 @@ const styles = StyleSheet.create({ fontSize: 12, fontWeight: '600', paddingHorizontal: 4, - borderRadius: 6, + borderRadius: 8, }, navItemCountTablet: { left: 18, From 179a913f20b9b77edbf9d167ebf32ad455f78eac Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Thu, 26 Sep 2024 09:59:32 -0500 Subject: [PATCH 04/16] Emoji in account list (#5497) --- src/components/AccountList.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/AccountList.tsx b/src/components/AccountList.tsx index 883c06c144..68bb482af2 100644 --- a/src/components/AccountList.tsx +++ b/src/components/AccountList.tsx @@ -126,10 +126,12 @@ function AccountItem({ - + {profile?.displayName || account.handle}{' '} - {account.handle} + + {account.handle} + {isCurrentAccount ? ( Date: Thu, 26 Sep 2024 09:59:47 -0500 Subject: [PATCH 05/16] Adjust line height to not cut off emoji (#5496) --- src/view/com/util/PostMeta.tsx | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx index 3f647f9784..adf9c5eb1b 100644 --- a/src/view/com/util/PostMeta.tsx +++ b/src/view/com/util/PostMeta.tsx @@ -77,7 +77,7 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => { disableMismatchWarning onPress={onBeforePressAuthor} style={[t.atoms.text]}> - + {forceLTR( sanitizeDisplayName( displayName, @@ -92,14 +92,10 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => { disableMismatchWarning disableUnderline onPress={onBeforePressAuthor} - style={[a.text_md, t.atoms.text_contrast_medium, a.leading_tight]}> + style={[a.text_md, t.atoms.text_contrast_medium, a.leading_snug]}> + style={[a.text_md, t.atoms.text_contrast_medium, a.leading_snug]}> {NON_BREAKING_SPACE + sanitizeHandle(handle, '@')} @@ -124,7 +120,7 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => { style={[ a.text_md, t.atoms.text_contrast_medium, - a.leading_tight, + a.leading_snug, web({ whiteSpace: 'nowrap', }), From 23dd638f6a730883df871e4968830067361d902b Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Thu, 26 Sep 2024 10:57:45 -0500 Subject: [PATCH 06/16] Clean up left nav on web (#5488) * Tweak nav item count style * Fix nav item icon width and alignment * Just refactor the thing * New compose button * Z index * Rounded * Tweak gradient * Tweak gradient * Tweak gradient * Solid * Adjust position of counter * Always a circle --- src/alf/tokens.ts | 9 ++ src/components/Button.tsx | 9 +- src/logger/index.ts | 2 +- src/view/shell/desktop/LeftNav.tsx | 141 +++++++++++------------------ 4 files changed, 68 insertions(+), 93 deletions(-) diff --git a/src/alf/tokens.ts b/src/alf/tokens.ts index 3f30702e85..f3ac17e6af 100644 --- a/src/alf/tokens.ts +++ b/src/alf/tokens.ts @@ -60,6 +60,15 @@ export const fontWeight = { } as const export const gradients = { + primary: { + values: [ + [0, '#054CFF'], + [0.4, '#1085FE'], + [0.6, '#1085FE'], + [1, '#59B9FF'], + ], + hover_value: '#1085FE', + }, sky: { values: [ [0, '#0A7AFF'], diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 8728b88c2c..17179994a9 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -24,6 +24,7 @@ export type ButtonColor = | 'secondary' | 'secondary_inverted' | 'negative' + | 'gradient_primary' | 'gradient_sky' | 'gradient_midnight' | 'gradient_sunrise' @@ -412,6 +413,7 @@ export const Button = React.forwardRef( secondary: tokens.gradients.sky, secondary_inverted: tokens.gradients.sky, negative: tokens.gradients.sky, + gradient_primary: tokens.gradients.primary, gradient_sky: tokens.gradients.sky, gradient_midnight: tokens.gradients.midnight, gradient_sunrise: tokens.gradients.sunrise, @@ -444,7 +446,7 @@ export const Button = React.forwardRef( [state, variant, color, size, disabled], ) - const flattenedBaseStyles = flatten(baseStyles) + const flattenedBaseStyles = flatten([baseStyles, style]) return ( ( a.align_center, a.justify_center, flattenedBaseStyles, - flatten(style), ...(state.hovered || state.pressed ? [hoverStyles, flatten(hoverStyleProp)] : []), @@ -626,9 +627,9 @@ export function useSharedButtonTextStyles() { } if (size === 'large') { - baseStyles.push(a.text_md, a.leading_tight, web({paddingTop: 1})) + baseStyles.push(a.text_md, a.leading_tight, web({top: -0.4})) } else if (size === 'small') { - baseStyles.push(a.text_sm, a.leading_tight, web({paddingTop: 1})) + baseStyles.push(a.text_sm, a.leading_tight, web({top: -0.4})) } else if (size === 'tiny') { baseStyles.push(a.text_xs, a.leading_tight) } diff --git a/src/logger/index.ts b/src/logger/index.ts index 98635c6a97..7bd812af00 100644 --- a/src/logger/index.ts +++ b/src/logger/index.ts @@ -1,10 +1,10 @@ import format from 'date-fns/format' import {nanoid} from 'nanoid/non-secure' +import {isNetworkError} from '#/lib/strings/errors' import {DebugContext} from '#/logger/debugContext' import {add} from '#/logger/logDump' import {Sentry} from '#/logger/sentry' -import {isNetworkError} from 'lib/strings/errors' import * as env from '#/env' export enum LogLevel { diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx index c36c4ae758..ecd00a9192 100644 --- a/src/view/shell/desktop/LeftNav.tsx +++ b/src/view/shell/desktop/LeftNav.tsx @@ -18,7 +18,6 @@ import {getCurrentRoute, isStateAtTabRoot, isTab} from '#/lib/routes/helpers' import {makeProfileLink} from '#/lib/routes/links' import {CommonNavigatorParams, NavigationProp} from '#/lib/routes/types' import {isInvalidHandle} from '#/lib/strings/handles' -import {colors, s} from '#/lib/styles' import {emitSoftReset} from '#/state/events' import {useFetchHandle} from '#/state/queries/handle' import {useUnreadMessageCount} from '#/state/queries/messages/list-converations' @@ -29,9 +28,10 @@ import {useComposerControls} from '#/state/shell/composer' import {Link} from '#/view/com/util/Link' import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' import {PressableWithHover} from '#/view/com/util/PressableWithHover' -import {Text} from '#/view/com/util/text/Text' import {UserAvatar} from '#/view/com/util/UserAvatar' import {NavSignupCard} from '#/view/shell/NavSignupCard' +import {atoms as a, useBreakpoints, useTheme} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' import { Bell_Filled_Corner0_Rounded as BellFilled, Bell_Stroke2_Corner0_Rounded as Bell, @@ -63,6 +63,7 @@ import { UserCircle_Filled_Corner0_Rounded as UserCircleFilled, UserCircle_Stroke2_Corner0_Rounded as UserCircle, } from '#/components/icons/UserCircle' +import {Text} from '#/components/Typography' import {router} from '../../../routes' const NAV_ICON_WIDTH = 28 @@ -149,9 +150,10 @@ interface NavItemProps { label: string } function NavItem({count, href, icon, iconFilled, label}: NavItemProps) { - const pal = usePalette('default') + const t = useTheme() const {currentAccount} = useSession() - const {isDesktop, isTablet} = useWebMediaQueries() + const {gtMobile, gtTablet} = useBreakpoints() + const isTablet = gtMobile && !gtTablet const [pathName] = React.useMemo(() => router.matchPath(href), [href]) const currentRouteInfo = useNavigationState(state => { if (!state) { @@ -183,8 +185,8 @@ function NavItem({count, href, icon, iconFilled, label}: NavItemProps) { return ( {isCurrent ? iconFilled : icon} {typeof count === 'string' && count ? ( {count} ) : null} - {isDesktop && ( - + {gtTablet && ( + {label} )} @@ -268,21 +297,20 @@ function ComposeBtn() { return null } return ( - - + ) } @@ -440,67 +468,4 @@ const styles = StyleSheet.create({ width: 30, height: 30, }, - - navItemWrapper: { - flexDirection: 'row', - alignItems: 'center', - paddingHorizontal: 12, - padding: 12, - borderRadius: 8, - gap: 10, - }, - navItemIconWrapper: { - alignItems: 'center', - justifyContent: 'center', - width: 28, - height: 24, - marginTop: 2, - zIndex: 1, - }, - navItemIconWrapperTablet: { - width: 40, - height: 40, - }, - navItemCount: { - position: 'absolute', - top: 0, - left: 15, - backgroundColor: colors.blue3, - color: colors.white, - fontSize: 12, - fontWeight: '600', - paddingHorizontal: 4, - borderRadius: 8, - }, - navItemCountTablet: { - left: 18, - fontSize: 14, - }, - - newPostBtnContainer: { - flexDirection: 'row', - }, - newPostBtn: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - borderRadius: 24, - paddingTop: 10, - paddingBottom: 12, // visually aligns the text vertically inside the button - paddingLeft: 16, - paddingRight: 18, // looks nicer like this - backgroundColor: colors.blue3, - marginLeft: 12, - marginTop: 20, - marginBottom: 10, - gap: 8, - }, - newPostBtnIconWrapper: { - marginTop: 2, // aligns the icon visually with the text - }, - newPostBtnLabel: { - color: colors.white, - fontSize: 16, - fontWeight: '600', - }, }) From 27e09e7adccc4d002359425492eb8773da73ebcb Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Thu, 26 Sep 2024 11:30:00 -0500 Subject: [PATCH 07/16] Fix font loading (#5500) --- src/alf/fonts.ts | 9 ++++++--- src/style.css | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/alf/fonts.ts b/src/alf/fonts.ts index b11ce939f8..46c741bc79 100644 --- a/src/alf/fonts.ts +++ b/src/alf/fonts.ts @@ -45,15 +45,18 @@ export function applyFonts( // '100': 'Inter-Thin', // '200': 'Inter-ExtraLight', // '300': 'Inter-Light', + // '500': 'Inter-Medium', + // '700': 'Inter-Bold', + // '900': 'Inter-Black', '100': 'Inter-Regular', '200': 'Inter-Regular', '300': 'Inter-Regular', '400': 'Inter-Regular', - '500': 'Inter-Medium', + '500': 'Inter-SemiBold', '600': 'Inter-SemiBold', - '700': 'Inter-Bold', + '700': 'Inter-SemiBold', '800': 'Inter-ExtraBold', - '900': 'Inter-Black', + '900': 'Inter-ExtraBold', }[style.fontWeight as string] || 'Inter-Regular' if (style.fontStyle === 'italic') { diff --git a/src/style.css b/src/style.css index 980d92ef77..b0b01ba3eb 100644 --- a/src/style.css +++ b/src/style.css @@ -9,7 +9,7 @@ @font-face { font-family: 'Inter-Regular'; src: local('Inter-Regular'), - url(/assets/fonts/inter/Inter-Regular.otf) format('font/otf'); + url(/assets/fonts/inter/Inter-Regular.otf) format('opentype'); font-weight: 400; font-style: normal; font-display: swap; @@ -17,7 +17,7 @@ @font-face { font-family: 'Inter-Italic'; src: local('Inter-Italic'), - url(/assets/fonts/inter/Inter-Italic.otf) format('font/otf'); + url(/assets/fonts/inter/Inter-Italic.otf) format('opentype'); font-weight: 400; font-style: italic; font-display: swap; @@ -25,14 +25,14 @@ /* @font-face { font-family: "Inter-Medium"; - src: local("Inter-Medium"), url(/assets/fonts/inter/Inter-Medium.otf) format("font/otf"); + src: local("Inter-Medium"), url(/assets/fonts/inter/Inter-Medium.otf) format("opentype"); font-weight: 500; font-style: normal; font-display: swap; } @font-face { font-family: "Inter-MediumItalic"; - src: local("Inter-MediumItalic"), url(/assets/fonts/inter/Inter-MediumItalic.otf) format("font/otf"); + src: local("Inter-MediumItalic"), url(/assets/fonts/inter/Inter-MediumItalic.otf) format("opentype"); font-weight: 500; font-style: italic; font-display: swap; @@ -41,7 +41,7 @@ @font-face { font-family: 'Inter-SemiBold'; src: local('Inter-SemiBold'), - url(/assets/fonts/inter/Inter-SemiBold.otf) format('font/otf'); + url(/assets/fonts/inter/Inter-SemiBold.otf) format('opentype'); font-weight: 600; font-style: normal; font-display: swap; @@ -49,7 +49,7 @@ @font-face { font-family: 'Inter-SemiBoldItalic'; src: local('Inter-SemiBoldItalic'), - url(/assets/fonts/inter/Inter-SemiBoldItalic.otf) format('font/otf'); + url(/assets/fonts/inter/Inter-SemiBoldItalic.otf) format('opentype'); font-weight: 600; font-style: italic; font-display: swap; @@ -57,14 +57,14 @@ /* @font-face { font-family: "Inter-Bold"; - src: local("Inter-Bold"), url(/assets/fonts/inter/Inter-Bold.otf) format("font/otf"); + src: local("Inter-Bold"), url(/assets/fonts/inter/Inter-Bold.otf) format("opentype"); font-weight: 700; font-style: normal; font-display: swap; } @font-face { font-family: "Inter-BoldItalic"; - src: local("Inter-BoldItalic"), url(/assets/fonts/inter/Inter-BoldItalic.otf) format("font/otf"); + src: local("Inter-BoldItalic"), url(/assets/fonts/inter/Inter-BoldItalic.otf) format("opentype"); font-weight: 700; font-style: italic; font-display: swap; @@ -73,7 +73,7 @@ @font-face { font-family: 'Inter-ExtraBold'; src: local('Inter-ExtraBold'), - url(/assets/fonts/inter/Inter-ExtraBold.otf) format('font/otf'); + url(/assets/fonts/inter/Inter-ExtraBold.otf) format('opentype'); font-weight: 800; font-style: normal; font-display: swap; @@ -81,7 +81,7 @@ @font-face { font-family: 'Inter-ExtraBoldItalic'; src: local('Inter-ExtraBoldItalic'), - url(/assets/fonts/inter/Inter-ExtraBoldItalic.otf) format('font/otf'); + url(/assets/fonts/inter/Inter-ExtraBoldItalic.otf) format('opentype'); font-weight: 800; font-style: italic; font-display: swap; @@ -89,14 +89,14 @@ /* @font-face { font-family: "Inter-Black"; - src: local("Inter-Black"), url(/assets/fonts/inter/Inter-Black.otf) format("font/otf"); + src: local("Inter-Black"), url(/assets/fonts/inter/Inter-Black.otf) format("opentype"); font-weight: 900; font-style: normal; font-display: swap; } @font-face { font-family: "Inter-BlackItalic"; - src: local("Inter-BlackItalic"), url(/assets/fonts/inter/Inter-BlackItalic.otf) format("font/otf"); + src: local("Inter-BlackItalic"), url(/assets/fonts/inter/Inter-BlackItalic.otf) format("opentype"); font-weight: 900; font-style: italic; font-display: swap; From 5a142161cb90f65c2593b58c7471b6657a2cd0a2 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Thu, 26 Sep 2024 11:47:13 -0500 Subject: [PATCH 08/16] Remove 10milly dialog, revert header logo changes (#5503) --- .../dialogs/nuxs/TenMillion/Trigger.tsx | 129 ---- .../nuxs/TenMillion/icons/OnePercent.tsx | 15 - .../nuxs/TenMillion/icons/PointOnePercent.tsx | 15 - .../nuxs/TenMillion/icons/TenPercent.tsx | 15 - .../TenMillion/icons/TwentyFivePercent.tsx | 15 - .../dialogs/nuxs/TenMillion/index.tsx | 712 ------------------ src/components/dialogs/nuxs/index.tsx | 9 +- src/state/queries/nuxs/definitions.ts | 15 +- src/view/com/home/HomeHeaderLayout.web.tsx | 78 +- src/view/com/home/HomeHeaderLayoutMobile.tsx | 75 +- 10 files changed, 28 insertions(+), 1050 deletions(-) delete mode 100644 src/components/dialogs/nuxs/TenMillion/Trigger.tsx delete mode 100644 src/components/dialogs/nuxs/TenMillion/icons/OnePercent.tsx delete mode 100644 src/components/dialogs/nuxs/TenMillion/icons/PointOnePercent.tsx delete mode 100644 src/components/dialogs/nuxs/TenMillion/icons/TenPercent.tsx delete mode 100644 src/components/dialogs/nuxs/TenMillion/icons/TwentyFivePercent.tsx delete mode 100644 src/components/dialogs/nuxs/TenMillion/index.tsx diff --git a/src/components/dialogs/nuxs/TenMillion/Trigger.tsx b/src/components/dialogs/nuxs/TenMillion/Trigger.tsx deleted file mode 100644 index 9616b3b1d3..0000000000 --- a/src/components/dialogs/nuxs/TenMillion/Trigger.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import React from 'react' -import {View} from 'react-native' -import Svg, {Circle, Path} from 'react-native-svg' -import {msg, Trans} from '@lingui/macro' -import {useLingui} from '@lingui/react' - -import {Nux, useUpsertNuxMutation} from '#/state/queries/nuxs' -import {atoms as a, ViewStyleProp} from '#/alf' -import {Button, ButtonProps} from '#/components/Button' -import * as Dialog from '#/components/Dialog' -import {InlineLinkText} from '#/components/Link' -import * as Prompt from '#/components/Prompt' -import {TenMillion} from './' - -export function Trigger({children}: {children: ButtonProps['children']}) { - const {_} = useLingui() - const {mutate: upsertNux} = useUpsertNuxMutation() - const [show, setShow] = React.useState(false) - const [fallback, setFallback] = React.useState(false) - const control = Prompt.usePromptControl() - - const handleOnPress = () => { - if (!fallback) { - setShow(true) - upsertNux({ - id: Nux.TenMillionDialog, - completed: true, - data: undefined, - }) - } else { - control.open() - } - } - - const onHandleFallback = () => { - setFallback(true) - control.open() - } - - return ( - <> - - - {show && !fallback && ( - setShow(false)} - onFallback={onHandleFallback} - /> - )} - - - - - Bluesky is celebrating 10 million users! - - - - - Together, we're rebuilding the social internet. We're glad you're - here. - - - - - To learn more,{' '} - { - control.close() - }} - style={[a.text_md, a.leading_snug]}> - check out our post. - - - - - - - ) -} - -export function Icon({width, style}: {width: number} & ViewStyleProp) { - return ( - - - - - - - - - - - - - - - - - - ) -} diff --git a/src/components/dialogs/nuxs/TenMillion/icons/OnePercent.tsx b/src/components/dialogs/nuxs/TenMillion/icons/OnePercent.tsx deleted file mode 100644 index 9c8d47afd0..0000000000 --- a/src/components/dialogs/nuxs/TenMillion/icons/OnePercent.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react' -import Svg, {Path} from 'react-native-svg' - -export function OnePercent({fill}: {fill?: string}) { - return ( - - - - ) -} diff --git a/src/components/dialogs/nuxs/TenMillion/icons/PointOnePercent.tsx b/src/components/dialogs/nuxs/TenMillion/icons/PointOnePercent.tsx deleted file mode 100644 index 1f9467e442..0000000000 --- a/src/components/dialogs/nuxs/TenMillion/icons/PointOnePercent.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react' -import Svg, {Path} from 'react-native-svg' - -export function PointOnePercent({fill}: {fill?: string}) { - return ( - - - - ) -} diff --git a/src/components/dialogs/nuxs/TenMillion/icons/TenPercent.tsx b/src/components/dialogs/nuxs/TenMillion/icons/TenPercent.tsx deleted file mode 100644 index 4197be8357..0000000000 --- a/src/components/dialogs/nuxs/TenMillion/icons/TenPercent.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react' -import Svg, {Path} from 'react-native-svg' - -export function TenPercent({fill}: {fill?: string}) { - return ( - - - - ) -} diff --git a/src/components/dialogs/nuxs/TenMillion/icons/TwentyFivePercent.tsx b/src/components/dialogs/nuxs/TenMillion/icons/TwentyFivePercent.tsx deleted file mode 100644 index 0d37971410..0000000000 --- a/src/components/dialogs/nuxs/TenMillion/icons/TwentyFivePercent.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react' -import Svg, {Path} from 'react-native-svg' - -export function TwentyFivePercent({fill}: {fill?: string}) { - return ( - - - - ) -} diff --git a/src/components/dialogs/nuxs/TenMillion/index.tsx b/src/components/dialogs/nuxs/TenMillion/index.tsx deleted file mode 100644 index 21e775a108..0000000000 --- a/src/components/dialogs/nuxs/TenMillion/index.tsx +++ /dev/null @@ -1,712 +0,0 @@ -import React from 'react' -import {View} from 'react-native' -import Animated, {FadeIn} from 'react-native-reanimated' -import ViewShot from 'react-native-view-shot' -import {Image} from 'expo-image' -import {requestMediaLibraryPermissionsAsync} from 'expo-image-picker' -import * as MediaLibrary from 'expo-media-library' -import {moderateProfile} from '@atproto/api' -import {msg, Trans} from '@lingui/macro' -import {useLingui} from '@lingui/react' - -import {networkRetry} from '#/lib/async/retry' -import {getCanvas} from '#/lib/canvas' -import {shareUrl} from '#/lib/sharing' -import {logEvent} from '#/lib/statsig/statsig' -import {sanitizeDisplayName} from '#/lib/strings/display-names' -import {sanitizeHandle} from '#/lib/strings/handles' -import {isIOS, isNative} from '#/platform/detection' -import {useModerationOpts} from '#/state/preferences/moderation-opts' -import {useProfileQuery} from '#/state/queries/profile' -import {useAgent, useSession} from '#/state/session' -import {useComposerControls} from '#/state/shell' -import {formatCount} from '#/view/com/util/numeric/format' -import * as Toast from '#/view/com/util/Toast' -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 {useNuxDialogContext} from '#/components/dialogs/nuxs' -import {OnePercent} from '#/components/dialogs/nuxs/TenMillion/icons/OnePercent' -import {PointOnePercent} from '#/components/dialogs/nuxs/TenMillion/icons/PointOnePercent' -import {TenPercent} from '#/components/dialogs/nuxs/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' - -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 - } - return null -} - -function Frame({children}: {children: React.ReactNode}) { - return ( - - {children} - - ) -} - -export function TenMillion({ - showTimeout, - onClose, - onFallback, -}: { - showTimeout?: number - onClose?: () => void - onFallback?: () => void -}) { - const agent = useAgent() - const nuxDialogs = useNuxDialogContext() - const [userNumber, setUserNumber] = React.useState(0) - const fetching = React.useRef(false) - - React.useEffect(() => { - async function fetchUserNumber() { - const isBlueskyHosted = agent.sessionManager.pdsUrl - ?.toString() - .includes('bsky.network') - - if (isBlueskyHosted && agent.session?.accessJwt) { - const res = await fetch( - `https://bsky.social/xrpc/com.atproto.temp.getSignupNumber`, - { - headers: { - Authorization: `Bearer ${agent.session.accessJwt}`, - }, - }, - ) - - if (!res.ok) { - throw new Error('Network request failed') - } - - const data = await res.json() - - if (data.number && data.number <= 10_000_000) { - setUserNumber(data.number) - } else { - // should be rare - nuxDialogs.dismissActiveNux() - onFallback?.() - } - } else { - nuxDialogs.dismissActiveNux() - onFallback?.() - } - } - - if (!fetching.current) { - fetching.current = true - networkRetry(3, fetchUserNumber).catch(() => { - nuxDialogs.dismissActiveNux() - onFallback?.() - }) - } - }, [ - agent.sessionManager.pdsUrl, - agent.session?.accessJwt, - setUserNumber, - nuxDialogs.dismissActiveNux, - nuxDialogs, - onFallback, - ]) - - return userNumber ? ( - - ) : null -} - -export function TenMillionInner({ - userNumber, - showTimeout, - onClose: onCloseOuter, -}: { - userNumber: number - showTimeout: number - onClose?: () => void -}) { - const t = useTheme() - const lightTheme = useTheme('light') - const {_, i18n} = useLingui() - const control = Dialog.useDialogControl() - const {gtMobile} = useBreakpoints() - const {openComposer} = useComposerControls() - const {currentAccount} = useSession() - const { - isLoading: isProfileLoading, - data: profile, - error: profileError, - } = useProfileQuery({ - did: currentAccount!.did, - }) - const moderationOpts = useModerationOpts() - const nuxDialogs = useNuxDialogContext() - const moderation = React.useMemo(() => { - return profile && moderationOpts - ? moderateProfile(profile, moderationOpts) - : undefined - }, [profile, moderationOpts]) - const [uri, setUri] = React.useState(null) - const percent = userNumber / 10_000_000 - const Badge = getPercentBadge(percent) - const isLoadingData = isProfileLoading || !moderation || !profile - const isLoadingImage = !uri - - const displayName = React.useMemo(() => { - if (!profile || !moderation) return '' - return sanitizeDisplayName( - profile.displayName || sanitizeHandle(profile.handle), - moderation.ui('displayName'), - ) - }, [profile, moderation]) - const handle = React.useMemo(() => { - if (!profile) return '' - return sanitizeHandle(profile.handle, '@') - }, [profile]) - const joinedDate = React.useMemo(() => { - if (!profile || !profile.createdAt) return '' - const date = i18n.date(profile.createdAt, { - month: 'short', - day: 'numeric', - year: 'numeric', - }) - return date - }, [i18n, profile]) - - const error: string = React.useMemo(() => { - if (profileError) { - return _( - msg`Oh no! We weren't able to generate an image for you to share. Rest assured, we're glad you're here 🦋`, - ) - } - return '' - }, [_, profileError]) - - /* - * Opening and closing - */ - React.useEffect(() => { - const timeout = setTimeout(() => { - control.open() - }, showTimeout) - return () => { - clearTimeout(timeout) - } - }, [control, showTimeout]) - const onClose = React.useCallback(() => { - nuxDialogs.dismissActiveNux() - onCloseOuter?.() - }, [nuxDialogs, onCloseOuter]) - - /* - * Actions - */ - const sharePost = React.useCallback(() => { - if (uri) { - control.close(() => { - setTimeout(() => { - logEvent('tmd:post', {}) - openComposer({ - text: _( - msg`Bluesky now has over 10 million users, and I was #${i18n.number( - userNumber, - )}!`, - ), - imageUris: [ - { - uri, - width: WIDTH, - height: HEIGHT, - altText: _( - msg`A virtual certificate with text "Celebrating 10M users on Bluesky, #${i18n.number( - userNumber, - )}, ${displayName} ${handle}, joined on ${joinedDate}"`, - ), - }, - ], - }) - }, 1e3) - }) - } - }, [ - _, - i18n, - control, - openComposer, - uri, - userNumber, - displayName, - handle, - joinedDate, - ]) - const onNativeShare = React.useCallback(() => { - if (uri) { - control.close(() => { - logEvent('tmd:share', {}) - shareUrl(uri) - }) - } - }, [uri, control]) - const onNativeDownload = React.useCallback(async () => { - if (uri) { - const res = await requestMediaLibraryPermissionsAsync() - - if (!res) { - Toast.show( - _( - msg`You must grant access to your photo library to save the image.`, - ), - 'xmark', - ) - return - } - - try { - await MediaLibrary.createAssetAsync(uri) - logEvent('tmd:download', {}) - Toast.show(_(msg`Image saved to your camera roll!`)) - } catch (e: unknown) { - console.log(e) - Toast.show(_(msg`An error occurred while saving the image!`), 'xmark') - return - } - } - }, [_, uri]) - const onWebDownload = React.useCallback(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() - logEvent('tmd:download', {}) - } - }, [uri]) - - /* - * Canvas stuff - */ - const imageRef = React.useRef(null) - const captureInProgress = React.useRef(false) - const onCanvasReady = React.useCallback(async () => { - if ( - imageRef.current && - imageRef.current.capture && - !captureInProgress.current - ) { - captureInProgress.current = true - const uri = await imageRef.current.capture() - setUri(uri) - } - }, [setUri]) - const canvas = isLoadingData ? null : ( - - - - - - - - - - - - - - {/* Centered content */} - - - - Celebrating {formatCount(i18n, 10000000)} users - {' '} - 🎉 - - - - # - - - {i18n.number(userNumber)} - - - - {Badge && ( - - - - )} - - {/* End centered content */} - - - - {/* - - */} - - - {displayName} - - - - {handle} - - - {profile.createdAt && ( - - Joined on {joinedDate} - - )} - - - - - - - - - - - - ) - - return ( - - - - - - - {error ? ( - - - (╯°□°)╯︵ ┻━┻ - - - {error} - - - ) : isLoadingData || isLoadingImage ? ( - - ) : ( - - - - )} - - - - {canvas} - - - - Thanks for being one of our first 10 million users. - - - - - Together, we're rebuilding the social internet. We're glad - you're here. - - - - - - - {gtMobile && ( - - Brag a little! - - )} - - - - - - - - - - - ) -} diff --git a/src/components/dialogs/nuxs/index.tsx b/src/components/dialogs/nuxs/index.tsx index b93831ad35..c740e1e6ad 100644 --- a/src/components/dialogs/nuxs/index.tsx +++ b/src/components/dialogs/nuxs/index.tsx @@ -16,10 +16,11 @@ import { import {useProfileQuery} from '#/state/queries/profile' import {SessionAccount, useSession} from '#/state/session' import {useOnboardingState} from '#/state/shell' +/* + * NUXs + */ import {NeueTypography} from '#/components/dialogs/nuxs/NeueTypography' import {isSnoozed, snooze, unsnooze} from '#/components/dialogs/nuxs/snoozing' -// NUXs -import {TenMillion} from '#/components/dialogs/nuxs/TenMillion' import {IS_DEV} from '#/env' type Context = { @@ -36,9 +37,6 @@ const queuedNuxs: { preferences: UsePreferencesQueryResponse }) => boolean }[] = [ - { - id: Nux.TenMillionDialog, - }, { id: Nux.NeueTypography, enabled(props) { @@ -176,7 +174,6 @@ function Inner({ return ( - {activeNux === Nux.TenMillionDialog && } {activeNux === Nux.NeueTypography && } ) diff --git a/src/state/queries/nuxs/definitions.ts b/src/state/queries/nuxs/definitions.ts index 63a8079623..8166602c8a 100644 --- a/src/state/queries/nuxs/definitions.ts +++ b/src/state/queries/nuxs/definitions.ts @@ -3,23 +3,16 @@ import zod from 'zod' import {BaseNux} from '#/state/queries/nuxs/types' export enum Nux { - TenMillionDialog = 'TenMillionDialog', NeueTypography = 'NeueTypography', } export const nuxNames = new Set(Object.values(Nux)) -export type AppNux = - | BaseNux<{ - id: Nux.TenMillionDialog - data: undefined - }> - | BaseNux<{ - id: Nux.NeueTypography - data: undefined - }> +export type AppNux = BaseNux<{ + id: Nux.NeueTypography + data: undefined +}> export const NuxSchemas: Record | undefined> = { - [Nux.TenMillionDialog]: undefined, [Nux.NeueTypography]: undefined, } diff --git a/src/view/com/home/HomeHeaderLayout.web.tsx b/src/view/com/home/HomeHeaderLayout.web.tsx index 9bfa82cd22..7049306eba 100644 --- a/src/view/com/home/HomeHeaderLayout.web.tsx +++ b/src/view/com/home/HomeHeaderLayout.web.tsx @@ -1,25 +1,16 @@ import React from 'react' import {StyleSheet, View} from 'react-native' -import Animated, { - useAnimatedStyle, - useReducedMotion, - useSharedValue, - withDelay, - withRepeat, - withSequence, - withSpring, - withTiming, -} from 'react-native-reanimated' +import Animated from 'react-native-reanimated' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {useMinimalShellHeaderTransform} from '#/lib/hooks/useMinimalShellTransform' +import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' +import {useKawaiiMode} from '#/state/preferences/kawaii' import {useSession} from '#/state/session' import {useShellLayout} from '#/state/shell/shell-layout' -import {useMinimalShellHeaderTransform} from 'lib/hooks/useMinimalShellTransform' -import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' -// import {Logo} from '#/view/icons/Logo' +import {Logo} from '#/view/icons/Logo' import {atoms as a, useTheme} from '#/alf' -import {Icon, Trigger} from '#/components/dialogs/nuxs/TenMillion/Trigger' import {Hashtag_Stroke2_Corner0_Rounded as FeedsIcon} from '#/components/icons/Hashtag' import {Link} from '#/components/Link' import {HomeHeaderLayoutMobile} from './HomeHeaderLayoutMobile' @@ -48,43 +39,7 @@ function HomeHeaderLayoutDesktopAndTablet({ const {headerHeight} = useShellLayout() const {hasSession} = useSession() const {_} = useLingui() - - // TEMPORARY - REMOVE AFTER MILLY - // This will just cause the icon to shake a bit when the user first opens the app, drawing attention to the celebration - // 🎉 - const rotate = useSharedValue(0) - const reducedMotion = useReducedMotion() - - // Run this a single time on app mount. - React.useEffect(() => { - if (reducedMotion) return - - // Waits 1500ms, then rotates 10 degrees with a spring animation. Repeats once. - rotate.value = withDelay( - 1000, - withRepeat( - withSequence( - withTiming(10, {duration: 100}), - withSpring(0, { - mass: 1, - damping: 1, - stiffness: 200, - overshootClamping: false, - }), - ), - 2, - false, - ), - ) - }, [rotate, reducedMotion]) - - const animatedStyle = useAnimatedStyle(() => ({ - transform: [ - { - rotateZ: `${rotate.value}deg`, - }, - ], - })) + const kawaii = useKawaiiMode() return ( <> @@ -101,30 +56,21 @@ function HomeHeaderLayoutDesktopAndTablet({ t.atoms.bg, t.atoms.border_contrast_low, styles.bar, + kawaii && {paddingTop: 22, paddingBottom: 16}, ]}> - - - {ctx => ( - - )} - - {/* */} - + + { - if (reducedMotion) return - - // Waits 1500ms, then rotates 10 degrees with a spring animation. Repeats once. - rotate.value = withDelay( - 1000, - withRepeat( - withSequence( - withTiming(10, {duration: 100}), - withSpring(0, { - mass: 1, - damping: 1, - stiffness: 200, - overshootClamping: false, - }), - ), - 2, - false, - ), - ) - }, [rotate, reducedMotion]) - - const animatedStyle = useAnimatedStyle(() => ({ - transform: [ - { - rotateZ: `${rotate.value}deg`, - }, - ], - })) - return ( - - - {ctx => ( - - )} - - {/* */} - + + + Date: Thu, 26 Sep 2024 11:58:47 -0500 Subject: [PATCH 09/16] Adjust dialog timing (#5502) --- src/components/dialogs/nuxs/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dialogs/nuxs/index.tsx b/src/components/dialogs/nuxs/index.tsx index c740e1e6ad..e7476972ad 100644 --- a/src/components/dialogs/nuxs/index.tsx +++ b/src/components/dialogs/nuxs/index.tsx @@ -41,7 +41,7 @@ const queuedNuxs: { id: Nux.NeueTypography, enabled(props) { if (props.currentProfile.createdAt) { - if (new Date(props.currentProfile.createdAt) < new Date('2024-09-25')) { + if (new Date(props.currentProfile.createdAt) < new Date('2024-10-01')) { return true } } From 1ae7fa6363259bd99b8c6d0f9c9656785e46309e Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Thu, 26 Sep 2024 11:59:04 -0500 Subject: [PATCH 10/16] Adjust header offset (#5501) --- src/components/hooks/useHeaderOffset.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/hooks/useHeaderOffset.ts b/src/components/hooks/useHeaderOffset.ts index e2290c04fb..5c80e18fe0 100644 --- a/src/components/hooks/useHeaderOffset.ts +++ b/src/components/hooks/useHeaderOffset.ts @@ -9,8 +9,8 @@ export function useHeaderOffset() { return 0 } const navBarHeight = 42 - const tabBarPad = 10 + 10 + 3 // padding + border - const normalLineHeight = 1.2 - const tabBarText = 16 * normalLineHeight * fontScale + const tabBarPad = 10 + 10 + 6 // padding + arbitrary + const normalLineHeight = 20 // matches tab bar + const tabBarText = normalLineHeight * fontScale return navBarHeight + tabBarPad + tabBarText } From 702dfa8536d1cf9168a40a1f6970da6daf8b2cfb Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Thu, 26 Sep 2024 12:47:27 -0500 Subject: [PATCH 11/16] Fix handle collapse on Android (#5504) --- src/view/com/util/PostMeta.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx index adf9c5eb1b..c0166a16ee 100644 --- a/src/view/com/util/PostMeta.tsx +++ b/src/view/com/util/PostMeta.tsx @@ -11,6 +11,7 @@ import {NON_BREAKING_SPACE} from '#/lib/strings/constants' import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeHandle} from '#/lib/strings/handles' import {niceDate} from '#/lib/strings/time' +import {isAndroid} from '#/platform/detection' import {precacheProfile} from '#/state/queries/profile' import {atoms as a, useTheme, web} from '#/alf' import {WebOnlyInlineLinkText} from '#/components/Link' @@ -70,7 +71,7 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => { )} - + { - - · - + {!isAndroid && ( + + · + + )} {({timeElapsed}) => ( From 863764b3fecd6905869f67eb46d80f7ffd7264b1 Mon Sep 17 00:00:00 2001 From: Hailey Date: Thu, 26 Sep 2024 10:51:22 -0700 Subject: [PATCH 12/16] [Share Extension] Use the proper identifier for if statement on iOS (#5505) --- modules/Share-with-Bluesky/Info.plist | 2 +- modules/Share-with-Bluesky/ShareViewController.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/Share-with-Bluesky/Info.plist b/modules/Share-with-Bluesky/Info.plist index 43f46a5e56..d73a9093fc 100644 --- a/modules/Share-with-Bluesky/Info.plist +++ b/modules/Share-with-Bluesky/Info.plist @@ -40,4 +40,4 @@ CFBundleShortVersionString $(MARKETING_VERSION) - + \ No newline at end of file diff --git a/modules/Share-with-Bluesky/ShareViewController.swift b/modules/Share-with-Bluesky/ShareViewController.swift index 63143277a5..e166c22d20 100644 --- a/modules/Share-with-Bluesky/ShareViewController.swift +++ b/modules/Share-with-Bluesky/ShareViewController.swift @@ -101,7 +101,7 @@ class ShareViewController: UIViewController { private func handleVideos(items: [NSItemProvider]) async { let firstItem = items.first - if let dataUri = try? await firstItem?.loadItem(forTypeIdentifier: "public.video") as? URL { + if let dataUri = try? await firstItem?.loadItem(forTypeIdentifier: "public.movie") as? URL { let ext = String(dataUri.lastPathComponent.split(separator: ".").last ?? "mp4") if let tempUrl = getTempUrl(ext: ext) { let data = try? Data(contentsOf: dataUri) From 175df72972343795a67be208e58edb02267e1ced Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Thu, 26 Sep 2024 12:58:47 -0500 Subject: [PATCH 13/16] Fix weird button wrapping on splash (#5507) * Fix weird button wrapping on splash * Web --- src/view/com/auth/SplashScreen.tsx | 9 ++++----- src/view/com/auth/SplashScreen.web.tsx | 7 ++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/view/com/auth/SplashScreen.tsx b/src/view/com/auth/SplashScreen.tsx index a18f17612e..ae18f13905 100644 --- a/src/view/com/auth/SplashScreen.tsx +++ b/src/view/com/auth/SplashScreen.tsx @@ -39,21 +39,21 @@ export const SplashScreen = ({ What's up? - +