From 6438ec6d282122206454110be2e10365d920dcf3 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 21 Nov 2024 12:10:16 +0000 Subject: [PATCH 01/32] Disable vertical scrollbars on Android only (#6586) --- src/view/com/util/List.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/view/com/util/List.tsx b/src/view/com/util/List.tsx index 4ee4d7d0b3..fa93ec5e6d 100644 --- a/src/view/com/util/List.tsx +++ b/src/view/com/util/List.tsx @@ -7,7 +7,7 @@ import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIX import {useDedupe} from '#/lib/hooks/useDedupe' import {useScrollHandlers} from '#/lib/ScrollContext' import {addStyle} from '#/lib/styles' -import {isIOS} from '#/platform/detection' +import {isAndroid, isIOS} from '#/platform/detection' import {useLightbox} from '#/state/lightbox' import {useTheme} from '#/alf' import {FlatList_INTERNAL} from './Views' @@ -149,6 +149,7 @@ function ListImpl( scrollEventThrottle={1} onViewableItemsChanged={onViewableItemsChanged} viewabilityConfig={viewabilityConfig} + showsVerticalScrollIndicator={!isAndroid} style={style} ref={ref} /> From ad83b8303e6a2d3c67838e535651bcaca1f943ff Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 21 Nov 2024 12:10:34 +0000 Subject: [PATCH 02/32] Remove unused RN.Animated code (#6584) * Remove unused RN.Animated code * Rm more dead code --- src/view/com/pager/Pager.tsx | 7 +- src/view/com/util/Selector.tsx | 148 ------------- .../com/util/anim/TriggerableAnimated.tsx | 74 ------- src/view/com/util/text/RichText.tsx | 201 ------------------ 4 files changed, 3 insertions(+), 427 deletions(-) delete mode 100644 src/view/com/util/Selector.tsx delete mode 100644 src/view/com/util/anim/TriggerableAnimated.tsx delete mode 100644 src/view/com/util/text/RichText.tsx diff --git a/src/view/com/pager/Pager.tsx b/src/view/com/pager/Pager.tsx index aca3245a75..de04099917 100644 --- a/src/view/com/pager/Pager.tsx +++ b/src/view/com/pager/Pager.tsx @@ -1,5 +1,5 @@ import React, {forwardRef} from 'react' -import {Animated, View} from 'react-native' +import {View} from 'react-native' import PagerView, { PagerViewOnPageScrollEvent, PagerViewOnPageSelectedEvent, @@ -10,7 +10,6 @@ import {LogEvents} from '#/lib/statsig/events' import {atoms as a, native} from '#/alf' export type PageSelectedEvent = PagerViewOnPageSelectedEvent -const AnimatedPagerView = Animated.createAnimatedComponent(PagerView) export interface PagerRef { setPage: ( @@ -138,7 +137,7 @@ export const Pager = forwardRef>( selectedPage, onSelect: onTabBarSelect, })} - >( onPageSelected={onPageSelectedInner} onPageScroll={onPageScroll}> {children} - + ) }, diff --git a/src/view/com/util/Selector.tsx b/src/view/com/util/Selector.tsx deleted file mode 100644 index 86bd322e38..0000000000 --- a/src/view/com/util/Selector.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import {createRef, useMemo, useRef, useState} from 'react' -import {Animated, Pressable, StyleSheet, View} from 'react-native' -import {msg} from '@lingui/macro' -import {useLingui} from '@lingui/react' - -import {usePalette} from '#/lib/hooks/usePalette' -import {Text} from './text/Text' - -interface Layout { - x: number - width: number -} - -export function Selector({ - selectedIndex, - items, - panX, - onSelect, -}: { - selectedIndex: number - items: string[] - panX: Animated.Value - onSelect?: (index: number) => void -}) { - const {_} = useLingui() - const containerRef = useRef(null) - const pal = usePalette('default') - const [itemLayouts, setItemLayouts] = useState( - undefined, - ) - const itemRefs = useMemo( - () => Array.from({length: items.length}).map(() => createRef()), - [items.length], - ) - - const currentLayouts = useMemo(() => { - const left = itemLayouts?.[selectedIndex - 1] || {x: 0, width: 0} - const middle = itemLayouts?.[selectedIndex] || {x: 0, width: 0} - const right = itemLayouts?.[selectedIndex + 1] || { - x: middle.x + 20, - width: middle.width, - } - return [left, middle, right] - }, [selectedIndex, itemLayouts]) - - const underlineStyle = { - backgroundColor: pal.colors.text, - left: panX.interpolate({ - inputRange: [-1, 0, 1], - outputRange: [ - currentLayouts[0].x, - currentLayouts[1].x, - currentLayouts[2].x, - ], - }), - width: panX.interpolate({ - inputRange: [-1, 0, 1], - outputRange: [ - currentLayouts[0].width, - currentLayouts[1].width, - currentLayouts[2].width, - ], - }), - } - - const onLayout = () => { - const promises = [] - for (let i = 0; i < items.length; i++) { - promises.push( - new Promise(resolve => { - if (!containerRef.current || !itemRefs[i].current) { - return resolve({x: 0, width: 0}) - } - itemRefs[i].current?.measureLayout( - containerRef.current, - (x: number, _y: number, width: number) => { - resolve({x, width}) - }, - ) - }), - ) - } - Promise.all(promises).then((layouts: Layout[]) => { - setItemLayouts(layouts) - }) - } - - const onPressItem = (index: number) => { - onSelect?.(index) - } - - const numItems = items.length - - return ( - - - {items.map((item, i) => { - const selected = i === selectedIndex - return ( - onPressItem(i)} - accessibilityLabel={_(msg`Select ${item}`)} - accessibilityHint={_(msg`Select option ${i} of ${numItems}`)}> - - - {item} - - - - ) - })} - - ) -} - -const styles = StyleSheet.create({ - outer: { - flexDirection: 'row', - paddingTop: 8, - paddingBottom: 12, - paddingHorizontal: 14, - }, - item: { - marginRight: 14, - paddingHorizontal: 10, - }, - label: { - fontWeight: '600', - }, - labelSelected: { - fontWeight: '600', - }, - underline: { - position: 'absolute', - height: 4, - bottom: 0, - }, -}) diff --git a/src/view/com/util/anim/TriggerableAnimated.tsx b/src/view/com/util/anim/TriggerableAnimated.tsx deleted file mode 100644 index 97605fb46c..0000000000 --- a/src/view/com/util/anim/TriggerableAnimated.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React from 'react' -import {Animated, StyleProp, View, ViewStyle} from 'react-native' - -import {useAnimatedValue} from '#/lib/hooks/useAnimatedValue' - -type CreateAnimFn = (interp: Animated.Value) => Animated.CompositeAnimation -type FinishCb = () => void - -interface TriggeredAnimation { - start: CreateAnimFn - style: ( - interp: Animated.Value, - ) => Animated.WithAnimatedValue> -} - -export interface TriggerableAnimatedRef { - trigger: (anim: TriggeredAnimation, onFinish?: FinishCb) => void -} - -type TriggerableAnimatedProps = React.PropsWithChildren<{}> - -type PropsInner = TriggerableAnimatedProps & { - anim: TriggeredAnimation - onFinish: () => void -} - -export const TriggerableAnimated = React.forwardRef< - TriggerableAnimatedRef, - TriggerableAnimatedProps ->(function TriggerableAnimatedImpl({children, ...props}, ref) { - const [anim, setAnim] = React.useState( - undefined, - ) - const [finishCb, setFinishCb] = React.useState( - undefined, - ) - React.useImperativeHandle(ref, () => ({ - trigger(v: TriggeredAnimation, cb?: FinishCb) { - setFinishCb(() => cb) // note- wrap in function due to react behaviors around setstate - setAnim(v) - }, - })) - const onFinish = () => { - finishCb?.() - setAnim(undefined) - setFinishCb(undefined) - } - return ( - - {anim ? ( - - {children} - - ) : ( - children - )} - - ) -}) - -function AnimatingView({ - anim, - onFinish, - children, -}: React.PropsWithChildren) { - const interp = useAnimatedValue(0) - React.useEffect(() => { - anim?.start(interp).start(() => { - onFinish() - }) - }) - const animStyle = anim?.style(interp) - return {children} -} diff --git a/src/view/com/util/text/RichText.tsx b/src/view/com/util/text/RichText.tsx deleted file mode 100644 index a4cf517a41..0000000000 --- a/src/view/com/util/text/RichText.tsx +++ /dev/null @@ -1,201 +0,0 @@ -import React from 'react' -import {StyleProp, TextStyle} from 'react-native' -import {AppBskyRichtextFacet, RichText as RichTextObj} from '@atproto/api' - -import {usePalette} from '#/lib/hooks/usePalette' -import {makeTagLink} from '#/lib/routes/links' -import {toShortUrl} from '#/lib/strings/url-helpers' -import {lh} from '#/lib/styles' -import {TypographyVariant, useTheme} from '#/lib/ThemeContext' -import {isNative} from '#/platform/detection' -import {TagMenu, useTagMenuControl} from '#/components/TagMenu' -import {TextLink} from '../Link' -import {Text} from './Text' - -const WORD_WRAP = {wordWrap: 1} - -/** - * @deprecated use `#/components/RichText` - */ -export function RichText({ - testID, - type = 'md', - richText, - lineHeight = 1.2, - style, - numberOfLines, - selectable, - noLinks, -}: { - testID?: string - type?: TypographyVariant - richText?: RichTextObj - lineHeight?: number - style?: StyleProp - numberOfLines?: number - selectable?: boolean - noLinks?: boolean -}) { - const theme = useTheme() - const pal = usePalette('default') - const lineHeightStyle = lh(theme, type, lineHeight) - - if (!richText) { - return null - } - - const {text, facets} = richText - if (!facets?.length) { - if (/^\p{Extended_Pictographic}+$/u.test(text) && text.length <= 5) { - style = { - fontSize: 26, - lineHeight: 30, - } - return ( - // @ts-ignore web only -prf - - {text} - - ) - } - return ( - - {text} - - ) - } - if (!style) { - style = [] - } else if (!Array.isArray(style)) { - style = [style] - } - - const els = [] - let key = 0 - for (const segment of richText.segments()) { - const link = segment.link - const mention = segment.mention - const tag = segment.tag - if ( - !noLinks && - mention && - AppBskyRichtextFacet.validateMention(mention).success - ) { - els.push( - , - ) - } else if (link && AppBskyRichtextFacet.validateLink(link).success) { - if (noLinks) { - els.push(toShortUrl(segment.text)) - } else { - els.push( - , - ) - } - } else if ( - !noLinks && - tag && - AppBskyRichtextFacet.validateTag(tag).success - ) { - els.push( - , - ) - } else { - els.push(segment.text) - } - key++ - } - return ( - - {els} - - ) -} - -function RichTextTag({ - text: tag, - type, - style, - lineHeightStyle, - selectable, -}: { - text: string - type?: TypographyVariant - style?: StyleProp - lineHeightStyle?: TextStyle - selectable?: boolean -}) { - const pal = usePalette('default') - const control = useTagMenuControl() - - const open = React.useCallback(() => { - control.open() - }, [control]) - - return ( - - - {isNative ? ( - - ) : ( - - {tag} - - )} - - - ) -} From dff11fc076c67f03319db375dfd844026dc03046 Mon Sep 17 00:00:00 2001 From: lauren Date: Thu, 21 Nov 2024 12:40:39 -0500 Subject: [PATCH 03/32] Pin exact version of React Compiler packages (#6607) This PR pins the various React Compiler packages to their exact versions. While it is typically safe to upgrade the compiler, we recommend manual upgrades because changes in memoization may or may not be unexpected. In the near future we will open source an upgrade script that automates comparing the current compiled output with the upgraded output and prints a diff for human confirmation prior to committing the upgrade. --- package.json | 6 +++--- yarn.lock | 11 +++-------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 3f93da07b7..03e6db8117 100644 --- a/package.json +++ b/package.json @@ -163,7 +163,7 @@ "postinstall-postinstall": "^2.1.0", "psl": "^1.9.0", "react": "18.2.0", - "react-compiler-runtime": "^19.0.0-beta-a7bf2bd-20241110", + "react-compiler-runtime": "19.0.0-beta-a7bf2bd-20241110", "react-dom": "^18.2.0", "react-image-crop": "^11.0.7", "react-keyed-flatten-children": "^3.0.0", @@ -232,14 +232,14 @@ "babel-jest": "^29.7.0", "babel-plugin-macros": "^3.1.0", "babel-plugin-module-resolver": "^5.0.0", - "babel-plugin-react-compiler": "^19.0.0-beta-a7bf2bd-20241110", + "babel-plugin-react-compiler": "19.0.0-beta-a7bf2bd-20241110", "babel-preset-expo": "^10.0.0", "eslint": "^8.19.0", "eslint-plugin-bsky-internal": "link:./eslint", "eslint-plugin-ft-flow": "^2.0.3", "eslint-plugin-lingui": "^0.2.0", "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-compiler": "^19.0.0-beta-a7bf2bd-20241110", + "eslint-plugin-react-compiler": "19.0.0-beta-a7bf2bd-20241110", "eslint-plugin-react-native-a11y": "^3.3.0", "eslint-plugin-simple-import-sort": "^12.0.0", "file-loader": "6.2.0", diff --git a/yarn.lock b/yarn.lock index 89a8d86ab5..bda793497b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6820,11 +6820,6 @@ resolved "https://registry.yarnpkg.com/@types/hammerjs/-/hammerjs-2.0.41.tgz#f6ecf57d1b12d2befcce00e928a6a097c22980aa" integrity sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA== -"@types/he@^1.1.2": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@types/he/-/he-1.2.0.tgz#3845193e597d943bab4e61ca5d7f3d8fc3d572a3" - integrity sha512-uH2smqTN4uGReAiKedIVzoLUAXIYLBTbSofhx3hbNqj74Ua6KqFsLYszduTrLCMEAEAozF73DbGi/SC1bzQq4g== - "@types/html-minifier-terser@^6.0.0": version "6.1.0" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" @@ -8007,7 +8002,7 @@ babel-plugin-polyfill-regenerator@^0.5.2: dependencies: "@babel/helper-define-polyfill-provider" "^0.4.2" -babel-plugin-react-compiler@^19.0.0-beta-a7bf2bd-20241110: +babel-plugin-react-compiler@19.0.0-beta-a7bf2bd-20241110: version "19.0.0-beta-a7bf2bd-20241110" resolved "https://registry.yarnpkg.com/babel-plugin-react-compiler/-/babel-plugin-react-compiler-19.0.0-beta-a7bf2bd-20241110.tgz#9e7abf2d9b6d0908cca7df010695678b830b36ae" integrity sha512-WdxXtLxsV4gh/GlEK4fuFDGkcED0Wb9UJEBB6Uc1SFqRFEmJNFKboW+Z4NUS5gYrPImqrjh4IwHAmgS6ZBg4Cg== @@ -10055,7 +10050,7 @@ eslint-plugin-lingui@^0.2.0: dependencies: "@typescript-eslint/utils" "^5.61.0" -eslint-plugin-react-compiler@^19.0.0-beta-a7bf2bd-20241110: +eslint-plugin-react-compiler@19.0.0-beta-a7bf2bd-20241110: version "19.0.0-beta-a7bf2bd-20241110" resolved "https://registry.yarnpkg.com/eslint-plugin-react-compiler/-/eslint-plugin-react-compiler-19.0.0-beta-a7bf2bd-20241110.tgz#b03c043cc70cc9297e4f79f3370900aacd013d13" integrity sha512-b5/hRnOQlnH9CEnJQ6UrPoIAG4y/wIGv+OVEHTeAkbq+1uojfcuQyLToYvK1T9a6vz5WQHeMjQqFOZk3mtWorg== @@ -15906,7 +15901,7 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.7, rc@~1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-compiler-runtime@^19.0.0-beta-a7bf2bd-20241110: +react-compiler-runtime@19.0.0-beta-a7bf2bd-20241110: version "19.0.0-beta-a7bf2bd-20241110" resolved "https://registry.yarnpkg.com/react-compiler-runtime/-/react-compiler-runtime-19.0.0-beta-a7bf2bd-20241110.tgz#58587b1a05d50f78f0a72f5e857d541f5dcb5cd1" integrity sha512-cSkrfz2eGcC9UZ/83mLf3aqKKDVjFkWJeA/kiYLwKTNp7B0Lq5M1FQ3vTfgSC027fK5ZutXU/JsCS5KxTwk8Mg== From ff23ddb556be4b2a9c4029dce6f857df34fc0b6b Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 21 Nov 2024 19:20:46 +0000 Subject: [PATCH 04/32] Don't underline links on native hover (#6588) --- src/components/Link.tsx | 18 +++++++----------- src/components/RichText.tsx | 18 ++++++------------ 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/components/Link.tsx b/src/components/Link.tsx index 054a543c19..a5203b2521 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -274,11 +274,6 @@ export function InlineLinkText({ onOut: onHoverOut, } = useInteractionState() const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState() - const { - state: pressed, - onIn: onPressIn, - onOut: onPressOut, - } = useInteractionState() const flattenedStyle = flatten(style) || {} return ( @@ -289,19 +284,20 @@ export function InlineLinkText({ {...rest} style={[ {color: t.palette.primary_500}, - (hovered || focused || pressed) && + (hovered || focused) && !disableUnderline && { - ...web({outline: 0}), - textDecorationLine: 'underline', - textDecorationColor: flattenedStyle.color ?? t.palette.primary_500, + ...web({ + outline: 0, + textDecorationLine: 'underline', + textDecorationColor: + flattenedStyle.color ?? t.palette.primary_500, + }), }, flattenedStyle, ]} role="link" onPress={download ? undefined : onPress} onLongPress={onLongPress} - onPressIn={onPressIn} - onPressOut={onPressOut} onFocus={onFocus} onBlur={onBlur} onMouseEnter={onHoverIn} diff --git a/src/components/RichText.tsx b/src/components/RichText.tsx index 8f6358dd5a..e2d05ac6cb 100644 --- a/src/components/RichText.tsx +++ b/src/components/RichText.tsx @@ -53,7 +53,6 @@ export function RichText({ const plainStyles = [a.leading_snug, flattenedStyle] const interactiveStyles = [ a.leading_snug, - a.pointer_events_auto, flatten(interactiveStyle), flattenedStyle, ] @@ -194,11 +193,6 @@ function RichTextTag({ onOut: onHoverOut, } = useInteractionState() const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState() - const { - state: pressed, - onIn: onPressIn, - onOut: onPressOut, - } = useInteractionState() const navigation = useNavigation() const navigateToPage = React.useCallback(() => { @@ -228,8 +222,6 @@ function RichTextTag({ accessibilityRole: isNative ? 'button' : undefined, onPress: navigateToPage, onLongPress: openDialog, - onPressIn: onPressIn, - onPressOut: onPressOut, })} {...web({ onMouseEnter: onHoverIn, @@ -243,10 +235,12 @@ function RichTextTag({ cursor: 'pointer', }), {color: t.palette.primary_500}, - (hovered || focused || pressed) && { - ...web({outline: 0}), - textDecorationLine: 'underline', - textDecorationColor: t.palette.primary_500, + (hovered || focused) && { + ...web({ + outline: 0, + textDecorationLine: 'underline', + textDecorationColor: t.palette.primary_500, + }), }, style, ]}> From cc60566cde880deeea1e50774a2108af8f7766f8 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 21 Nov 2024 20:44:40 +0000 Subject: [PATCH 05/32] Fix Fast Refresh in files (#6609) * Separate non-components from components * Mark old Text as deprecated * Move emoji utilities to non-React file * Fix type * Fix import --- src/alf/typography.tsx | 135 ++++++++++++++++ src/components/Typography.tsx | 145 ++---------------- src/screens/Onboarding/Layout.tsx | 3 +- .../com/composer/text-input/TextInput.tsx | 2 +- .../com/composer/text-input/TextInput.web.tsx | 2 +- src/view/com/util/text/Text.tsx | 8 +- 6 files changed, 156 insertions(+), 139 deletions(-) create mode 100644 src/alf/typography.tsx diff --git a/src/alf/typography.tsx b/src/alf/typography.tsx new file mode 100644 index 0000000000..d00910bf87 --- /dev/null +++ b/src/alf/typography.tsx @@ -0,0 +1,135 @@ +import React from 'react' +import {TextProps as RNTextProps} from 'react-native' +import {StyleProp, TextStyle} from 'react-native' +import {UITextView} from 'react-native-uitextview' +import createEmojiRegex from 'emoji-regex' + +import {isNative} from '#/platform/detection' +import {Alf, applyFonts, atoms, flatten} from '#/alf' + +/** + * Util to calculate lineHeight from a text size atom and a leading atom + * + * Example: + * `leading(atoms.text_md, atoms.leading_normal)` // => 24 + */ +export function leading< + Size extends {fontSize?: number}, + Leading extends {lineHeight?: number}, +>(textSize: Size, leading: Leading) { + const size = textSize?.fontSize || atoms.text_md.fontSize + const lineHeight = leading?.lineHeight || atoms.leading_normal.lineHeight + return Math.round(size * lineHeight) +} + +/** + * Ensures that `lineHeight` defaults to a relative value of `1`, or applies + * other relative leading atoms. + * + * If the `lineHeight` value is > 2, we assume it's an absolute value and + * returns it as-is. + */ +export function normalizeTextStyles( + styles: StyleProp, + { + fontScale, + fontFamily, + }: { + fontScale: number + fontFamily: Alf['fonts']['family'] + } & Pick, +) { + const s = flatten(styles) + // should always be defined on these components + s.fontSize = (s.fontSize || atoms.text_md.fontSize) * fontScale + + if (s?.lineHeight) { + if (s.lineHeight !== 0 && s.lineHeight <= 2) { + s.lineHeight = Math.round(s.fontSize * s.lineHeight) + } + } else if (!isNative) { + s.lineHeight = s.fontSize + } + + applyFonts(s, fontFamily) + + return s +} + +export type StringChild = string | (string | null)[] +export type TextProps = Omit & { + /** + * Lets the user select text, to use the native copy and paste functionality. + */ + selectable?: boolean + /** + * Provides `data-*` attributes to the underlying `UITextView` component on + * web only. + */ + dataSet?: Record + /** + * Appears as a small tooltip on web hover. + */ + title?: string +} & ( + | { + emoji?: true + children: StringChild + } + | { + emoji?: false + children: RNTextProps['children'] + } + ) + +const EMOJI = createEmojiRegex() + +export function childHasEmoji(children: React.ReactNode) { + return (Array.isArray(children) ? children : [children]).some( + child => typeof child === 'string' && createEmojiRegex().test(child), + ) +} + +export function childIsString( + children: React.ReactNode, +): children is StringChild { + return ( + typeof children === 'string' || + (Array.isArray(children) && + children.every(child => typeof child === 'string' || child === null)) + ) +} + +export function renderChildrenWithEmoji( + children: StringChild, + props: Omit = {}, +) { + const normalized = Array.isArray(children) ? children : [children] + + return ( + + {normalized.map(child => { + if (typeof child !== 'string') return child + + const emojis = child.match(EMOJI) + + if (emojis === null) { + return child + } + + return child.split(EMOJI).map((stringPart, index) => ( + + {stringPart} + {emojis[index] ? ( + + {emojis[index]} + + ) : null} + + )) + })} + + ) +} diff --git a/src/components/Typography.tsx b/src/components/Typography.tsx index 69e0732713..4bcbf9f091 100644 --- a/src/components/Typography.tsx +++ b/src/components/Typography.tsx @@ -1,140 +1,17 @@ -import React from 'react' -import {StyleProp, TextProps as RNTextProps, TextStyle} from 'react-native' import {UITextView} from 'react-native-uitextview' -import createEmojiRegex from 'emoji-regex' import {logger} from '#/logger' -import {isIOS, isNative} from '#/platform/detection' -import {Alf, applyFonts, atoms, flatten, useAlf, useTheme, web} from '#/alf' +import {isIOS} from '#/platform/detection' +import {atoms, flatten, useAlf, useTheme, web} from '#/alf' +import { + childHasEmoji, + childIsString, + normalizeTextStyles, + renderChildrenWithEmoji, + TextProps, +} from '#/alf/typography' import {IS_DEV} from '#/env' - -export type StringChild = string | (string | null)[] - -export type TextProps = Omit & { - /** - * Lets the user select text, to use the native copy and paste functionality. - */ - selectable?: boolean - /** - * Provides `data-*` attributes to the underlying `UITextView` component on - * web only. - */ - dataSet?: Record - /** - * Appears as a small tooltip on web hover. - */ - title?: string -} & ( - | { - emoji?: true - children: StringChild - } - | { - emoji?: false - children: RNTextProps['children'] - } - ) - -const EMOJI = createEmojiRegex() - -export function childHasEmoji(children: React.ReactNode) { - return (Array.isArray(children) ? children : [children]).some( - child => typeof child === 'string' && createEmojiRegex().test(child), - ) -} - -export function childIsString( - children: React.ReactNode, -): children is StringChild { - return ( - typeof children === 'string' || - (Array.isArray(children) && - children.every(child => typeof child === 'string' || child === null)) - ) -} - -export function renderChildrenWithEmoji( - children: StringChild, - props: Omit = {}, -) { - const normalized = Array.isArray(children) ? children : [children] - - return ( - - {normalized.map(child => { - if (typeof child !== 'string') return child - - const emojis = child.match(EMOJI) - - if (emojis === null) { - return child - } - - return child.split(EMOJI).map((stringPart, index) => ( - - {stringPart} - {emojis[index] ? ( - - {emojis[index]} - - ) : null} - - )) - })} - - ) -} - -/** - * Util to calculate lineHeight from a text size atom and a leading atom - * - * Example: - * `leading(atoms.text_md, atoms.leading_normal)` // => 24 - */ -export function leading< - Size extends {fontSize?: number}, - Leading extends {lineHeight?: number}, ->(textSize: Size, leading: Leading) { - const size = textSize?.fontSize || atoms.text_md.fontSize - const lineHeight = leading?.lineHeight || atoms.leading_normal.lineHeight - return Math.round(size * lineHeight) -} - -/** - * Ensures that `lineHeight` defaults to a relative value of `1`, or applies - * other relative leading atoms. - * - * If the `lineHeight` value is > 2, we assume it's an absolute value and - * returns it as-is. - */ -export function normalizeTextStyles( - styles: StyleProp, - { - fontScale, - fontFamily, - }: { - fontScale: number - fontFamily: Alf['fonts']['family'] - } & Pick, -) { - const s = flatten(styles) - // should always be defined on these components - s.fontSize = (s.fontSize || atoms.text_md.fontSize) * fontScale - - if (s?.lineHeight) { - if (s.lineHeight !== 0 && s.lineHeight <= 2) { - s.lineHeight = Math.round(s.fontSize * s.lineHeight) - } - } else if (!isNative) { - s.lineHeight = s.fontSize - } - - applyFonts(s, fontFamily) - - return s -} +export type {TextProps} /** * Our main text component. Use this most of the time. @@ -183,7 +60,7 @@ export function Text({ ) } -export function createHeadingElement({level}: {level: number}) { +function createHeadingElement({level}: {level: number}) { return function HeadingElement({style, ...rest}: TextProps) { const attr = web({ diff --git a/src/screens/Onboarding/Layout.tsx b/src/screens/Onboarding/Layout.tsx index 02b207303e..4a07ebd830 100644 --- a/src/screens/Onboarding/Layout.tsx +++ b/src/screens/Onboarding/Layout.tsx @@ -17,10 +17,11 @@ import { useTheme, web, } from '#/alf' +import {leading} from '#/alf/typography' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron' import {createPortalGroup} from '#/components/Portal' -import {leading, P, Text} from '#/components/Typography' +import {P, Text} from '#/components/Typography' import {IS_DEV} from '#/env' const COL_WIDTH = 420 diff --git a/src/view/com/composer/text-input/TextInput.tsx b/src/view/com/composer/text-input/TextInput.tsx index 10cf1a931b..96cecb37cb 100644 --- a/src/view/com/composer/text-input/TextInput.tsx +++ b/src/view/com/composer/text-input/TextInput.tsx @@ -31,7 +31,7 @@ import { suggestLinkCardUri, } from '#/view/com/composer/text-input/text-input-util' import {atoms as a, useAlf} from '#/alf' -import {normalizeTextStyles} from '#/components/Typography' +import {normalizeTextStyles} from '#/alf/typography' import {Autocomplete} from './mobile/Autocomplete' export interface TextInputRef { diff --git a/src/view/com/composer/text-input/TextInput.web.tsx b/src/view/com/composer/text-input/TextInput.web.tsx index fa742d2587..34d19e14cc 100644 --- a/src/view/com/composer/text-input/TextInput.web.tsx +++ b/src/view/com/composer/text-input/TextInput.web.tsx @@ -23,8 +23,8 @@ import { } from '#/view/com/composer/text-input/text-input-util' import {textInputWebEmitter} from '#/view/com/composer/text-input/textInputWebEmitter' import {atoms as a, useAlf} from '#/alf' +import {normalizeTextStyles} from '#/alf/typography' import {Portal} from '#/components/Portal' -import {normalizeTextStyles} from '#/components/Typography' import {Text} from '../../util/text/Text' import {createSuggestion} from './web/Autocomplete' import {Emoji} from './web/EmojiPicker.web' diff --git a/src/view/com/util/text/Text.tsx b/src/view/com/util/text/Text.tsx index dbf5e2e13f..a5278e0a07 100644 --- a/src/view/com/util/text/Text.tsx +++ b/src/view/com/util/text/Text.tsx @@ -12,7 +12,7 @@ import { childIsString, renderChildrenWithEmoji, StringChild, -} from '#/components/Typography' +} from '#/alf/typography' import {IS_DEV} from '#/env' export type CustomTextProps = Omit & { @@ -32,7 +32,11 @@ export type CustomTextProps = Omit & { } ) -export function Text({ +export {Text_DEPRECATED as Text} +/** + * @deprecated use Text from Typography instead. + */ +function Text_DEPRECATED({ type = 'md', children, emoji, From 48cda1350e877a43fa42923a75e02e1f0e5b0e1c Mon Sep 17 00:00:00 2001 From: jviide Date: Thu, 21 Nov 2024 22:45:40 +0200 Subject: [PATCH 06/32] Dedup zod versions in bskyembed/yarn.lock (#6610) --- bskyembed/yarn.lock | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/bskyembed/yarn.lock b/bskyembed/yarn.lock index 46c8519ba1..6a86395c1b 100644 --- a/bskyembed/yarn.lock +++ b/bskyembed/yarn.lock @@ -4209,12 +4209,7 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -zod@^3.21.4: - version "3.22.4" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff" - integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg== - -zod@^3.23.8: +zod@^3.21.4, zod@^3.23.8: version "3.23.8" resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g== From f67e00a85c61ae8be65afda3fe75e7eaac1c2ffa Mon Sep 17 00:00:00 2001 From: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com> Date: Fri, 22 Nov 2024 02:40:19 +0530 Subject: [PATCH 07/32] fix: reused getRkey function in embed component (#6591) --- bskyembed/src/components/embed.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bskyembed/src/components/embed.tsx b/bskyembed/src/components/embed.tsx index 82c3fd60a0..e96acb89e1 100644 --- a/bskyembed/src/components/embed.tsx +++ b/bskyembed/src/components/embed.tsx @@ -9,7 +9,6 @@ import { AppBskyGraphDefs, AppBskyGraphStarterpack, AppBskyLabelerDefs, - AtUri, } from '@atproto/api' import {ComponentChildren, h} from 'preact' import {useMemo} from 'preact/hooks' @@ -437,14 +436,14 @@ function StarterPackEmbed({ // from #/lib/strings/starter-pack.ts function getStarterPackImage(starterPack: AppBskyGraphDefs.StarterPackView) { - const rkey = new AtUri(starterPack.uri).rkey + const rkey = getRkey(starterPack.uri) return `https://ogcard.cdn.bsky.app/start/${starterPack.creator.did}/${rkey}` } function getStarterPackHref( starterPack: AppBskyGraphDefs.StarterPackViewBasic, ) { - const rkey = new AtUri(starterPack.uri).rkey + const rkey = getRkey(starterPack.uri) const handleOrDid = starterPack.creator.handle || starterPack.creator.did return `/starter-pack/${handleOrDid}/${rkey}` } From 9f1e648686498ad6ec68a7c4487b1090f8e74f89 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Thu, 21 Nov 2024 22:32:32 +0000 Subject: [PATCH 08/32] Fix overflow issue on iOS autocomplete, among other things (#6611) * stop using ref in render * fix display name fallback on web * use unicode ellipsis for useGrapheme * fix overflow issue * sanitize handle/displayname on web --- .../composer/text-input/hooks/useGrapheme.tsx | 2 +- .../text-input/mobile/Autocomplete.tsx | 57 +++++++------------ .../composer/text-input/web/Autocomplete.tsx | 8 ++- 3 files changed, 27 insertions(+), 40 deletions(-) diff --git a/src/view/com/composer/text-input/hooks/useGrapheme.tsx b/src/view/com/composer/text-input/hooks/useGrapheme.tsx index 01b5b96989..aa375ff470 100644 --- a/src/view/com/composer/text-input/hooks/useGrapheme.tsx +++ b/src/view/com/composer/text-input/hooks/useGrapheme.tsx @@ -13,7 +13,7 @@ export const useGrapheme = () => { if (graphemes.length > length) { remainingCharacters = 0 - name = `${graphemes.slice(0, length).join('')}...` + name = `${graphemes.slice(0, length).join('')}…` } else { remainingCharacters = length - graphemes.length name = graphemes.join('') diff --git a/src/view/com/composer/text-input/mobile/Autocomplete.tsx b/src/view/com/composer/text-input/mobile/Autocomplete.tsx index f1b5941364..0fda6843b4 100644 --- a/src/view/com/composer/text-input/mobile/Autocomplete.tsx +++ b/src/view/com/composer/text-input/mobile/Autocomplete.tsx @@ -1,7 +1,5 @@ -import {useRef} from 'react' import {View} from 'react-native' import Animated, {FadeInDown, FadeOut} from 'react-native-reanimated' -import {AppBskyActorDefs} from '@atproto/api' import {Trans} from '@lingui/macro' import {PressableScale} from '#/lib/custom-animations/PressableScale' @@ -11,7 +9,6 @@ import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete' import {UserAvatar} from '#/view/com/util/UserAvatar' import {atoms as a, useTheme} from '#/alf' import {Text} from '#/components/Typography' -import {useGrapheme} from '../hooks/useGrapheme' export function Autocomplete({ prefix, @@ -22,15 +19,11 @@ export function Autocomplete({ }) { const t = useTheme() - const {getGraphemeString} = useGrapheme() const isActive = !!prefix - const {data: suggestions, isFetching} = useActorAutocompleteQuery(prefix) - const suggestionsRef = useRef< - AppBskyActorDefs.ProfileViewBasic[] | undefined - >(undefined) - if (suggestions) { - suggestionsRef.current = suggestions - } + const {data: suggestions, isFetching} = useActorAutocompleteQuery( + prefix, + true, + ) if (!isActive) return null @@ -46,26 +39,8 @@ export function Autocomplete({ t.atoms.border_contrast_high, {marginLeft: -62}, ]}> - {suggestionsRef.current?.length ? ( - suggestionsRef.current.slice(0, 5).map((item, index, arr) => { - // Eventually use an average length - const MAX_CHARS = 40 - const MAX_HANDLE_CHARS = 20 - - // Using this approach because styling is not respecting - // bounding box wrapping (before converting to ellipsis) - const {name: displayHandle, remainingCharacters} = getGraphemeString( - item.handle, - MAX_HANDLE_CHARS, - ) - - const {name: displayName} = getGraphemeString( - item.displayName || item.handle, - MAX_CHARS - - MAX_HANDLE_CHARS + - (remainingCharacters > 0 ? remainingCharacters : 0), - ) - + {suggestions?.length ? ( + suggestions.slice(0, 5).map((item, index, arr) => { return ( + {sanitizeDisplayName( + item.displayName || sanitizeHandle(item.handle), + )} + + - {sanitizeDisplayName(displayName)} + {sanitizeHandle(item.handle, '@')} - - {sanitizeHandle(displayHandle, '@')} - ) diff --git a/src/view/com/composer/text-input/web/Autocomplete.tsx b/src/view/com/composer/text-input/web/Autocomplete.tsx index 0599ddb395..f40c2ee8d9 100644 --- a/src/view/com/composer/text-input/web/Autocomplete.tsx +++ b/src/view/com/composer/text-input/web/Autocomplete.tsx @@ -10,6 +10,8 @@ import { import tippy, {Instance as TippyInstance} from 'tippy.js' import {usePalette} from '#/lib/hooks/usePalette' +import {sanitizeDisplayName} from '#/lib/strings/display-names' +import {sanitizeHandle} from '#/lib/strings/handles' import {ActorAutocompleteFn} from '#/state/queries/actor-autocomplete' import {Text} from '#/view/com/util/text/Text' import {UserAvatar} from '#/view/com/util/UserAvatar' @@ -148,7 +150,9 @@ const MentionList = forwardRef( {items.length > 0 ? ( items.map((item, index) => { const {name: displayName} = getGraphemeString( - item.displayName ?? item.handle, + sanitizeDisplayName( + item.displayName || sanitizeHandle(item.handle), + ), 30, // Heuristic value; can be modified ) const isSelected = selectedIndex === index @@ -181,7 +185,7 @@ const MentionList = forwardRef( - @{item.handle} + {sanitizeHandle(item.handle, '@')} ) From 84724bb940a827a7020da6ccbd6fa80f7209a455 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 21 Nov 2024 22:32:49 +0000 Subject: [PATCH 09/32] [Android] Patch react-native-svg to cache parsed paths (#6583) --- patches/react-native-svg+15.3.0.patch | 57 +++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 patches/react-native-svg+15.3.0.patch diff --git a/patches/react-native-svg+15.3.0.patch b/patches/react-native-svg+15.3.0.patch new file mode 100644 index 0000000000..54540023f1 --- /dev/null +++ b/patches/react-native-svg+15.3.0.patch @@ -0,0 +1,57 @@ +diff --git a/node_modules/react-native-svg/android/src/main/java/com/horcrux/svg/PathView.java b/node_modules/react-native-svg/android/src/main/java/com/horcrux/svg/PathView.java +index 06829bd..1b15818 100644 +--- a/node_modules/react-native-svg/android/src/main/java/com/horcrux/svg/PathView.java ++++ b/node_modules/react-native-svg/android/src/main/java/com/horcrux/svg/PathView.java +@@ -14,17 +14,33 @@ import android.graphics.Paint; + import android.graphics.Path; + import com.facebook.react.bridge.ReactContext; + ++import java.util.ArrayList; ++import java.util.HashMap; ++ ++class ParsedPath { ++ final Path path; ++ final ArrayList elements; ++ ++ ParsedPath(Path path, ArrayList elements) { ++ this.path = path; ++ this.elements = elements; ++ } ++} ++ + @SuppressLint("ViewConstructor") + class PathView extends RenderableView { + private Path mPath; + ++ // This grows forever but for our use case (static icons) it's ok. ++ private static final HashMap sPathCache = new HashMap<>(); ++ + public PathView(ReactContext reactContext) { + super(reactContext); + PathParser.mScale = mScale; + mPath = new Path(); + } + +- public void setD(String d) { ++ void setDByParsing(String d) { + mPath = PathParser.parse(d); + elements = PathParser.elements; + for (PathElement elem : elements) { +@@ -33,6 +49,17 @@ class PathView extends RenderableView { + point.y *= mScale; + } + } ++ } ++ ++ public void setD(String d) { ++ ParsedPath cached = sPathCache.get(d); ++ if (cached != null) { ++ mPath = cached.path; ++ elements = cached.elements; ++ } else { ++ setDByParsing(d); ++ sPathCache.put(d, new ParsedPath(mPath, elements)); ++ } + invalidate(); + } + From dc3a42edb1bcdb8c7f07111e936dddcfac1c1bb1 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 21 Nov 2024 22:52:16 +0000 Subject: [PATCH 10/32] Reduce nesting (#6615) * Move isOnlyEmoji out of RichText To fix Fast Refresh. * Make renderChildrenWithEmoji work with any children * Always go through UITextView for consistency It already contains the `selectable` and `iOS` checks inside. * Move `emoji` check into `renderChildrenWithEmoji` * Remove unnecessary intermediate UITextView nodes * Make childHasEmoji check recursive It didn't handle nested arrays etc correctly before. * Remove the "children must be string" limitation Should not be necessary now that we correctly handle nested arrays etc. * Fix unnecessary regex reallocation This doesn't have a global flag so it's okay to reuse. * Remove unnecessary wrapper in RichText --- src/alf/typography.tsx | 91 ++++++++++++++---------------- src/components/RichText.tsx | 15 +---- src/components/Typography.tsx | 8 +-- src/components/dms/MessageItem.tsx | 3 +- src/view/com/util/text/Text.tsx | 23 ++------ 5 files changed, 51 insertions(+), 89 deletions(-) diff --git a/src/alf/typography.tsx b/src/alf/typography.tsx index d00910bf87..1b1e81bd4c 100644 --- a/src/alf/typography.tsx +++ b/src/alf/typography.tsx @@ -1,10 +1,11 @@ -import React from 'react' +import React, {Children} from 'react' import {TextProps as RNTextProps} from 'react-native' import {StyleProp, TextStyle} from 'react-native' import {UITextView} from 'react-native-uitextview' import createEmojiRegex from 'emoji-regex' import {isNative} from '#/platform/detection' +import {isIOS} from '#/platform/detection' import {Alf, applyFonts, atoms, flatten} from '#/alf' /** @@ -57,7 +58,7 @@ export function normalizeTextStyles( } export type StringChild = string | (string | null)[] -export type TextProps = Omit & { +export type TextProps = RNTextProps & { /** * Lets the user select text, to use the native copy and paste functionality. */ @@ -71,65 +72,55 @@ export type TextProps = Omit & { * Appears as a small tooltip on web hover. */ title?: string -} & ( - | { - emoji?: true - children: StringChild - } - | { - emoji?: false - children: RNTextProps['children'] - } - ) + /** + * Whether the children could possibly contain emoji. + */ + emoji?: boolean +} const EMOJI = createEmojiRegex() export function childHasEmoji(children: React.ReactNode) { - return (Array.isArray(children) ? children : [children]).some( - child => typeof child === 'string' && createEmojiRegex().test(child), - ) -} - -export function childIsString( - children: React.ReactNode, -): children is StringChild { - return ( - typeof children === 'string' || - (Array.isArray(children) && - children.every(child => typeof child === 'string' || child === null)) - ) + let hasEmoji = false + Children.forEach(children, child => { + if (typeof child === 'string' && createEmojiRegex().test(child)) { + hasEmoji = true + } + }) + return hasEmoji } export function renderChildrenWithEmoji( - children: StringChild, + children: React.ReactNode, props: Omit = {}, + emoji: boolean, ) { - const normalized = Array.isArray(children) ? children : [children] + if (!isIOS || !emoji) { + return children + } + return Children.map(children, child => { + if (typeof child !== 'string') return child - return ( - - {normalized.map(child => { - if (typeof child !== 'string') return child + const emojis = child.match(EMOJI) - const emojis = child.match(EMOJI) + if (emojis === null) { + return child + } - if (emojis === null) { - return child - } + return child.split(EMOJI).map((stringPart, index) => [ + stringPart, + emojis[index] ? ( + + {emojis[index]} + + ) : null, + ]) + }) +} - return child.split(EMOJI).map((stringPart, index) => ( - - {stringPart} - {emojis[index] ? ( - - {emojis[index]} - - ) : null} - - )) - })} - - ) +const SINGLE_EMOJI_RE = /^[\p{Emoji_Presentation}\p{Extended_Pictographic}]+$/u +export function isOnlyEmoji(text: string) { + return text.length <= 15 && SINGLE_EMOJI_RE.test(text) } diff --git a/src/components/RichText.tsx b/src/components/RichText.tsx index e2d05ac6cb..6d7e50e480 100644 --- a/src/components/RichText.tsx +++ b/src/components/RichText.tsx @@ -9,6 +9,7 @@ import {NavigationProp} from '#/lib/routes/types' import {toShortUrl} from '#/lib/strings/url-helpers' import {isNative} from '#/platform/detection' import {atoms as a, flatten, native, TextStyleProp, useTheme, web} from '#/alf' +import {isOnlyEmoji} from '#/alf/typography' import {useInteractionState} from '#/components/hooks/useInteractionState' import {InlineLinkText, LinkProps} from '#/components/Link' import {ProfileHoverCard} from '#/components/ProfileHoverCard' @@ -150,17 +151,14 @@ export function RichText({ />, ) } else { - els.push( - - {segment.text} - , - ) + els.push(segment.text) } key++ } return ( ) } - -export function isOnlyEmoji(text: string) { - return ( - text.length <= 15 && - /^[\p{Emoji_Presentation}\p{Extended_Pictographic}]+$/u.test(text) - ) -} diff --git a/src/components/Typography.tsx b/src/components/Typography.tsx index 4bcbf9f091..3e202cb8f2 100644 --- a/src/components/Typography.tsx +++ b/src/components/Typography.tsx @@ -1,11 +1,9 @@ import {UITextView} from 'react-native-uitextview' import {logger} from '#/logger' -import {isIOS} from '#/platform/detection' import {atoms, flatten, useAlf, useTheme, web} from '#/alf' import { childHasEmoji, - childIsString, normalizeTextStyles, renderChildrenWithEmoji, TextProps, @@ -39,10 +37,6 @@ export function Text({ `Text: emoji detected but emoji not enabled: "${children}"\n\nPlease add '`, ) } - - if (emoji && !childIsString(children)) { - logger.error('Text: when , children can only be strings.') - } } const shared = { @@ -55,7 +49,7 @@ export function Text({ return ( - {isIOS && emoji ? renderChildrenWithEmoji(children, shared) : children} + {renderChildrenWithEmoji(children, shared, emoji ?? false)} ) } diff --git a/src/components/dms/MessageItem.tsx b/src/components/dms/MessageItem.tsx index 52220e2cac..79f0997fd6 100644 --- a/src/components/dms/MessageItem.tsx +++ b/src/components/dms/MessageItem.tsx @@ -19,10 +19,11 @@ import {ConvoItem} from '#/state/messages/convo/types' import {useSession} from '#/state/session' import {TimeElapsed} from '#/view/com/util/TimeElapsed' import {atoms as a, useTheme} from '#/alf' +import {isOnlyEmoji} from '#/alf/typography' import {ActionsWrapper} from '#/components/dms/ActionsWrapper' import {InlineLinkText} from '#/components/Link' import {Text} from '#/components/Typography' -import {isOnlyEmoji, RichText} from '../RichText' +import {RichText} from '../RichText' import {DateDivider} from './DateDivider' import {MessageItemEmbed} from './MessageItemEmbed' import {localDateString} from './util' diff --git a/src/view/com/util/text/Text.tsx b/src/view/com/util/text/Text.tsx index a5278e0a07..f05274f44e 100644 --- a/src/view/com/util/text/Text.tsx +++ b/src/view/com/util/text/Text.tsx @@ -1,5 +1,5 @@ import React from 'react' -import {StyleSheet, Text as RNText, TextProps} from 'react-native' +import {StyleSheet, TextProps} from 'react-native' import {UITextView} from 'react-native-uitextview' import {lh, s} from '#/lib/styles' @@ -9,7 +9,6 @@ import {isIOS, isWeb} from '#/platform/detection' import {applyFonts, useAlf} from '#/alf' import { childHasEmoji, - childIsString, renderChildrenWithEmoji, StringChild, } from '#/alf/typography' @@ -56,10 +55,6 @@ function Text_DEPRECATED({ `Text: emoji detected but emoji not enabled: "${children}"\n\nPlease add '`, ) } - - if (emoji && !childIsString(children)) { - logger.error('Text: when , children can only be strings.') - } } const textProps = React.useMemo(() => { @@ -107,19 +102,9 @@ function Text_DEPRECATED({ type, ]) - if (selectable && isIOS) { - return ( - - {isIOS && emoji - ? renderChildrenWithEmoji(children, textProps) - : children} - - ) - } - return ( - - {isIOS && emoji ? renderChildrenWithEmoji(children, textProps) : children} - + + {renderChildrenWithEmoji(children, textProps, emoji ?? false)} + ) } From 391e0c4c1881e1bf416321725bad7662e9883868 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 21 Nov 2024 22:52:28 +0000 Subject: [PATCH 11/32] Fix embeder (#6616) --- bskyembed/src/components/embed.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bskyembed/src/components/embed.tsx b/bskyembed/src/components/embed.tsx index e96acb89e1..74eacf16d4 100644 --- a/bskyembed/src/components/embed.tsx +++ b/bskyembed/src/components/embed.tsx @@ -436,14 +436,14 @@ function StarterPackEmbed({ // from #/lib/strings/starter-pack.ts function getStarterPackImage(starterPack: AppBskyGraphDefs.StarterPackView) { - const rkey = getRkey(starterPack.uri) + const rkey = getRkey({uri: starterPack.uri}) return `https://ogcard.cdn.bsky.app/start/${starterPack.creator.did}/${rkey}` } function getStarterPackHref( starterPack: AppBskyGraphDefs.StarterPackViewBasic, ) { - const rkey = getRkey(starterPack.uri) + const rkey = getRkey({uri: starterPack.uri}) const handleOrDid = starterPack.creator.handle || starterPack.creator.did return `/starter-pack/${handleOrDid}/${rkey}` } From 625459add6c14c9ae6c1158fbe5c67a4e0838fc6 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 21 Nov 2024 23:40:37 +0000 Subject: [PATCH 12/32] Fix some overdraw (#6617) --- src/view/com/home/HomeHeaderLayoutMobile.tsx | 5 ++--- src/view/shell/index.tsx | 9 +++------ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/view/com/home/HomeHeaderLayoutMobile.tsx b/src/view/com/home/HomeHeaderLayoutMobile.tsx index 98253ad748..8323960924 100644 --- a/src/view/com/home/HomeHeaderLayoutMobile.tsx +++ b/src/view/com/home/HomeHeaderLayoutMobile.tsx @@ -41,12 +41,12 @@ export function HomeHeaderLayoutMobile({ return ( { headerHeight.set(e.nativeEvent.layout.height) }}> - + {IS_DEV && ( diff --git a/src/view/shell/index.tsx b/src/view/shell/index.tsx index 1b5abe4788..b4043bb0e3 100644 --- a/src/view/shell/index.tsx +++ b/src/view/shell/index.tsx @@ -1,7 +1,6 @@ import React from 'react' import {BackHandler, StyleSheet, useWindowDimensions, View} from 'react-native' import {Drawer} from 'react-native-drawer-layout' -import Animated from 'react-native-reanimated' import {useSafeAreaInsets} from 'react-native-safe-area-context' import * as NavigationBar from 'expo-navigation-bar' import {StatusBar} from 'expo-status-bar' @@ -10,7 +9,6 @@ import {useNavigation, useNavigationState} from '@react-navigation/native' import {useDedupe} from '#/lib/hooks/useDedupe' import {useIntentHandler} from '#/lib/hooks/useIntentHandler' import {useNotificationsHandler} from '#/lib/hooks/useNotificationHandler' -import {usePalette} from '#/lib/hooks/usePalette' import {useNotificationsRegistration} from '#/lib/notifications/notifications' import {isStateAtTabRoot} from '#/lib/routes/helpers' import {useTheme} from '#/lib/ThemeContext' @@ -95,7 +93,7 @@ function ShellInner() { return ( <> - + - + @@ -133,7 +131,6 @@ function ShellInner() { export const Shell: React.FC = function ShellImpl() { const {fullyExpandedCount} = useDialogStateControlContext() - const pal = usePalette('default') const theme = useTheme() useIntentHandler() @@ -147,7 +144,7 @@ export const Shell: React.FC = function ShellImpl() { } }, [theme]) return ( - + 0) From c858700c173340c912e0aa4b8d0f921218a8df6e Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 22 Nov 2024 04:35:53 +0000 Subject: [PATCH 13/32] [Android] Lower Feed maxRenderPerBatch (#6624) --- src/view/com/posts/Feed.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx index 07c1fd6b7a..a962fa5a42 100644 --- a/src/view/com/posts/Feed.tsx +++ b/src/view/com/posts/Feed.tsx @@ -19,7 +19,7 @@ import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' import {logEvent, useGate} from '#/lib/statsig/statsig' import {useTheme} from '#/lib/ThemeContext' import {logger} from '#/logger' -import {isWeb} from '#/platform/detection' +import {isIOS, isWeb} from '#/platform/detection' import {listenPostCreated} from '#/state/events' import {useFeedFeedbackContext} from '#/state/feed-feedback' import {STALE} from '#/state/queries' @@ -636,7 +636,7 @@ let Feed = ({ } initialNumToRender={initialNumToRenderOverride ?? initialNumToRender} windowSize={9} - maxToRenderPerBatch={5} + maxToRenderPerBatch={isIOS ? 5 : 1} updateCellsBatchingPeriod={40} onItemSeen={feedFeedback.onItemSeen} /> From 09ce65b24e968cfc0c45d8239dfbc995afcc78a8 Mon Sep 17 00:00:00 2001 From: Frudrax Cheng Date: Fri, 22 Nov 2024 19:32:25 +0800 Subject: [PATCH 14/32] Fix display language not switching correctly to Chinese on native. (#6621) * Update deviceLocales.ts * Update deviceLocales.ts --- src/locale/deviceLocales.ts | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/locale/deviceLocales.ts b/src/locale/deviceLocales.ts index 9e19e372b8..1abaa20f69 100644 --- a/src/locale/deviceLocales.ts +++ b/src/locale/deviceLocales.ts @@ -13,6 +13,12 @@ type LocalWithLanguageCode = Locale & { * * {@link https://github.com/bluesky-social/social-app/pull/4461} * {@link https://xml.coverpages.org/iso639a.html} + * + * Convert Chinese language tags for Native. + * + * {@link https://datatracker.ietf.org/doc/html/rfc5646#appendix-A} + * {@link https://developer.apple.com/documentation/packagedescription/languagetag} + * {@link https://gist.github.com/amake/0ac7724681ac1c178c6f95a5b09f03ce#new-locales-vs-old-locales-chinese} */ export function getLocales() { const locales = defaultGetLocales?.() ?? [] @@ -32,10 +38,25 @@ export function getLocales() { // yiddish locale.languageCode = 'yi' } + } - // @ts-ignore checked above - output.push(locale) + if (typeof locale.languageTag === 'string') { + if (locale.languageTag.startsWith('zh-Hans')) { + // Simplified Chinese to zh-CN + locale.languageTag = 'zh-CN' + } + if (locale.languageTag.startsWith('zh-Hant')) { + // Traditional Chinese to zh-TW + locale.languageTag = 'zh-TW' + } + if (locale.languageTag.startsWith('yue')) { + // Cantonese (Yue) to zh-HK + locale.languageTag = 'zh-HK' + } } + + // @ts-ignore checked above + output.push(locale) } return output From 058a29c6e3e4e00c9ee71d56382435b95cb3d3be Mon Sep 17 00:00:00 2001 From: surfdude29 <149612116+surfdude29@users.noreply.github.com> Date: Fri, 22 Nov 2024 11:34:37 +0000 Subject: [PATCH 15/32] Add `CFBundleLocalizations` key to `Info.plist` (#6567) --- app.config.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/app.config.js b/app.config.js index 56142c5f19..149b949e6f 100644 --- a/app.config.js +++ b/app.config.js @@ -93,6 +93,30 @@ module.exports = function (config) { NSPhotoLibraryUsageDescription: 'Used for profile pictures, posts, and other kinds of content', CFBundleSpokenName: 'Blue Sky', + CFBundleLocalizations: [ + 'en', + 'ca', + 'de', + 'es', + 'fi', + 'fr', + 'ga', + 'hi', + 'hu', + 'id', + 'it', + 'ja', + 'ko', + 'pl', + 'pt', + 'ru', + 'th', + 'tr', + 'uk', + 'zh_CN', + 'zh_HK', + 'zh_TW', + ], }, associatedDomains: ASSOCIATED_DOMAINS, splash: { From 5028753367facb269ac226477091b77fad966f17 Mon Sep 17 00:00:00 2001 From: Paul Coroneos Date: Fri, 22 Nov 2024 05:59:47 -0600 Subject: [PATCH 16/32] Fix lightbox spinner (#6561) * refactor imageitem useanimatedreaction logic * revert copy/paste fix * revert last commit * Fix conditions --------- Co-authored-by: Dan Abramov --- .../ImageViewing/components/ImageItem/ImageItem.android.tsx | 6 +++--- .../ImageViewing/components/ImageItem/ImageItem.ios.tsx | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx index 7aca8721b9..8e046e5ba9 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx @@ -363,10 +363,10 @@ const ImageItem = ({ return transforms.get().isResting && !hasLoaded }, (show, prevShow) => { - if (show && !prevShow) { - runOnJS(setShowLoader)(false) - } else if (!prevShow && show) { + if (!prevShow && show) { runOnJS(setShowLoader)(true) + } else if (prevShow && !show) { + runOnJS(setShowLoader)(false) } }, ) diff --git a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx index c7be4f3e36..d42fb70c18 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx @@ -187,10 +187,10 @@ const ImageItem = ({ return transforms.get().isResting && !hasLoaded }, (show, prevShow) => { - if (show && !prevShow) { - runOnJS(setShowLoader)(false) - } else if (!prevShow && show) { + if (!prevShow && show) { runOnJS(setShowLoader)(true) + } else if (prevShow && !show) { + runOnJS(setShowLoader)(false) } }, ) From a0acd514abd83c444575616cc3b920a846c2fd08 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 22 Nov 2024 13:34:16 +0000 Subject: [PATCH 17/32] [Lightbox] Fix jump when zooming out on iOS (#6633) --- .../components/ImageItem/ImageItem.ios.tsx | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx index d42fb70c18..c103e131b8 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx @@ -16,9 +16,11 @@ import { import Animated, { runOnJS, SharedValue, + useAnimatedProps, useAnimatedReaction, useAnimatedRef, useAnimatedStyle, + useSharedValue, } from 'react-native-reanimated' import {useSafeAreaFrame} from 'react-native-safe-area-context' import {Image} from 'expo-image' @@ -75,6 +77,7 @@ const ImageItem = ({ }: Props) => { const scrollViewRef = useAnimatedRef() const [scaled, setScaled] = useState(false) + const isDragging = useSharedValue(false) const screenSizeDelayedForJSThreadOnly = useSafeAreaFrame() const maxZoomScale = Math.max( MIN_SCREEN_ZOOM, @@ -86,11 +89,20 @@ const ImageItem = ({ const scrollHandler = useAnimatedScrollHandler({ onScroll(e) { + 'worklet' const nextIsScaled = e.zoomScale > 1 if (scaled !== nextIsScaled) { runOnJS(handleZoom)(nextIsScaled) } }, + onBeginDrag() { + 'worklet' + isDragging.value = true + }, + onEndDrag() { + 'worklet' + isDragging.value = false + }, }) function handleZoom(nextIsScaled: boolean) { @@ -199,6 +211,11 @@ const ImageItem = ({ const borderRadius = type === 'circle-avi' ? 1e5 : type === 'rect-avi' ? 20 : 0 + const scrollViewProps = useAnimatedProps(() => ({ + // Don't allow bounce at 1:1 rest so it can be swiped away. + bounces: scaled || isDragging.value, + })) + return ( {showLoader && ( From a5dbb35237d014a07c0f3f82e9dffce67129e542 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Fri, 22 Nov 2024 13:41:24 +0000 Subject: [PATCH 18/32] Use outline style for repost cancel button (#6509) * use outline style for repost cancel button * use trans macro in JSX --- src/view/com/util/post-ctrls/RepostButton.tsx | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/view/com/util/post-ctrls/RepostButton.tsx b/src/view/com/util/post-ctrls/RepostButton.tsx index eb7505d2ed..06b1fcaf6b 100644 --- a/src/view/com/util/post-ctrls/RepostButton.tsx +++ b/src/view/com/util/post-ctrls/RepostButton.tsx @@ -1,6 +1,6 @@ import React, {memo, useCallback} from 'react' import {View} from 'react-native' -import {msg, plural} from '@lingui/macro' +import {msg, plural, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {POST_CTRL_HITSLOP} from '#/lib/constants' @@ -151,9 +151,11 @@ let RepostButtonDialogInner = ({ color="primary"> - {isReposted - ? _(msg`Remove repost`) - : _(msg({message: `Repost`, context: 'action'}))} + {isReposted ? ( + Remove repost + ) : ( + Repost + )} @@ -193,9 +197,11 @@ let RepostButtonDialogInner = ({ label={_(msg`Cancel quote post`)} onPress={onPressClose} size="large" - variant="solid" + variant="outline" color="primary"> - {_(msg`Cancel`)} + + Cancel + From 72326844cd9677085e406c5a4ff8b42897b58ea0 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Fri, 22 Nov 2024 08:45:59 -0600 Subject: [PATCH 19/32] Improve error name and message for signed-in only feeds (#6577) * Improve error name and message for logged-in only feeds * Revert msg edit --------- Co-authored-by: Dan Abramov --- src/state/queries/post-feed.ts | 2 +- src/view/com/posts/FeedErrorMessage.tsx | 10 ++++---- yarn.lock | 31 +++---------------------- 3 files changed, 9 insertions(+), 34 deletions(-) diff --git a/src/state/queries/post-feed.ts b/src/state/queries/post-feed.ts index b2e9dcd4c9..016d8893b0 100644 --- a/src/state/queries/post-feed.ts +++ b/src/state/queries/post-feed.ts @@ -602,7 +602,7 @@ function assertSomePostsPassModeration(feed: AppBskyFeedDefs.FeedViewPost[]) { } if (!somePostsPassModeration) { - throw new Error(KnownError.FeedNSFPublic) + throw new Error(KnownError.FeedSignedInOnly) } } diff --git a/src/view/com/posts/FeedErrorMessage.tsx b/src/view/com/posts/FeedErrorMessage.tsx index cc7b34750a..a582162331 100644 --- a/src/view/com/posts/FeedErrorMessage.tsx +++ b/src/view/com/posts/FeedErrorMessage.tsx @@ -25,7 +25,7 @@ export enum KnownError { FeedgenBadResponse = 'FeedgenBadResponse', FeedgenOffline = 'FeedgenOffline', FeedgenUnknown = 'FeedgenUnknown', - FeedNSFPublic = 'FeedNSFPublic', + FeedSignedInOnly = 'FeedSignedInOnly', FeedTooManyRequests = 'FeedTooManyRequests', Unknown = 'Unknown', } @@ -110,7 +110,7 @@ function FeedgenErrorMessage({ [KnownError.FeedgenOffline]: _l( msgLingui`Hmm, the feed server appears to be offline. Please let the feed owner know about this issue.`, ), - [KnownError.FeedNSFPublic]: _l( + [KnownError.FeedSignedInOnly]: _l( msgLingui`This content is not viewable without a Bluesky account.`, ), [KnownError.FeedgenUnknown]: _l( @@ -152,7 +152,7 @@ function FeedgenErrorMessage({ const cta = React.useMemo(() => { switch (knownError) { - case KnownError.FeedNSFPublic: { + case KnownError.FeedSignedInOnly: { return null } case KnownError.FeedgenDoesNotExist: @@ -249,8 +249,8 @@ function detectKnownError( if (typeof error !== 'string') { error = error.toString() } - if (error.includes(KnownError.FeedNSFPublic)) { - return KnownError.FeedNSFPublic + if (error.includes(KnownError.FeedSignedInOnly)) { + return KnownError.FeedSignedInOnly } if (!feedDesc.startsWith('feedgen')) { return KnownError.Unknown diff --git a/yarn.lock b/yarn.lock index bda793497b..0df8155e19 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17458,16 +17458,7 @@ string-natural-compare@^3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -17567,7 +17558,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -17581,13 +17572,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -18928,7 +18912,7 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -18946,15 +18930,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From d9ba324d7f3d173cd84ebbc31b727141032e49f9 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Fri, 22 Nov 2024 14:56:13 +0000 Subject: [PATCH 20/32] Use android native animations (#6562) --- src/Navigation.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 1806df92eb..cc815ef70b 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -32,7 +32,7 @@ import { import {RouteParams, State} from '#/lib/routes/types' import {attachRouteToLogEvents, logEvent} from '#/lib/statsig/statsig' import {bskyTitle} from '#/lib/strings/headings' -import {isAndroid, isNative, isWeb} from '#/platform/detection' +import {isNative, isWeb} from '#/platform/detection' import {useModalControls} from '#/state/modals' import {useUnreadNotifications} from '#/state/queries/notifications/unread' import {useSession} from '#/state/session' @@ -453,7 +453,6 @@ function HomeTabNavigator() { return ( { Date: Fri, 22 Nov 2024 09:22:48 -0600 Subject: [PATCH 21/32] Remove unnecessary lint rule suppression (#6544) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ▫️remove deprecated react native eslint package. add react hooks and modern native eslint package. fix bugs * in retrospect lets just remove the async from uselocalelanguage * finally clean it all up * revert yarn.lock * restore logic --- .eslintrc.js | 2 -- src/locale/i18n.ts | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 2786b35364..8f8383bcc7 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -17,8 +17,6 @@ module.exports = { 'eslint-plugin-react-compiler', ], rules: { - // Temporary until https://github.com/facebook/react-native/pull/43756 gets into a release. - 'prettier/prettier': 0, 'react/no-unescaped-entities': 0, 'react/prop-types': 0, 'react-native/no-inline-styles': 0, diff --git a/src/locale/i18n.ts b/src/locale/i18n.ts index 7a841f383d..be9b939503 100644 --- a/src/locale/i18n.ts +++ b/src/locale/i18n.ts @@ -224,7 +224,7 @@ export async function dynamicActivate(locale: AppLanguage) { } } -export async function useLocaleLanguage() { +export function useLocaleLanguage() { const {appLanguage} = useLanguagePrefs() useEffect(() => { dynamicActivate(sanitizeAppLanguageSetting(appLanguage)) From 5d4aaa5b5faa595d1967eabe11fda9615d7ba1f6 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Fri, 22 Nov 2024 15:23:24 +0000 Subject: [PATCH 22/32] [a11y] Video - fix labels and make more detailed (#6635) * fix labels and make more detailed * move overall label to parent --- .../util/post-embeds/VideoEmbedInner/TimeIndicator.tsx | 5 +++++ .../post-embeds/VideoEmbedInner/VideoEmbedInnerWeb.tsx | 8 +++++++- .../VideoEmbedInner/web-controls/ControlButton.tsx | 7 ++++--- .../post-embeds/VideoEmbedInner/web-controls/Scrubber.tsx | 4 +++- .../VideoEmbedInner/web-controls/VideoControls.tsx | 5 +++-- .../VideoEmbedInner/web-controls/VolumeControl.tsx | 1 + 6 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx b/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx index e9615fe63c..75e544aca9 100644 --- a/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx +++ b/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx @@ -1,5 +1,7 @@ import {StyleProp, ViewStyle} from 'react-native' import {View} from 'react-native' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {atoms as a, useTheme} from '#/alf' import {Text} from '#/components/Typography' @@ -16,6 +18,7 @@ export function TimeIndicator({ style?: StyleProp }) { const t = useTheme() + const {_} = useLingui() if (isNaN(time)) { return null @@ -27,6 +30,8 @@ export function TimeIndicator({ return ( (null) @@ -49,7 +52,10 @@ export function VideoEmbedInnerWeb({ }, [lastKnownTime]) return ( - +