Bluesky app fork with some witchin' additions 💫 witchsky.app
bluesky fork client
117
fork

Configure Feed

Select the types of activity you want to include in your feed.

Create codemod for addressing ESLint warnings (#10032)

authored by

DS Boyce and committed by
GitHub
1db01a09 80429ec9

+1559 -1346
+135
.jscodeshift/react-import.js
··· 1 + /** 2 + * Codemod to replace namespaced React calls with named imports 3 + * 4 + * Before: 5 + * import React from 'react' 6 + * React.useEffect(() => {}, []) 7 + * 8 + * After: 9 + * import { useEffect } from 'react' 10 + * useEffect(() => {}, []) 11 + * 12 + * Usage: jscodeshift -t .jscodeshift/react-import.js <file-path> 13 + * Example: jscodeshift -t .jscodeshift/react-import.js src/App.native.tsx 14 + */ 15 + 16 + /* eslint-disable */ 17 + 18 + export const parser = 'tsx' 19 + 20 + export default function transformer(file, api) { 21 + const j = api.jscodeshift 22 + const root = j(file.source) 23 + 24 + // Find the React import 25 + let reactImportPath = null 26 + const reactMembers = new Set() 27 + 28 + root.find(j.ImportDeclaration).forEach(path => { 29 + const node = path.value 30 + if (node.source.value === 'react') { 31 + node.specifiers.forEach(spec => { 32 + // Check if this is a default import of React 33 + if ( 34 + spec.type === 'ImportDefaultSpecifier' && 35 + spec.local.name === 'React' 36 + ) { 37 + reactImportPath = path 38 + } 39 + }) 40 + } 41 + }) 42 + 43 + if (!reactImportPath) { 44 + // No React import found, nothing to do 45 + return file.source 46 + } 47 + 48 + // Find all React.* member expressions 49 + root 50 + .find(j.MemberExpression) 51 + .filter(path => { 52 + const node = path.value 53 + return ( 54 + node.object.type === 'Identifier' && 55 + node.object.name === 'React' && 56 + node.property.type === 'Identifier' 57 + ) 58 + }) 59 + .forEach(path => { 60 + const propertyName = path.value.property.name 61 + reactMembers.add(propertyName) 62 + }) 63 + 64 + // Find all React.* JSX member expressions (e.g., <React.Fragment>) 65 + root 66 + .find(j.JSXMemberExpression) 67 + .filter(path => { 68 + const node = path.value 69 + return node.object.name === 'React' && node.property.name 70 + }) 71 + .forEach(path => { 72 + const propertyName = path.value.property.name 73 + reactMembers.add(propertyName) 74 + }) 75 + 76 + // If no React members are used, remove the import 77 + if (reactMembers.size === 0) { 78 + reactImportPath.prune() 79 + return root.toSource() 80 + } 81 + 82 + // Sort the members for consistent output 83 + const sortedMembers = Array.from(reactMembers).sort() 84 + 85 + // Create new import specifiers 86 + const newSpecifiers = sortedMembers.map(name => 87 + j.importSpecifier(j.identifier(name), j.identifier(name)), 88 + ) 89 + 90 + // Get the existing import specifiers 91 + const sortedImports = Array.from(reactImportPath.value.specifiers).sort() 92 + const existingSpecifiers = sortedImports.filter( 93 + specifier => specifier.type !== 'ImportDefaultSpecifier', 94 + ) 95 + 96 + const allSpecifiers = [ 97 + ...new Map( 98 + [...existingSpecifiers, ...newSpecifiers].map(item => [ 99 + item.imported.name, 100 + item, 101 + ]), 102 + ).values(), 103 + ] 104 + 105 + // Update the import declaration 106 + reactImportPath.value.specifiers = allSpecifiers 107 + 108 + // Replace all React.* member expressions with just the identifier 109 + root 110 + .find(j.MemberExpression) 111 + .filter(path => { 112 + const node = path.value 113 + return ( 114 + node.object.type === 'Identifier' && 115 + node.object.name === 'React' && 116 + node.property.type === 'Identifier' 117 + ) 118 + }) 119 + .replaceWith(path => { 120 + return j.identifier(path.value.property.name) 121 + }) 122 + 123 + // Replace all React.* JSX member expressions with just the identifier 124 + root 125 + .find(j.JSXMemberExpression) 126 + .filter(path => { 127 + const node = path.value 128 + return node.object.name === 'React' && node.property.name 129 + }) 130 + .replaceWith(path => { 131 + return j.jsxIdentifier(path.value.property.name) 132 + }) 133 + 134 + return root.toSource() 135 + }
+1
eslint.config.mjs
··· 37 37 '*.e2e.ts', 38 38 '*.e2e.tsx', 39 39 'eslint.config.mjs', 40 + '.jscodeshift/**', 40 41 ], 41 42 }, 42 43
+5 -5
src/App.native.tsx
··· 1 1 import '#/logger/sentry/setup' 2 2 import '#/view/icons' 3 3 4 - import React, {useEffect, useState} from 'react' 4 + import {Fragment, useEffect, useState} from 'react' 5 5 import {GestureHandlerRootView} from 'react-native-gesture-handler' 6 6 import {KeyboardProvider as KeyboardControllerProvider} from 'react-native-keyboard-controller' 7 7 import { ··· 111 111 prefetchAppConfig() 112 112 113 113 function InnerApp() { 114 - const [isReady, setIsReady] = React.useState(false) 114 + const [isReady, setIsReady] = useState(false) 115 115 const {currentAccount} = useSession() 116 116 const {resumeSession} = useSessionApi() 117 117 const theme = useColorModeTheme() ··· 152 152 <ContextMenuProvider> 153 153 <Splash isReady={isReady && hasCheckedReferrer}> 154 154 <VideoVolumeProvider> 155 - <React.Fragment 155 + <Fragment 156 156 // Resets the entire tree below when it changes: 157 157 key={currentAccount?.did}> 158 158 <AnalyticsFeaturesContext> ··· 208 208 </PolicyUpdateOverlayProvider> 209 209 </QueryProvider> 210 210 </AnalyticsFeaturesContext> 211 - </React.Fragment> 211 + </Fragment> 212 212 </VideoVolumeProvider> 213 213 </Splash> 214 214 </ContextMenuProvider> ··· 220 220 function App() { 221 221 const [isReady, setReady] = useState(false) 222 222 223 - React.useEffect(() => { 223 + useEffect(() => { 224 224 Promise.all([initPersistedState(), Geo.resolve(), setupDeviceId]).then(() => 225 225 setReady(true), 226 226 )
+6 -8
src/Splash.tsx
··· 1 - import React, {useCallback, useEffect} from 'react' 1 + import {forwardRef, useCallback, useEffect, useState} from 'react' 2 2 import { 3 3 AccessibilityInfo, 4 4 Image as RNImage, ··· 29 29 darkSplashImagePointer, 30 30 ).uri 31 31 32 - export const Logo = React.forwardRef(function LogoImpl(props: SvgProps, ref) { 32 + export const Logo = forwardRef(function LogoImpl(props: SvgProps, ref) { 33 33 const width = 1000 34 34 const height = width * (67 / 64) 35 35 return ( ··· 58 58 const outroLogo = useSharedValue(0) 59 59 const outroApp = useSharedValue(0) 60 60 const outroAppOpacity = useSharedValue(0) 61 - const [isAnimationComplete, setIsAnimationComplete] = React.useState(false) 62 - const [isImageLoaded, setIsImageLoaded] = React.useState(false) 63 - const [isLayoutReady, setIsLayoutReady] = React.useState(false) 64 - const [reduceMotion, setReduceMotion] = React.useState<boolean | undefined>( 65 - false, 66 - ) 61 + const [isAnimationComplete, setIsAnimationComplete] = useState(false) 62 + const [isImageLoaded, setIsImageLoaded] = useState(false) 63 + const [isLayoutReady, setIsLayoutReady] = useState(false) 64 + const [reduceMotion, setReduceMotion] = useState<boolean | undefined>(false) 67 65 const isReady = 68 66 props.isReady && 69 67 isImageLoaded &&
+11 -15
src/alf/index.tsx
··· 1 - import React from 'react' 1 + import {createContext, useCallback, useContext, useMemo, useState} from 'react' 2 2 import {type Theme, type ThemeName} from '@bsky.app/alf' 3 3 4 4 import { ··· 46 46 /* 47 47 * Context 48 48 */ 49 - export const Context = React.createContext<Alf>({ 49 + export const Context = createContext<Alf>({ 50 50 themeName: 'light', 51 51 theme: themes.light, 52 52 themes, ··· 65 65 children, 66 66 theme: themeName, 67 67 }: React.PropsWithChildren<{theme: ThemeName}>) { 68 - const [fontScale, setFontScale] = React.useState<Alf['fonts']['scale']>(() => 68 + const [fontScale, setFontScale] = useState<Alf['fonts']['scale']>(() => 69 69 getFontScale(), 70 70 ) 71 - const [fontScaleMultiplier, setFontScaleMultiplier] = React.useState(() => 71 + const [fontScaleMultiplier, setFontScaleMultiplier] = useState(() => 72 72 computeFontScaleMultiplier(fontScale), 73 73 ) 74 - const setFontScaleAndPersist = React.useCallback< 75 - Alf['fonts']['setFontScale'] 76 - >( 74 + const setFontScaleAndPersist = useCallback<Alf['fonts']['setFontScale']>( 77 75 fs => { 78 76 setFontScale(fs) 79 77 persistFontScale(fs) ··· 81 79 }, 82 80 [setFontScale], 83 81 ) 84 - const [fontFamily, setFontFamily] = React.useState<Alf['fonts']['family']>( 85 - () => getFontFamily(), 82 + const [fontFamily, setFontFamily] = useState<Alf['fonts']['family']>(() => 83 + getFontFamily(), 86 84 ) 87 - const setFontFamilyAndPersist = React.useCallback< 88 - Alf['fonts']['setFontFamily'] 89 - >( 85 + const setFontFamilyAndPersist = useCallback<Alf['fonts']['setFontFamily']>( 90 86 ff => { 91 87 setFontFamily(ff) 92 88 persistFontFamily(ff) ··· 94 90 [setFontFamily], 95 91 ) 96 92 97 - const value = React.useMemo<Alf>( 93 + const value = useMemo<Alf>( 98 94 () => ({ 99 95 themes, 100 96 themeName: themeName, ··· 122 118 } 123 119 124 120 export function useAlf() { 125 - return React.useContext(Context) 121 + return useContext(Context) 126 122 } 127 123 128 124 export function useTheme(theme?: ThemeName) { 129 125 const alf = useAlf() 130 - return React.useMemo(() => { 126 + return useMemo(() => { 131 127 return theme ? alf.themes[theme] : alf.theme 132 128 }, [theme, alf]) 133 129 }
+2 -2
src/alf/util/useColorModeTheme.ts
··· 1 - import React from 'react' 1 + import {useLayoutEffect} from 'react' 2 2 import {type ColorSchemeName, useColorScheme} from 'react-native' 3 3 import {type ThemeName} from '@bsky.app/alf' 4 4 ··· 9 9 export function useColorModeTheme(): ThemeName { 10 10 const theme = useThemeName() 11 11 12 - React.useLayoutEffect(() => { 12 + useLayoutEffect(() => { 13 13 updateDocument(theme) 14 14 }, [theme]) 15 15
+2 -2
src/alf/util/useGutters.ts
··· 1 - import React from 'react' 1 + import {useMemo} from 'react' 2 2 3 3 import {type Breakpoint, useBreakpoints} from '#/alf/breakpoints' 4 4 import * as tokens from '#/alf/tokens' ··· 52 52 bottom = top 53 53 left = right 54 54 } 55 - return React.useMemo(() => { 55 + return useMemo(() => { 56 56 return { 57 57 paddingTop: top === 0 ? 0 : gutters[top][activeBreakpoint || 'default'], 58 58 paddingRight:
+3 -3
src/components/AccountList.tsx
··· 1 - import React, {useCallback} from 'react' 1 + import {Fragment, useCallback} from 'react' 2 2 import {View} from 'react-native' 3 3 import {type AppBskyActorDefs} from '@atproto/api' 4 4 import {msg} from '@lingui/core/macro' ··· 52 52 t.atoms.border_contrast_low, 53 53 ]}> 54 54 {accounts.map(account => ( 55 - <React.Fragment key={account.did}> 55 + <Fragment key={account.did}> 56 56 <AccountItem 57 57 profile={profiles?.profiles.find(p => p.did === account.did)} 58 58 account={account} ··· 61 61 isPendingAccount={account.did === pendingDid} 62 62 /> 63 63 <View style={[a.border_b, t.atoms.border_contrast_low]} /> 64 - </React.Fragment> 64 + </Fragment> 65 65 ))} 66 66 <Button 67 67 testID="chooseAddAccountBtn"
+2 -2
src/components/AppLanguageDropdown.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {msg} from '@lingui/core/macro' 3 3 import {useLingui} from '@lingui/react' 4 4 import {useQueryClient} from '@tanstack/react-query' ··· 20 20 const setLangPrefs = useLanguagePrefsApi() 21 21 const sanitizedLang = sanitizeAppLanguageSetting(langPrefs.appLanguage) 22 22 23 - const onChangeAppLanguage = React.useCallback( 23 + const onChangeAppLanguage = useCallback( 24 24 (value: string) => { 25 25 if (!value) return 26 26 if (sanitizedLang !== value) {
+76 -70
src/components/Button.tsx
··· 1 - import React from 'react' 1 + import { 2 + createContext, 3 + forwardRef, 4 + useCallback, 5 + useContext, 6 + useMemo, 7 + useState, 8 + } from 'react' 2 9 import { 3 10 type AccessibilityProps, 4 11 type GestureResponderEvent, ··· 108 115 export type ButtonTextProps = TextProps & 109 116 VariantProps & {disabled?: boolean; emoji?: boolean} 110 117 111 - const Context = React.createContext<VariantProps & ButtonState>({ 118 + const Context = createContext<VariantProps & ButtonState>({ 112 119 hovered: false, 113 120 focused: false, 114 121 pressed: false, ··· 117 124 Context.displayName = 'ButtonContext' 118 125 119 126 export function useButtonContext() { 120 - return React.useContext(Context) 127 + return useContext(Context) 121 128 } 122 129 123 - export const Button = React.forwardRef<View, ButtonProps>( 130 + export const Button = forwardRef<View, ButtonProps>( 124 131 ( 125 132 { 126 133 children, ··· 153 160 } 154 161 155 162 const t = useTheme() 156 - const [state, setState] = React.useState({ 163 + const [state, setState] = useState({ 157 164 pressed: false, 158 165 hovered: false, 159 166 focused: false, 160 167 }) 161 168 162 - const onPressIn = React.useCallback( 169 + const onPressIn = useCallback( 163 170 (e: GestureResponderEvent) => { 164 171 setState(s => ({ 165 172 ...s, ··· 169 176 }, 170 177 [setState, onPressInOuter], 171 178 ) 172 - const onPressOut = React.useCallback( 179 + const onPressOut = useCallback( 173 180 (e: GestureResponderEvent) => { 174 181 setState(s => ({ 175 182 ...s, ··· 179 186 }, 180 187 [setState, onPressOutOuter], 181 188 ) 182 - const onHoverIn = React.useCallback( 189 + const onHoverIn = useCallback( 183 190 (e: MouseEvent) => { 184 191 setState(s => ({ 185 192 ...s, ··· 189 196 }, 190 197 [setState, onHoverInOuter], 191 198 ) 192 - const onHoverOut = React.useCallback( 199 + const onHoverOut = useCallback( 193 200 (e: MouseEvent) => { 194 201 setState(s => ({ 195 202 ...s, ··· 199 206 }, 200 207 [setState, onHoverOutOuter], 201 208 ) 202 - const onFocus = React.useCallback( 209 + const onFocus = useCallback( 203 210 (e: NativeSyntheticEvent<TargetedEvent>) => { 204 211 setState(s => ({ 205 212 ...s, ··· 209 216 }, 210 217 [setState, onFocusOuter], 211 218 ) 212 - const onBlur = React.useCallback( 219 + const onBlur = useCallback( 213 220 (e: NativeSyntheticEvent<TargetedEvent>) => { 214 221 setState(s => ({ 215 222 ...s, ··· 220 227 [setState, onBlurOuter], 221 228 ) 222 229 223 - const {baseStyles, hoverStyles} = React.useMemo(() => { 230 + const {baseStyles, hoverStyles} = useMemo(() => { 224 231 const baseStyles: ViewStyle[] = [] 225 232 const hoverStyles: ViewStyle[] = [] 226 233 ··· 526 533 } 527 534 }, [t, variant, color, size, shape, disabled]) 528 535 529 - const context = React.useMemo<ButtonContext>( 536 + const context = useMemo<ButtonContext>( 530 537 () => ({ 531 538 ...state, 532 539 variant, ··· 581 588 export function useSharedButtonTextStyles() { 582 589 const t = useTheme() 583 590 const {color, variant, disabled, size} = useButtonContext() 584 - return React.useMemo(() => { 591 + return useMemo(() => { 585 592 const baseStyles: TextStyle[] = [] 586 593 587 594 /* ··· 778 785 }) { 779 786 const {size: buttonSize, shape: buttonShape} = useButtonContext() 780 787 const textStyles = useSharedButtonTextStyles() 781 - const {iconSize, iconContainerSize, iconNegativeMargin} = 782 - React.useMemo(() => { 783 - /** 784 - * Pre-set icon sizes for different button sizes 785 - */ 786 - const iconSizeShorthand = 787 - size ?? 788 - (({ 789 - large: 'md', 790 - small: 'sm', 791 - tiny: 'xs', 792 - }[buttonSize || 'small'] || 'sm') as Exclude< 793 - SVGIconProps['size'], 794 - undefined 795 - >) 788 + const {iconSize, iconContainerSize, iconNegativeMargin} = useMemo(() => { 789 + /** 790 + * Pre-set icon sizes for different button sizes 791 + */ 792 + const iconSizeShorthand = 793 + size ?? 794 + (({ 795 + large: 'md', 796 + small: 'sm', 797 + tiny: 'xs', 798 + }[buttonSize || 'small'] || 'sm') as Exclude< 799 + SVGIconProps['size'], 800 + undefined 801 + >) 796 802 797 - /* 798 - * Copied here from icons/common.tsx so we can tweak if we need to, but 799 - * also so that we can calculate transforms. 800 - */ 801 - const iconSize = { 802 - xs: 12, 803 - sm: 16, 804 - md: 18, 805 - lg: 24, 806 - xl: 28, 807 - '2xs': 8, 808 - '2xl': 32, 809 - '3xl': 40, 810 - }[iconSizeShorthand] 803 + /* 804 + * Copied here from icons/common.tsx so we can tweak if we need to, but 805 + * also so that we can calculate transforms. 806 + */ 807 + const iconSize = { 808 + xs: 12, 809 + sm: 16, 810 + md: 18, 811 + lg: 24, 812 + xl: 28, 813 + '2xs': 8, 814 + '2xl': 32, 815 + '3xl': 40, 816 + }[iconSizeShorthand] 811 817 812 - /* 813 - * Goal here is to match rendered text size so that different size icons 814 - * don't increase button size 815 - */ 816 - const iconContainerSize = { 817 - large: 20, 818 - small: 17, 819 - tiny: 15, 820 - }[buttonSize || 'small'] 818 + /* 819 + * Goal here is to match rendered text size so that different size icons 820 + * don't increase button size 821 + */ 822 + const iconContainerSize = { 823 + large: 20, 824 + small: 17, 825 + tiny: 15, 826 + }[buttonSize || 'small'] 821 827 822 - /* 823 - * The icon needs to be closer to the edge of the button than the text. Therefore 824 - * we make the gap slightly too large, and then pull in the sides using negative margins. 825 - */ 826 - let iconNegativeMargin = 0 828 + /* 829 + * The icon needs to be closer to the edge of the button than the text. Therefore 830 + * we make the gap slightly too large, and then pull in the sides using negative margins. 831 + */ 832 + let iconNegativeMargin = 0 827 833 828 - if (buttonShape === 'default') { 829 - iconNegativeMargin = { 830 - large: -2, 831 - small: -2, 832 - tiny: -1, 833 - }[buttonSize || 'small'] 834 - } 834 + if (buttonShape === 'default') { 835 + iconNegativeMargin = { 836 + large: -2, 837 + small: -2, 838 + tiny: -1, 839 + }[buttonSize || 'small'] 840 + } 835 841 836 - return { 837 - iconSize, 838 - iconContainerSize, 839 - iconNegativeMargin, 840 - } 841 - }, [buttonSize, buttonShape, size]) 842 + return { 843 + iconSize, 844 + iconContainerSize, 845 + iconNegativeMargin, 846 + } 847 + }, [buttonSize, buttonShape, size]) 842 848 843 849 return ( 844 850 <View
+8 -5
src/components/ContextMenu/index.tsx
··· 1 - import React, { 1 + import { 2 + cloneElement, 3 + Fragment, 4 + isValidElement, 2 5 useCallback, 3 6 useEffect, 4 7 useId, ··· 689 692 t.atoms.border_contrast_low, 690 693 ]}> 691 694 {flattenReactChildren(children).map((child, i) => { 692 - return React.isValidElement(child) && 695 + return isValidElement(child) && 693 696 (child.type === Item || child.type === Divider) ? ( 694 - <React.Fragment key={i}> 697 + <Fragment key={i}> 695 698 {i > 0 ? ( 696 699 <View 697 700 style={[a.border_b, t.atoms.border_contrast_low]} 698 701 /> 699 702 ) : null} 700 - {React.cloneElement(child, { 703 + {cloneElement(child, { 701 704 // @ts-expect-error not typed 702 705 style: { 703 706 borderRadius: 0, 704 707 borderWidth: 0, 705 708 }, 706 709 })} 707 - </React.Fragment> 710 + </Fragment> 708 711 ) : null 709 712 })} 710 713 </View>
+16 -9
src/components/Dialog/index.web.tsx
··· 1 - import React, {useImperativeHandle} from 'react' 1 + import { 2 + forwardRef, 3 + useCallback, 4 + useContext, 5 + useImperativeHandle, 6 + useMemo, 7 + useState, 8 + } from 'react' 2 9 import { 3 10 FlatList, 4 11 type FlatListProps, ··· 48 55 }: React.PropsWithChildren<DialogOuterProps>) { 49 56 const {_} = useLingui() 50 57 const {gtMobile} = useBreakpoints() 51 - const [isOpen, setIsOpen] = React.useState(false) 58 + const [isOpen, setIsOpen] = useState(false) 52 59 const {setDialogIsOpen} = useDialogStateControlContext() 53 60 54 - const open = React.useCallback(() => { 61 + const open = useCallback(() => { 55 62 setDialogIsOpen(control.id, true) 56 63 setIsOpen(true) 57 64 }, [setIsOpen, setDialogIsOpen, control.id]) 58 65 59 - const close = React.useCallback<DialogControlProps['close']>( 66 + const close = useCallback<DialogControlProps['close']>( 60 67 cb => { 61 68 setDialogIsOpen(control.id, false) 62 69 setIsOpen(false) ··· 80 87 [control.id, onClose, setDialogIsOpen], 81 88 ) 82 89 83 - const handleBackgroundPress = React.useCallback( 90 + const handleBackgroundPress = useCallback( 84 91 async (e: GestureResponderEvent) => { 85 92 webOptions?.onBackgroundPress ? webOptions.onBackgroundPress(e) : close() 86 93 }, ··· 96 103 [close, open], 97 104 ) 98 105 99 - const context = React.useMemo( 106 + const context = useMemo( 100 107 () => ({ 101 108 close, 102 109 isNativeDialog: false, ··· 165 172 contentContainerStyle, 166 173 }: DialogInnerProps) { 167 174 const t = useTheme() 168 - const {close} = React.useContext(Context) 175 + const {close} = useContext(Context) 169 176 const {gtMobile} = useBreakpoints() 170 177 const {reduceMotionEnabled} = useA11y() 171 178 FocusGuards.useFocusGuards() ··· 215 222 216 223 export const ScrollableInner = Inner 217 224 218 - export const InnerFlatList = React.forwardRef< 225 + export const InnerFlatList = forwardRef< 219 226 FlatList, 220 227 FlatListProps<any> & {label: string} & { 221 228 webInnerStyle?: StyleProp<ViewStyle> ··· 284 291 285 292 export function Close() { 286 293 const {_} = useLingui() 287 - const {close} = React.useContext(Context) 294 + const {close} = useContext(Context) 288 295 return ( 289 296 <View 290 297 style={[
+2 -2
src/components/Dialog/utils.ts
··· 1 - import React from 'react' 1 + import {useEffect} from 'react' 2 2 3 3 import {type DialogControlProps} from '#/components/Dialog/types' 4 4 5 5 export function useAutoOpen(control: DialogControlProps, showTimeout?: number) { 6 - React.useEffect(() => { 6 + useEffect(() => { 7 7 if (showTimeout) { 8 8 const timeout = setTimeout(() => { 9 9 control.open()
-1
src/components/Fill.tsx
··· 1 1 import {View} from 'react-native' 2 - import type React from 'react' 3 2 4 3 import {atoms as a, type ViewStyleProp} from '#/alf' 5 4
+4 -6
src/components/KnownFollowers.tsx
··· 1 - import React from 'react' 1 + import {useRef} from 'react' 2 2 import {View} from 'react-native' 3 3 import { 4 4 type AppBskyActorDefs, ··· 46 46 minimal?: boolean 47 47 showIfEmpty?: boolean 48 48 }) { 49 - const cache = React.useRef<Map<string, AppBskyActorDefs.KnownFollowers>>( 50 - new Map(), 51 - ) 49 + const cache = useRef<Map<string, AppBskyActorDefs.KnownFollowers>>(new Map()) 52 50 53 51 /* 54 52 * Results for `knownFollowers` are not sorted consistently, so when ··· 190 188 numberOfLines={2}> 191 189 {slice.length >= 2 ? ( 192 190 // 2-n followers, including blocks 193 - serverCount > 2 ? ( 191 + serverCount > 2 ? ( // only 2 194 192 <Trans> 195 193 Followed by{' '} 196 194 <Text emoji key={slice[0].profile.did} style={textStyle}> ··· 206 204 one="# other" 207 205 other="# others" 208 206 /> 209 - </Trans> // only 2 207 + </Trans> 210 208 ) : ( 211 209 <Trans> 212 210 Followed by{' '}
-1
src/components/LabelingServiceCard/index.tsx
··· 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' 5 5 import {Plural, Trans} from '@lingui/react/macro' 6 - import type React from 'react' 7 6 8 7 import {getLabelingServiceTitle} from '#/lib/moderation' 9 8 import {sanitizeHandle} from '#/lib/strings/handles'
+2 -2
src/components/LanguageSelect.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {msg} from '@lingui/core/macro' 3 3 import {useLingui} from '@lingui/react' 4 4 ··· 22 22 }) { 23 23 const {_} = useLingui() 24 24 25 - const handleOnChange = React.useCallback( 25 + const handleOnChange = useCallback( 26 26 (value: string) => { 27 27 if (!value) return 28 28 onChange(sanitizeAppLanguageSetting(value))
+2 -2
src/components/Layout/context.ts
··· 1 - import React from 'react' 1 + import {createContext} from 'react' 2 2 3 - export const ScrollbarOffsetContext = React.createContext({ 3 + export const ScrollbarOffsetContext = createContext({ 4 4 isWithinOffsetView: false, 5 5 }) 6 6 ScrollbarOffsetContext.displayName = 'ScrollbarOffsetContext'
+5 -5
src/components/LikedByList.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useMemo, useState} from 'react' 2 2 import {type AppBskyFeedGetLikes as GetLikes} from '@atproto/api' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 29 29 export function LikedByList({uri}: {uri: string}) { 30 30 const {_} = useLingui() 31 31 const initialNumToRender = useInitialNumToRender() 32 - const [isPTRing, setIsPTRing] = React.useState(false) 32 + const [isPTRing, setIsPTRing] = useState(false) 33 33 34 34 const { 35 35 data: resolvedUri, ··· 49 49 const error = resolveError || likedByError 50 50 const isError = !!resolveError || !!likedByError 51 51 52 - const likes = React.useMemo(() => { 52 + const likes = useMemo(() => { 53 53 if (data?.pages) { 54 54 return data.pages.flatMap(page => page.likes) 55 55 } 56 56 return [] 57 57 }, [data]) 58 58 59 - const onRefresh = React.useCallback(async () => { 59 + const onRefresh = useCallback(async () => { 60 60 setIsPTRing(true) 61 61 try { 62 62 await refetch() ··· 66 66 setIsPTRing(false) 67 67 }, [refetch, setIsPTRing]) 68 68 69 - const onEndReached = React.useCallback(async () => { 69 + const onEndReached = useCallback(async () => { 70 70 if (isFetchingNextPage || !hasNextPage || isError) return 71 71 try { 72 72 await fetchNextPage()
-1
src/components/LinearGradientBackground.tsx
··· 1 1 import {type StyleProp, type ViewStyle} from 'react-native' 2 2 import {LinearGradient} from 'expo-linear-gradient' 3 - import type React from 'react' 4 3 5 4 import {gradients} from '#/alf/tokens' 6 5
+4 -4
src/components/Link.tsx
··· 1 - import React, {useMemo} from 'react' 1 + import {useCallback, useMemo} from 'react' 2 2 import {type GestureResponderEvent, Linking} from 'react-native' 3 3 import {sanitizeUrl} from '@braintree/sanitize-url' 4 4 import { ··· 117 117 const {linkWarningDialogControl} = useGlobalDialogsControlContext() 118 118 const openLink = useOpenLink() 119 119 120 - const onPress = React.useCallback( 120 + const onPress = useCallback( 121 121 (e: GestureResponderEvent) => { 122 122 const exitEarlyIfFalse = outerOnPress?.(e) 123 123 ··· 217 217 ], 218 218 ) 219 219 220 - const handleLongPress = React.useCallback(() => { 220 + const handleLongPress = useCallback(() => { 221 221 const requiresWarning = Boolean( 222 222 !disableMismatchWarning && 223 223 displayText && ··· 242 242 linkWarningDialogControl, 243 243 ]) 244 244 245 - const onLongPress = React.useCallback( 245 + const onLongPress = useCallback( 246 246 (e: GestureResponderEvent) => { 247 247 const exitEarlyIfFalse = outerOnLongPress?.(e) 248 248 if (exitEarlyIfFalse === false) return
+3 -3
src/components/ListCard.tsx
··· 1 - import React from 'react' 1 + import {useEffect, useMemo} from 'react' 2 2 import {View} from 'react-native' 3 3 import { 4 4 type AppBskyGraphDefs, ··· 88 88 }: Props & Omit<LinkProps, 'to' | 'label'>) { 89 89 const queryClient = useQueryClient() 90 90 91 - const href = React.useMemo(() => { 91 + const href = useMemo(() => { 92 92 return createProfileListHref({list: view}) 93 93 }, [view]) 94 94 95 - React.useEffect(() => { 95 + useEffect(() => { 96 96 precacheList(queryClient, view) 97 97 }, [view, queryClient]) 98 98
+2 -2
src/components/Loader.tsx
··· 1 - import React from 'react' 1 + import {useEffect} from 'react' 2 2 import Animated, { 3 3 Easing, 4 4 useAnimatedStyle, ··· 20 20 transform: [{rotate: rotation.get() + 'deg'}], 21 21 })) 22 22 23 - React.useEffect(() => { 23 + useEffect(() => { 24 24 rotation.set(() => 25 25 withRepeat(withTiming(360, {duration: 500, easing: Easing.linear}), -1), 26 26 )
-1
src/components/MediaInsetBorder.tsx
··· 1 1 import {StyleSheet} from 'react-native' 2 - import type React from 'react' 3 2 4 3 import {atoms as a, platform, useTheme, type ViewStyleProp} from '#/alf' 5 4 import {Fill} from '#/components/Fill'
+5 -5
src/components/Menu/context.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext} from 'react' 2 2 3 3 import {type ContextType, type ItemContextType} from '#/components/Menu/types' 4 4 5 - export const Context = React.createContext<ContextType | null>(null) 5 + export const Context = createContext<ContextType | null>(null) 6 6 Context.displayName = 'MenuContext' 7 7 8 - export const ItemContext = React.createContext<ItemContextType | null>(null) 8 + export const ItemContext = createContext<ItemContextType | null>(null) 9 9 ItemContext.displayName = 'MenuItemContext' 10 10 11 11 export function useMenuContext() { 12 - const context = React.useContext(Context) 12 + const context = useContext(Context) 13 13 14 14 if (!context) { 15 15 throw new Error('useMenuContext must be used within a Context.Provider') ··· 19 19 } 20 20 21 21 export function useMenuItemContext() { 22 - const context = React.useContext(ItemContext) 22 + const context = useContext(ItemContext) 23 23 24 24 if (!context) { 25 25 throw new Error('useMenuItemContext must be used within a Context.Provider')
-1
src/components/Menu/types.ts
··· 4 4 type GestureResponderEvent, 5 5 type PressableProps, 6 6 } from 'react-native' 7 - import type React from 'react' 8 7 9 8 import {type TextStyleProp, type ViewStyleProp} from '#/alf' 10 9 import type * as Dialog from '#/components/Dialog'
+4 -4
src/components/Pills.tsx
··· 1 - import React from 'react' 1 + import {useMemo} from 'react' 2 2 import {View} from 'react-native' 3 3 import {BSKY_LABELER_DID, type ModerationCause} from '@atproto/api' 4 4 import {Trans} from '@lingui/react/macro' ··· 32 32 size = 'sm', 33 33 }: {children: React.ReactNode | React.ReactNode[]} & CommonProps & 34 34 ViewStyleProp) { 35 - const styles = React.useMemo(() => { 35 + const styles = useMemo(() => { 36 36 switch (size) { 37 37 case 'lg': 38 38 return [{gap: 5}] ··· 67 67 const isBlueskyLabel = 68 68 desc.sourceType === 'labeler' && desc.sourceDid === BSKY_LABELER_DID 69 69 70 - const {outer, avi, text} = React.useMemo(() => { 70 + const {outer, avi, text} = useMemo(() => { 71 71 switch (size) { 72 72 case 'lg': { 73 73 return { ··· 154 154 export function FollowsYou({size = 'sm'}: CommonProps) { 155 155 const t = useTheme() 156 156 157 - const variantStyles = React.useMemo(() => { 157 + const variantStyles = useMemo(() => { 158 158 switch (size) { 159 159 case 'sm': 160 160 case 'lg':
+7 -7
src/components/Post/Embed/ExternalEmbed/ExternalGif.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useRef, useState} from 'react' 2 2 import { 3 3 ActivityIndicator, 4 4 type GestureResponderEvent, ··· 31 31 const consentDialogControl = useDialogControl() 32 32 33 33 // Tracking if the placer has been activated 34 - const [isPlayerActive, setIsPlayerActive] = React.useState(false) 34 + const [isPlayerActive, setIsPlayerActive] = useState(false) 35 35 // Tracking whether the gif has been loaded yet 36 - const [isPrefetched, setIsPrefetched] = React.useState(false) 36 + const [isPrefetched, setIsPrefetched] = useState(false) 37 37 // Tracking whether the image is animating 38 - const [isAnimating, setIsAnimating] = React.useState(true) 38 + const [isAnimating, setIsAnimating] = useState(true) 39 39 40 40 // Used for controlling animation 41 - const imageRef = React.useRef<Image>(null) 41 + const imageRef = useRef<Image>(null) 42 42 43 - const load = React.useCallback(() => { 43 + const load = useCallback(() => { 44 44 setIsPlayerActive(true) 45 45 Image.prefetch(params.playerUri).then(() => { 46 46 // Replace the image once it's fetched ··· 48 48 }) 49 49 }, [params.playerUri]) 50 50 51 - const onPlayPress = React.useCallback( 51 + const onPlayPress = useCallback( 52 52 (event: GestureResponderEvent) => { 53 53 // Don't propagate on web 54 54 event.preventDefault()
+9 -9
src/components/Post/Embed/ExternalEmbed/ExternalPlayer.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useEffect, useMemo, useState} from 'react' 2 2 import { 3 3 ActivityIndicator, 4 4 type GestureResponderEvent, ··· 84 84 }) { 85 85 // ensures we only load what's requested 86 86 // when it's a youtube video, we need to allow both bsky.app and youtube.com 87 - const onShouldStartLoadWithRequest = React.useCallback( 87 + const onShouldStartLoadWithRequest = useCallback( 88 88 (event: ShouldStartLoadRequest) => 89 89 event.url === params.playerUri || 90 90 (params.source.startsWith('youtube') && ··· 129 129 const externalEmbedsPrefs = useExternalEmbedsPrefs() 130 130 const consentDialogControl = useDialogControl() 131 131 132 - const [isPlayerActive, setPlayerActive] = React.useState(false) 133 - const [isLoading, setIsLoading] = React.useState(true) 132 + const [isPlayerActive, setPlayerActive] = useState(false) 133 + const [isLoading, setIsLoading] = useState(true) 134 134 135 - const aspect = React.useMemo(() => { 135 + const aspect = useMemo(() => { 136 136 return getPlayerAspect({ 137 137 type: params.type, 138 138 width: windowDims.width, ··· 166 166 }, false) // False here disables autostarting the callback 167 167 168 168 // watch for leaving the viewport due to scrolling 169 - React.useEffect(() => { 169 + useEffect(() => { 170 170 // We don't want to do anything if the player isn't active 171 171 if (!isPlayerActive) return 172 172 ··· 185 185 } 186 186 }, [navigation, isPlayerActive, frameCallback]) 187 187 188 - const onLoad = React.useCallback(() => { 188 + const onLoad = useCallback(() => { 189 189 setIsLoading(false) 190 190 }, []) 191 191 192 - const onPlayPress = React.useCallback( 192 + const onPlayPress = useCallback( 193 193 (event: GestureResponderEvent) => { 194 194 // Prevent this from propagating upward on web 195 195 event.preventDefault() ··· 204 204 [externalEmbedsPrefs, consentDialogControl, params.source], 205 205 ) 206 206 207 - const onAcceptConsent = React.useCallback(() => { 207 + const onAcceptConsent = useCallback(() => { 208 208 setPlayerActive(true) 209 209 }, []) 210 210
+2 -2
src/components/Post/Embed/ExternalEmbed/index.tsx
··· 1 - import React, {useCallback} from 'react' 1 + import {useCallback, useMemo} from 'react' 2 2 import {type StyleProp, View, type ViewStyle} from 'react-native' 3 3 import {Image} from 'expo-image' 4 4 import {type AppBskyEmbedExternal} from '@atproto/api' ··· 38 38 const externalEmbedPrefs = useExternalEmbedsPrefs() 39 39 const niceUrl = toNiceDomain(link.uri) 40 40 const imageUri = link.thumb 41 - const embedPlayerParams = React.useMemo(() => { 41 + const embedPlayerParams = useMemo(() => { 42 42 const params = parseEmbedPlayerFromUrl(link.uri) 43 43 44 44 if (params && externalEmbedPrefs?.[params.source] !== 'hide') {
+5 -3
src/components/Post/Embed/VideoEmbed/ActiveVideoWebContext.tsx
··· 1 - import React, { 1 + import { 2 + createContext, 2 3 useCallback, 4 + useContext, 3 5 useEffect, 4 6 useId, 5 7 useMemo, ··· 10 12 11 13 import {IS_NATIVE, IS_WEB} from '#/env' 12 14 13 - const Context = React.createContext<{ 15 + const Context = createContext<{ 14 16 activeViewId: string | null 15 17 setActiveView: (viewId: string) => void 16 18 sendViewPosition: (viewId: string, y: number) => void ··· 94 96 } 95 97 96 98 export function useActiveVideoWeb() { 97 - const context = React.useContext(Context) 99 + const context = useContext(Context) 98 100 if (!context) { 99 101 throw new Error( 100 102 'useActiveVideoWeb must be used within a ActiveVideoWebProvider',
+7 -8
src/components/Post/Embed/VideoEmbed/VideoVolumeContext.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext, useMemo, useState} from 'react' 2 2 3 - const Context = React.createContext<{ 4 - // native 3 + const Context = createContext<{ 5 4 muted: boolean 6 5 setMuted: React.Dispatch<React.SetStateAction<boolean>> 7 6 // web ··· 11 10 Context.displayName = 'VideoVolumeContext' 12 11 13 12 export function Provider({children}: {children: React.ReactNode}) { 14 - const [muted, setMuted] = React.useState(true) 15 - const [volume, setVolume] = React.useState(1) 13 + const [muted, setMuted] = useState(true) 14 + const [volume, setVolume] = useState(1) 16 15 17 - const value = React.useMemo( 16 + const value = useMemo( 18 17 () => ({ 19 18 muted, 20 19 setMuted, ··· 28 27 } 29 28 30 29 export function useVideoVolumeState() { 31 - const context = React.useContext(Context) 30 + const context = useContext(Context) 32 31 if (!context) { 33 32 throw new Error( 34 33 'useVideoVolumeState must be used within a VideoVolumeProvider', ··· 38 37 } 39 38 40 39 export function useVideoMuteState() { 41 - const context = React.useContext(Context) 40 + const context = useContext(Context) 42 41 if (!context) { 43 42 throw new Error( 44 43 'useVideoMuteState must be used within a VideoVolumeProvider',
-1
src/components/PostControls/BookmarkButton.tsx
··· 4 4 import {msg} from '@lingui/core/macro' 5 5 import {useLingui} from '@lingui/react' 6 6 import {Trans} from '@lingui/react/macro' 7 - import type React from 'react' 8 7 9 8 import {useCleanError} from '#/lib/hooks/useCleanError' 10 9 import {type Shadow} from '#/state/cache/post-shadow'
+15 -15
src/components/ProfileHoverCard/index.web.tsx
··· 1 - import React, {useCallback} from 'react' 1 + import {memo, useCallback, useEffect, useMemo, useReducer, useRef} from 'react' 2 2 import {View} from 'react-native' 3 3 import { 4 4 type AppBskyActorDefs, ··· 61 61 62 62 export function ProfileHoverCard(props: ProfileHoverCardProps) { 63 63 const prefetchProfileQuery = usePrefetchProfileQuery() 64 - const prefetchedProfile = React.useRef(false) 64 + const prefetchedProfile = useRef(false) 65 65 const onPointerMove = () => { 66 66 if (!prefetchedProfile.current) { 67 67 prefetchedProfile.current = true ··· 116 116 middleware: floatingMiddlewares, 117 117 }) 118 118 119 - const [currentState, dispatch] = React.useReducer( 119 + const [currentState, dispatch] = useReducer( 120 120 // Tip: console.log(state, action) when debugging. 121 121 (state: State, action: Action): State => { 122 122 // Pressing within a card should always hide it. ··· 262 262 {stage: 'hidden'}, 263 263 ) 264 264 265 - React.useEffect(() => { 265 + useEffect(() => { 266 266 if (currentState.effect) { 267 267 const effect = currentState.effect 268 268 return effect() ··· 270 270 }, [currentState]) 271 271 272 272 const prefetchProfileQuery = usePrefetchProfileQuery() 273 - const prefetchedProfile = React.useRef(false) 274 - const prefetchIfNeeded = React.useCallback(async () => { 273 + const prefetchedProfile = useRef(false) 274 + const prefetchIfNeeded = useCallback(async () => { 275 275 if (!prefetchedProfile.current) { 276 276 prefetchedProfile.current = true 277 277 prefetchProfileQuery(props.did) 278 278 } 279 279 }, [prefetchProfileQuery, props.did]) 280 280 281 - const didFireHover = React.useRef(false) 282 - const onPointerMoveTarget = React.useCallback(() => { 281 + const didFireHover = useRef(false) 282 + const onPointerMoveTarget = useCallback(() => { 283 283 prefetchIfNeeded() 284 284 // Conceptually we want something like onPointerEnter, 285 285 // but we want to ignore entering only due to scrolling. ··· 290 290 } 291 291 }, [prefetchIfNeeded]) 292 292 293 - const onPointerLeaveTarget = React.useCallback(() => { 293 + const onPointerLeaveTarget = useCallback(() => { 294 294 didFireHover.current = false 295 295 dispatch('unhovered-target') 296 296 }, []) 297 297 298 - const onPointerEnterCard = React.useCallback(() => { 298 + const onPointerEnterCard = useCallback(() => { 299 299 dispatch('hovered-card') 300 300 }, []) 301 301 302 - const onPointerLeaveCard = React.useCallback(() => { 302 + const onPointerLeaveCard = useCallback(() => { 303 303 dispatch('unhovered-card') 304 304 }, []) 305 305 306 - const onPress = React.useCallback(() => { 306 + const onPress = useCallback(() => { 307 307 dispatch('pressed') 308 308 }, []) 309 309 ··· 411 411 </View> 412 412 ) 413 413 } 414 - Card = React.memo(Card) 414 + Card = memo(Card) 415 415 416 416 function Inner({ 417 417 profile, ··· 425 425 const t = useTheme() 426 426 const {_, i18n} = useLingui() 427 427 const {currentAccount} = useSession() 428 - const moderation = React.useMemo( 428 + const moderation = useMemo( 429 429 () => moderateProfile(profile, moderationOpts), 430 430 [profile, moderationOpts], 431 431 ) ··· 453 453 did: profile.did, 454 454 handle: profile.handle, 455 455 }) 456 - const isMe = React.useMemo( 456 + const isMe = useMemo( 457 457 () => currentAccount?.did === profile.did, 458 458 [currentAccount, profile], 459 459 )
+15 -8
src/components/ProgressGuide/Toast.tsx
··· 1 - import React, {useImperativeHandle} from 'react' 1 + import { 2 + forwardRef, 3 + useCallback, 4 + useImperativeHandle, 5 + useMemo, 6 + useRef, 7 + useState, 8 + } from 'react' 2 9 import {Pressable, useWindowDimensions, View} from 'react-native' 3 10 import Animated, { 4 11 Easing, ··· 28 35 visibleDuration?: number // default 5s 29 36 } 30 37 31 - export const ProgressGuideToast = React.forwardRef< 38 + export const ProgressGuideToast = forwardRef< 32 39 ProgressGuideToastRef, 33 40 ProgressGuideToastProps 34 41 >(function ProgressGuideToast({title, subtitle, visibleDuration}, ref) { 35 42 const t = useTheme() 36 43 const {_} = useLingui() 37 44 const insets = useSafeAreaInsets() 38 - const [isOpen, setIsOpen] = React.useState(false) 45 + const [isOpen, setIsOpen] = useState(false) 39 46 const translateY = useSharedValue(0) 40 47 const opacity = useSharedValue(0) 41 - const animatedCheckRef = React.useRef<AnimatedCheckRef | null>(null) 42 - const timeoutRef = React.useRef<NodeJS.Timeout | undefined>(undefined) 48 + const animatedCheckRef = useRef<AnimatedCheckRef | null>(null) 49 + const timeoutRef = useRef<NodeJS.Timeout | undefined>(undefined) 43 50 const winDim = useWindowDimensions() 44 51 45 52 /** 46 53 * Methods 47 54 */ 48 55 49 - const close = React.useCallback(() => { 56 + const close = useCallback(() => { 50 57 // clear the timeout, in case this was called imperatively 51 58 if (timeoutRef.current) { 52 59 clearTimeout(timeoutRef.current) ··· 67 74 ) 68 75 }, [setIsOpen, opacity]) 69 76 70 - const open = React.useCallback(() => { 77 + const open = useCallback(() => { 71 78 // set isOpen=true to render 72 79 setIsOpen(true) 73 80 ··· 105 112 [open, close], 106 113 ) 107 114 108 - const containerStyle = React.useMemo(() => { 115 + const containerStyle = useMemo(() => { 109 116 let left = 10 110 117 let right = 10 111 118 if (IS_WEB && winDim.width > 400) {
+2 -2
src/components/RichTextTag.tsx
··· 1 - import React from 'react' 1 + import {useMemo} from 'react' 2 2 import {type StyleProp, Text as RNText, type TextStyle} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 68 68 /* 69 69 * Mute word records that exactly match the tag in question. 70 70 */ 71 - const removeableMuteWords = React.useMemo(() => { 71 + const removeableMuteWords = useMemo(() => { 72 72 return ( 73 73 preferences?.moderationPrefs.mutedWords?.filter(word => { 74 74 return word.value === tag
-1
src/components/ScreenTransition.tsx
··· 6 6 SlideInLeft, 7 7 SlideInRight, 8 8 } from 'react-native-reanimated' 9 - import type React from 'react' 10 9 11 10 import {IS_WEB} from '#/env' 12 11
+4 -4
src/components/StarterPack/Main/FeedsList.tsx
··· 1 - import React, {useCallback} from 'react' 1 + import {forwardRef, useCallback, useImperativeHandle, useState} from 'react' 2 2 import {type ListRenderItemInfo, View} from 'react-native' 3 3 import {type AppBskyFeedDefs} from '@atproto/api' 4 4 ··· 19 19 scrollElRef: ListRef 20 20 } 21 21 22 - export const FeedsList = React.forwardRef<SectionRef, ProfilesListProps>( 22 + export const FeedsList = forwardRef<SectionRef, ProfilesListProps>( 23 23 function FeedsListImpl({feeds, headerHeight, scrollElRef}, ref) { 24 - const [initialHeaderHeight] = React.useState(headerHeight) 24 + const [initialHeaderHeight] = useState(headerHeight) 25 25 const bottomBarOffset = useBottomBarOffset(20) 26 26 const t = useTheme() 27 27 ··· 32 32 }) 33 33 }, [scrollElRef, headerHeight]) 34 34 35 - React.useImperativeHandle(ref, () => ({ 35 + useImperativeHandle(ref, () => ({ 36 36 scrollToTop: onScrollToTop, 37 37 })) 38 38
+3 -3
src/components/StarterPack/Main/PostsList.tsx
··· 1 - import React, {useCallback} from 'react' 1 + import {forwardRef, useCallback, useImperativeHandle} from 'react' 2 2 import {View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 17 17 scrollElRef: ListRef 18 18 } 19 19 20 - export const PostsList = React.forwardRef<SectionRef, ProfilesListProps>( 20 + export const PostsList = forwardRef<SectionRef, ProfilesListProps>( 21 21 function PostsListImpl({listUri, headerHeight, scrollElRef}, ref) { 22 22 const feed: FeedDescriptor = `list|${listUri}` 23 23 const {_} = useLingui() ··· 29 29 }) 30 30 }, [scrollElRef, headerHeight]) 31 31 32 - React.useImperativeHandle(ref, () => ({ 32 + useImperativeHandle(ref, () => ({ 33 33 scrollToTop: onScrollToTop, 34 34 })) 35 35
+4 -4
src/components/StarterPack/Main/ProfilesList.tsx
··· 1 - import React, {useCallback} from 'react' 1 + import {forwardRef, useCallback, useImperativeHandle, useState} from 'react' 2 2 import {type ListRenderItemInfo, View} from 'react-native' 3 3 import { 4 4 type AppBskyActorDefs, ··· 37 37 scrollElRef: ListRef 38 38 } 39 39 40 - export const ProfilesList = React.forwardRef<SectionRef, ProfilesListProps>( 40 + export const ProfilesList = forwardRef<SectionRef, ProfilesListProps>( 41 41 function ProfilesListImpl( 42 42 {listUri, moderationOpts, headerHeight, scrollElRef}, 43 43 ref, ··· 48 48 const {currentAccount} = useSession() 49 49 const {data, refetch, isError} = useAllListMembersQuery(listUri) 50 50 51 - const [isPTRing, setIsPTRing] = React.useState(false) 51 + const [isPTRing, setIsPTRing] = useState(false) 52 52 53 53 // The server returns these sorted by descending creation date, so we want to invert 54 54 ··· 80 80 }) 81 81 }, [scrollElRef, headerHeight]) 82 82 83 - React.useImperativeHandle(ref, () => ({ 83 + useImperativeHandle(ref, () => ({ 84 84 scrollToTop: onScrollToTop, 85 85 })) 86 86
+3 -3
src/components/StarterPack/StarterPackCard.tsx
··· 1 - import React from 'react' 1 + import {useMemo} from 'react' 2 2 import {View} from 'react-native' 3 3 import {Image} from 'expo-image' 4 4 import {AppBskyGraphStarterpack, AtUri} from '@atproto/api' ··· 115 115 }) { 116 116 const {_} = useLingui() 117 117 const qc = useQueryClient() 118 - const {rkey, handleOrDid} = React.useMemo(() => { 118 + const {rkey, handleOrDid} = useMemo(() => { 119 119 const rkey = new AtUri(view.uri).rkey 120 120 const {creator} = view 121 121 return {rkey, handleOrDid: creator.handle || creator.did} ··· 148 148 const {_} = useLingui() 149 149 const queryClient = useQueryClient() 150 150 const {record} = starterPack 151 - const {rkey, handleOrDid} = React.useMemo(() => { 151 + const {rkey, handleOrDid} = useMemo(() => { 152 152 const rkey = new AtUri(starterPack.uri).rkey 153 153 const {creator} = starterPack 154 154 return {rkey, handleOrDid: creator.handle || creator.did}
+2 -2
src/components/Toast/index.tsx
··· 1 - import React from 'react' 1 + import {isValidElement} from 'react' 2 2 import {View} from 'react-native' 3 3 import {nanoid} from 'nanoid/non-secure' 4 4 import {toast as sonner, Toaster} from 'sonner-native' ··· 61 61 duration: options?.duration ?? DURATION, 62 62 }, 63 63 ) 64 - } else if (React.isValidElement(content)) { 64 + } else if (isValidElement(content)) { 65 65 sonner.custom( 66 66 <ToastConfigProvider id={id} type={type}> 67 67 {content}
+2 -2
src/components/Toast/index.web.tsx
··· 1 - import React from 'react' 1 + import {isValidElement} from 'react' 2 2 import {nanoid} from 'nanoid/non-secure' 3 3 import {toast as sonner, Toaster} from 'sonner' 4 4 ··· 60 60 duration: options?.duration ?? DURATION, 61 61 }, 62 62 ) 63 - } else if (React.isValidElement(content)) { 63 + } else if (isValidElement(content)) { 64 64 sonner( 65 65 <ToastConfigProvider id={id} type={type}> 66 66 {content}
+2 -2
src/components/TrendingTopics.tsx
··· 1 - import React from 'react' 1 + import {useMemo} from 'react' 2 2 import {View} from 'react-native' 3 3 import {type AtUri} from '@atproto/api' 4 4 import {msg} from '@lingui/core/macro' ··· 170 170 171 171 export function useTopic(raw: TrendingTopic): ParsedTrendingTopic { 172 172 const {_} = useLingui() 173 - return React.useMemo(() => { 173 + return useMemo(() => { 174 174 const {topic: displayName, link} = raw 175 175 176 176 if (link.startsWith('/search')) {
+2 -2
src/components/ageAssurance/AgeAssuranceAppealDialog.tsx
··· 1 - import React from 'react' 1 + import {useState} from 'react' 2 2 import {View} from 'react-native' 3 3 import {ToolsOzoneReportDefs} from '@atproto/api' 4 4 import {msg} from '@lingui/core/macro' ··· 44 44 const {gtPhone} = useBreakpoints() 45 45 const agent = useAgent() 46 46 47 - const [details, setDetails] = React.useState('') 47 + const [details, setDetails] = useState('') 48 48 const isInvalid = details.length > 1000 49 49 50 50 const {mutate, isPending} = useMutation({
+64 -65
src/components/anim/AnimatedCheck.tsx
··· 1 - import React from 'react' 1 + import {forwardRef, useCallback, useEffect, useImperativeHandle} from 'react' 2 2 import Animated, { 3 3 Easing, 4 4 useAnimatedProps, ··· 23 23 playOnMount?: boolean 24 24 } 25 25 26 - export const AnimatedCheck = React.forwardRef< 27 - AnimatedCheckRef, 28 - AnimatedCheckProps 29 - >(function AnimatedCheck({playOnMount, ...props}, ref) { 30 - const {fill, size, style, ...rest} = useCommonSVGProps(props) 31 - const circleAnim = useSharedValue(0) 32 - const checkAnim = useSharedValue(0) 26 + export const AnimatedCheck = forwardRef<AnimatedCheckRef, AnimatedCheckProps>( 27 + function AnimatedCheck({playOnMount, ...props}, ref) { 28 + const {fill, size, style, ...rest} = useCommonSVGProps(props) 29 + const circleAnim = useSharedValue(0) 30 + const checkAnim = useSharedValue(0) 33 31 34 - const circleAnimatedProps = useAnimatedProps(() => ({ 35 - strokeDashoffset: 166 - circleAnim.get() * 166, 36 - })) 37 - const checkAnimatedProps = useAnimatedProps(() => ({ 38 - strokeDashoffset: 48 - 48 * checkAnim.get(), 39 - })) 32 + const circleAnimatedProps = useAnimatedProps(() => ({ 33 + strokeDashoffset: 166 - circleAnim.get() * 166, 34 + })) 35 + const checkAnimatedProps = useAnimatedProps(() => ({ 36 + strokeDashoffset: 48 - 48 * checkAnim.get(), 37 + })) 40 38 41 - const play = React.useCallback( 42 - (cb?: () => void) => { 43 - circleAnim.set(0) 44 - checkAnim.set(0) 39 + const play = useCallback( 40 + (cb?: () => void) => { 41 + circleAnim.set(0) 42 + checkAnim.set(0) 45 43 46 - circleAnim.set(() => 47 - withTiming(1, {duration: 500, easing: Easing.linear}), 48 - ) 49 - checkAnim.set(() => 50 - withDelay( 51 - 500, 52 - withTiming(1, {duration: 300, easing: Easing.linear}, cb), 53 - ), 54 - ) 55 - }, 56 - [circleAnim, checkAnim], 57 - ) 44 + circleAnim.set(() => 45 + withTiming(1, {duration: 500, easing: Easing.linear}), 46 + ) 47 + checkAnim.set(() => 48 + withDelay( 49 + 500, 50 + withTiming(1, {duration: 300, easing: Easing.linear}, cb), 51 + ), 52 + ) 53 + }, 54 + [circleAnim, checkAnim], 55 + ) 58 56 59 - React.useImperativeHandle(ref, () => ({ 60 - play, 61 - })) 57 + useImperativeHandle(ref, () => ({ 58 + play, 59 + })) 62 60 63 - React.useEffect(() => { 64 - if (playOnMount) { 65 - play() 66 - } 67 - }, [play, playOnMount]) 61 + useEffect(() => { 62 + if (playOnMount) { 63 + play() 64 + } 65 + }, [play, playOnMount]) 68 66 69 - return ( 70 - <Svg 71 - fill="none" 72 - {...rest} 73 - viewBox="0 0 52 52" 74 - width={size} 75 - height={size} 76 - style={style}> 77 - <AnimatedCircle 78 - animatedProps={circleAnimatedProps} 79 - cx="26" 80 - cy="26" 81 - r="24" 67 + return ( 68 + <Svg 82 69 fill="none" 83 - stroke={fill} 84 - strokeWidth={4} 85 - strokeDasharray={166} 86 - /> 87 - <AnimatedPath 88 - animatedProps={checkAnimatedProps} 89 - stroke={fill} 90 - d={PATH} 91 - strokeWidth={4} 92 - strokeDasharray={48} 93 - /> 94 - </Svg> 95 - ) 96 - }) 70 + {...rest} 71 + viewBox="0 0 52 52" 72 + width={size} 73 + height={size} 74 + style={style}> 75 + <AnimatedCircle 76 + animatedProps={circleAnimatedProps} 77 + cx="26" 78 + cy="26" 79 + r="24" 80 + fill="none" 81 + stroke={fill} 82 + strokeWidth={4} 83 + strokeDasharray={166} 84 + /> 85 + <AnimatedPath 86 + animatedProps={checkAnimatedProps} 87 + stroke={fill} 88 + d={PATH} 89 + strokeWidth={4} 90 + strokeDasharray={48} 91 + /> 92 + </Svg> 93 + ) 94 + }, 95 + )
+4 -6
src/components/dialogs/BirthDateSettings.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useMemo, useState} from 'react' 2 2 import {View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 125 125 }) { 126 126 const {_} = useLingui() 127 127 const cleanError = useCleanError() 128 - const [date, setDate] = React.useState( 129 - preferences.birthDate || getDateAgo(18), 130 - ) 128 + const [date, setDate] = useState(preferences.birthDate || getDateAgo(18)) 131 129 const {isPending, error, mutateAsync: setBirthDate} = useBirthdateMutation() 132 130 const hasChanged = date !== preferences.birthDate 133 - const errorMessage = React.useMemo(() => { 131 + const errorMessage = useMemo(() => { 134 132 if (error) { 135 133 const {raw, clean} = cleanError(error) 136 134 return clean || raw || error.toString() ··· 141 139 const isUnder13 = age < 13 142 140 const isUnder18 = age >= 13 && age < 18 143 141 144 - const onSave = React.useCallback(async () => { 142 + const onSave = useCallback(async () => { 145 143 try { 146 144 // skip if date is the same 147 145 if (hasChanged) {
+8 -8
src/components/dialogs/MutedWords.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useState} from 'react' 2 2 import {View} from 'react-native' 3 3 import {type AppBskyActorDefs, sanitizeMutedWordValue} from '@atproto/api' 4 4 import {msg} from '@lingui/core/macro' ··· 58 58 error: preferencesError, 59 59 } = usePreferencesQuery() 60 60 const {isPending, mutateAsync: addMutedWord} = useUpsertMutedWordsMutation() 61 - const [field, setField] = React.useState('') 62 - const [targets, setTargets] = React.useState(['content']) 63 - const [error, setError] = React.useState('') 64 - const [durations, setDurations] = React.useState(['forever']) 65 - const [excludeFollowing, setExcludeFollowing] = React.useState(false) 61 + const [field, setField] = useState('') 62 + const [targets, setTargets] = useState(['content']) 63 + const [error, setError] = useState('') 64 + const [durations, setDurations] = useState(['forever']) 65 + const [excludeFollowing, setExcludeFollowing] = useState(false) 66 66 67 - const submit = React.useCallback(async () => { 67 + const submit = useCallback(async () => { 68 68 const sanitizedValue = sanitizeMutedWordValue(field) 69 69 const surfaces = ['tag', targets.includes('content') && 'content'].filter( 70 70 Boolean, ··· 431 431 const isExpired = expiryDate && expiryDate < new Date() 432 432 const formatDistance = useFormatDistance() 433 433 434 - const remove = React.useCallback(async () => { 434 + const remove = useCallback(async () => { 435 435 control.close() 436 436 removeMutedWord(word) 437 437 }, [removeMutedWord, word, control])
+3 -3
src/components/dialogs/Signin.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 32 32 const {requestSwitchToAccount} = useLoggedOutViewControls() 33 33 const closeAllActiveElements = useCloseAllActiveElements() 34 34 35 - const showSignIn = React.useCallback(() => { 35 + const showSignIn = useCallback(() => { 36 36 closeAllActiveElements() 37 37 requestSwitchToAccount({requestedAccount: 'none'}) 38 38 }, [requestSwitchToAccount, closeAllActiveElements]) 39 39 40 - const showCreateAccount = React.useCallback(() => { 40 + const showCreateAccount = useCallback(() => { 41 41 closeAllActiveElements() 42 42 requestSwitchToAccount({requestedAccount: 'new'}) 43 43 }, [requestSwitchToAccount, closeAllActiveElements])
-1
src/components/dms/AfterReportDialog.tsx
··· 5 5 import {useLingui} from '@lingui/react' 6 6 import {Trans} from '@lingui/react/macro' 7 7 import {StackActions, useNavigation} from '@react-navigation/native' 8 - import type React from 'react' 9 8 10 9 import {type NavigationProp} from '#/lib/routes/types' 11 10 import {useProfileShadow} from '#/state/cache/profile-shadow'
+3 -6
src/components/dms/BlockedByListDialog.tsx
··· 1 - import React from 'react' 1 + import {Fragment} from 'react' 2 2 import {View} from 'react-native' 3 3 import {type ModerationCause} from '@atproto/api' 4 4 import {msg} from '@lingui/core/macro' ··· 25 25 return ( 26 26 <Prompt.Outer control={control} testID="blockedByListDialog"> 27 27 <Prompt.TitleText>{_(msg`User blocked by list`)}</Prompt.TitleText> 28 - 29 28 <View style={[a.gap_sm, a.pb_lg]}> 30 29 <Text 31 30 selectable ··· 39 38 {_(msg`Lists blocking this user:`)}{' '} 40 39 {listBlocks.map((block, i) => 41 40 block.source.type === 'list' ? ( 42 - <React.Fragment key={block.source.list.uri}> 41 + <Fragment key={block.source.list.uri}> 43 42 {i === 0 ? null : ', '} 44 43 <InlineLinkText 45 44 label={block.source.list.name} ··· 47 46 style={[a.text_md, a.leading_snug]}> 48 47 {block.source.list.name} 49 48 </InlineLinkText> 50 - </React.Fragment> 49 + </Fragment> 51 50 ) : null, 52 51 )} 53 52 </Text> 54 53 </View> 55 - 56 54 <Prompt.Actions> 57 55 <Prompt.Action cta={_(msg`I understand`)} onPress={() => {}} /> 58 56 </Prompt.Actions> 59 - 60 57 <Dialog.Close /> 61 58 </Prompt.Outer> 62 59 )
+6 -6
src/components/dms/ChatEmptyPill.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useMemo, useState} from 'react' 2 2 import {Pressable, View} from 'react-native' 3 3 import Animated, { 4 4 runOnJS, ··· 24 24 const t = useTheme() 25 25 const {_} = useLingui() 26 26 const playHaptic = useHaptics() 27 - const [promptIndex, setPromptIndex] = React.useState(lastIndex) 27 + const [promptIndex, setPromptIndex] = useState(lastIndex) 28 28 29 29 const scale = useSharedValue(1) 30 30 31 - const prompts = React.useMemo(() => { 31 + const prompts = useMemo(() => { 32 32 return [ 33 33 _(msg`Say hello!`), 34 34 _(msg`Share your favorite feed!`), ··· 40 40 ] 41 41 }, [_]) 42 42 43 - const onPressIn = React.useCallback(() => { 43 + const onPressIn = useCallback(() => { 44 44 if (IS_WEB) return 45 45 scale.set(() => withTiming(1.075, {duration: 100})) 46 46 }, [scale]) 47 47 48 - const onPressOut = React.useCallback(() => { 48 + const onPressOut = useCallback(() => { 49 49 if (IS_WEB) return 50 50 scale.set(() => withTiming(1, {duration: 100})) 51 51 }, [scale]) 52 52 53 - const onPress = React.useCallback(() => { 53 + const onPress = useCallback(() => { 54 54 runOnJS(playHaptic)() 55 55 let randomPromptIndex = Math.floor(Math.random() * prompts.length) 56 56 while (randomPromptIndex === lastIndex) {
+3 -3
src/components/dms/ConvoMenu.tsx
··· 1 - import React, {useCallback} from 'react' 1 + import {memo, useCallback} from 'react' 2 2 import {Keyboard, View} from 'react-native' 3 3 import {type ChatBskyConvoDefs, type ModerationCause} from '@atproto/api' 4 4 import {msg} from '@lingui/core/macro' ··· 159 159 </> 160 160 ) 161 161 } 162 - ConvoMenu = React.memo(ConvoMenu) 162 + ConvoMenu = memo(ConvoMenu) 163 163 164 164 function MenuContent({ 165 165 convo: initialConvo, ··· 211 211 212 212 const [queueBlock, queueUnblock] = useProfileBlockMutationQueue(profile) 213 213 214 - const toggleBlock = React.useCallback(() => { 214 + const toggleBlock = useCallback(() => { 215 215 if (listBlocks.length) { 216 216 blockedByListControl.open() 217 217 return
+2 -2
src/components/dms/DateDivider.tsx
··· 1 - import React from 'react' 1 + import {memo} from 'react' 2 2 import {View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 78 78 </View> 79 79 ) 80 80 } 81 - DateDivider = React.memo(DateDivider) 81 + DateDivider = memo(DateDivider) 82 82 export {DateDivider}
+3 -3
src/components/dms/MessageContext.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext} from 'react' 2 2 3 - const MessageContext = React.createContext(false) 3 + const MessageContext = createContext(false) 4 4 MessageContext.displayName = 'MessageContext' 5 5 6 6 export function MessageContextProvider({ ··· 14 14 } 15 15 16 16 export function useIsWithinMessage() { 17 - return React.useContext(MessageContext) 17 + return useContext(MessageContext) 18 18 }
+3 -3
src/components/dms/MessageItem.tsx
··· 1 - import React, {useCallback, useMemo} from 'react' 1 + import {memo, useCallback, useMemo} from 'react' 2 2 import { 3 3 type GestureResponderEvent, 4 4 type StyleProp, ··· 233 233 </> 234 234 ) 235 235 } 236 - MessageItem = React.memo(MessageItem) 236 + MessageItem = memo(MessageItem) 237 237 export {MessageItem} 238 238 239 239 let MessageItemMetadata = ({ ··· 328 328 </Text> 329 329 ) 330 330 } 331 - MessageItemMetadata = React.memo(MessageItemMetadata) 331 + MessageItemMetadata = memo(MessageItemMetadata) 332 332 export {MessageItemMetadata}
+3 -4
src/components/dms/MessageItemEmbed.tsx
··· 1 - import React from 'react' 1 + import {memo} from 'react' 2 2 import {useWindowDimensions, View} from 'react-native' 3 3 import {type $Typed, type AppBskyEmbedRecord} from '@atproto/api' 4 4 5 5 import {atoms as a, native, tokens, useTheme, web} from '#/alf' 6 - import {PostEmbedViewContext} from '#/components/Post/Embed' 7 - import {Embed} from '#/components/Post/Embed' 6 + import {Embed, PostEmbedViewContext} from '#/components/Post/Embed' 8 7 import {MessageContextProvider} from './MessageContext' 9 8 10 9 let MessageItemEmbed = ({ ··· 43 42 </MessageContextProvider> 44 43 ) 45 44 } 46 - MessageItemEmbed = React.memo(MessageItemEmbed) 45 + MessageItemEmbed = memo(MessageItemEmbed) 47 46 export {MessageItemEmbed}
+2 -2
src/components/dms/MessageProfileButton.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {View} from 'react-native' 3 3 import {type AppBskyActorDefs} from '@atproto/api' 4 4 import {msg} from '@lingui/core/macro' ··· 39 39 }, 40 40 }) 41 41 42 - const onPress = React.useCallback(() => { 42 + const onPress = useCallback(() => { 43 43 if (!convoAvailability?.canChat) { 44 44 return 45 45 }
+3 -3
src/components/dms/MessagesListBlockedFooter.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useMemo} from 'react' 2 2 import {View} from 'react-native' 3 3 import {type ModerationDecision} from '@atproto/api' 4 4 import {msg} from '@lingui/core/macro' ··· 38 38 const reportControl = useDialogControl() 39 39 const blockedByListControl = useDialogControl() 40 40 41 - const {listBlocks, userBlock} = React.useMemo(() => { 41 + const {listBlocks, userBlock} = useMemo(() => { 42 42 const modui = moderation.ui('profileView') 43 43 const blocks = modui.alerts.filter(alert => alert.type === 'blocking') 44 44 const listBlocks = blocks.filter(alert => alert.source.type === 'list') ··· 51 51 52 52 const isBlocking = !!userBlock || !!listBlocks.length 53 53 54 - const onUnblockPress = React.useCallback(() => { 54 + const onUnblockPress = useCallback(() => { 55 55 if (listBlocks.length) { 56 56 blockedByListControl.open() 57 57 } else {
+4 -4
src/components/dms/NewMessagesPill.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {Pressable, View} from 'react-native' 3 3 import Animated, { 4 4 runOnJS, ··· 33 33 34 34 const scale = useSharedValue(1) 35 35 36 - const onPressIn = React.useCallback(() => { 36 + const onPressIn = useCallback(() => { 37 37 if (IS_WEB) return 38 38 scale.set(() => withTiming(1.075, {duration: 100})) 39 39 }, [scale]) 40 40 41 - const onPressOut = React.useCallback(() => { 41 + const onPressOut = useCallback(() => { 42 42 if (IS_WEB) return 43 43 scale.set(() => withTiming(1, {duration: 100})) 44 44 }, [scale]) 45 45 46 - const onPress = React.useCallback(() => { 46 + const onPress = useCallback(() => { 47 47 runOnJS(playHaptic)() 48 48 onPressInner?.() 49 49 }, [onPressInner, playHaptic])
+3 -3
src/components/forms/DateField/index.web.tsx
··· 1 - import React from 'react' 1 + import {forwardRef, useCallback} from 'react' 2 2 import {StyleSheet, type TextInput, type TextInputProps} from 'react-native' 3 3 // @ts-expect-error untyped 4 4 import {unstable_createElement} from 'react-native-web' ··· 11 11 export * as utils from '#/components/forms/DateField/utils' 12 12 export const LabelText = TextField.LabelText 13 13 14 - const InputBase = React.forwardRef<HTMLInputElement, TextInputProps>( 14 + const InputBase = forwardRef<HTMLInputElement, TextInputProps>( 15 15 ({style, ...props}, ref) => { 16 16 return unstable_createElement('input', { 17 17 ...props, ··· 42 42 accessibilityHint, 43 43 maximumDate, 44 44 }: DateFieldProps) { 45 - const handleOnChange = React.useCallback( 45 + const handleOnChange = useCallback( 46 46 (e: any) => { 47 47 const date = e.target.valueAsDate || e.target.value 48 48
+2 -2
src/components/forms/HostingProvider.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {Keyboard, View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 28 28 const t = useTheme() 29 29 const {_} = useLingui() 30 30 31 - const onPressSelectService = React.useCallback(() => { 31 + const onPressSelectService = useCallback(() => { 32 32 Keyboard.dismiss() 33 33 serverInputControl.open() 34 34 onOpenDialog?.()
+6 -6
src/components/forms/InputGroup.tsx
··· 1 - import React from 'react' 1 + import {Children, cloneElement, Fragment, isValidElement} from 'react' 2 2 import {View} from 'react-native' 3 3 4 4 import {atoms, useTheme} from '#/alf' ··· 8 8 */ 9 9 export function InputGroup(props: React.PropsWithChildren<{}>) { 10 10 const t = useTheme() 11 - const children = React.Children.toArray(props.children) 11 + const children = Children.toArray(props.children) 12 12 const total = children.length 13 13 return ( 14 14 <View style={[atoms.w_full]}> 15 15 {children.map((child, i) => { 16 - return React.isValidElement(child) ? ( 17 - <React.Fragment key={i}> 16 + return isValidElement(child) ? ( 17 + <Fragment key={i}> 18 18 {i > 0 ? ( 19 19 <View 20 20 style={[atoms.border_b, {borderColor: t.palette.contrast_500}]} 21 21 /> 22 22 ) : null} 23 - {React.cloneElement(child, { 23 + {cloneElement(child, { 24 24 // @ts-ignore 25 25 style: [ 26 26 // @ts-ignore ··· 38 38 }, 39 39 ], 40 40 })} 41 - </React.Fragment> 41 + </Fragment> 42 42 ) : null 43 43 })} 44 44 </View>
+3 -3
src/components/hooks/useDelayedLoading.ts
··· 1 - import React from 'react' 1 + import {useEffect, useState} from 'react' 2 2 3 3 export function useDelayedLoading(delay: number, initialState: boolean = true) { 4 - const [isLoading, setIsLoading] = React.useState(initialState) 4 + const [isLoading, setIsLoading] = useState(initialState) 5 5 6 - React.useEffect(() => { 6 + useEffect(() => { 7 7 let timeout: NodeJS.Timeout 8 8 // on initial load, show a loading spinner for a hot sec to prevent flash 9 9 if (isLoading) timeout = setTimeout(() => setIsLoading(false), delay)
+3 -3
src/components/hooks/useFollowMethods.ts
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {msg} from '@lingui/core/macro' 3 3 import {useLingui} from '@lingui/react' 4 4 ··· 25 25 logContext, 26 26 ) 27 27 28 - const follow = React.useCallback(() => { 28 + const follow = useCallback(() => { 29 29 requireAuth(async () => { 30 30 try { 31 31 await queueFollow() ··· 38 38 }) 39 39 }, [_, queueFollow, requireAuth]) 40 40 41 - const unfollow = React.useCallback(() => { 41 + const unfollow = useCallback(() => { 42 42 requireAuth(async () => { 43 43 try { 44 44 await queueUnfollow()
+5 -5
src/components/hooks/useInteractionState.ts
··· 1 - import React from 'react' 1 + import {useCallback, useMemo, useState} from 'react' 2 2 3 3 export function useInteractionState() { 4 - const [state, setState] = React.useState(false) 4 + const [state, setState] = useState(false) 5 5 6 - const onIn = React.useCallback(() => { 6 + const onIn = useCallback(() => { 7 7 setState(true) 8 8 }, []) 9 - const onOut = React.useCallback(() => { 9 + const onOut = useCallback(() => { 10 10 setState(false) 11 11 }, []) 12 12 13 - return React.useMemo( 13 + return useMemo( 14 14 () => ({ 15 15 state, 16 16 onIn,
+5 -5
src/components/hooks/useRichText.ts
··· 1 - import React from 'react' 1 + import {useEffect, useState} from 'react' 2 2 import {RichText as RichTextAPI} from '@atproto/api' 3 3 4 4 import {useAgent} from '#/state/session' 5 5 6 6 export function useRichText(text: string): [RichTextAPI, boolean] { 7 - const [prevText, setPrevText] = React.useState(text) 8 - const [rawRT, setRawRT] = React.useState(() => new RichTextAPI({text})) 9 - const [resolvedRT, setResolvedRT] = React.useState<RichTextAPI | null>(null) 7 + const [prevText, setPrevText] = useState(text) 8 + const [rawRT, setRawRT] = useState(() => new RichTextAPI({text})) 9 + const [resolvedRT, setResolvedRT] = useState<RichTextAPI | null>(null) 10 10 const agent = useAgent() 11 11 if (text !== prevText) { 12 12 setPrevText(text) ··· 14 14 setResolvedRT(null) 15 15 // This will queue an immediate re-render 16 16 } 17 - React.useEffect(() => { 17 + useEffect(() => { 18 18 let ignore = false 19 19 async function resolveRTFacets() { 20 20 // new each time
+3 -3
src/components/hooks/useStarterPackEntry.native.ts
··· 1 - import React from 'react' 1 + import {useEffect, useState} from 'react' 2 2 3 3 import { 4 4 createStarterPackLinkFromAndroidReferrer, ··· 10 10 import {Referrer, SharedPrefs} from '../../../modules/expo-bluesky-swiss-army' 11 11 12 12 export function useStarterPackEntry() { 13 - const [ready, setReady] = React.useState(false) 13 + const [ready, setReady] = useState(false) 14 14 const setActiveStarterPack = useSetActiveStarterPack() 15 15 const hasCheckedForStarterPack = useHasCheckedForStarterPack() 16 16 17 - React.useEffect(() => { 17 + useEffect(() => { 18 18 if (ready) return 19 19 20 20 // On Android, we cannot clear the referral link. It gets stored for 90 days and all we can do is query for it. So,
+3 -3
src/components/hooks/useStarterPackEntry.ts
··· 1 - import React from 'react' 1 + import {useEffect, useState} from 'react' 2 2 3 3 import {httpStarterPackUriToAtUri} from '#/lib/strings/starter-pack' 4 4 import {useSetActiveStarterPack} from '#/state/shell/starter-pack' 5 5 6 6 export function useStarterPackEntry() { 7 - const [ready, setReady] = React.useState(false) 7 + const [ready, setReady] = useState(false) 8 8 9 9 const setActiveStarterPack = useSetActiveStarterPack() 10 10 11 - React.useEffect(() => { 11 + useEffect(() => { 12 12 const href = window.location.href 13 13 const atUri = httpStarterPackUriToAtUri(href) 14 14
+5 -5
src/components/icons/TEMPLATE.tsx
··· 1 - import React from 'react' 1 + import {forwardRef} from 'react' 2 2 import Svg, {Path} from 'react-native-svg' 3 3 4 4 import {type Props, useCommonSVGProps} from '#/components/icons/common' 5 5 6 - export const IconTemplate_Stroke2_Corner0_Rounded = React.forwardRef( 6 + export const IconTemplate_Stroke2_Corner0_Rounded = forwardRef( 7 7 function LogoImpl(props: Props, ref) { 8 8 const {fill, size, style, ...rest} = useCommonSVGProps(props) 9 9 ··· 41 41 strokeLinecap?: 'butt' | 'round' | 'square' 42 42 strokeLinejoin?: 'miter' | 'round' | 'bevel' 43 43 }) { 44 - return React.forwardRef<Svg, Props>(function LogoImpl(props, ref) { 44 + return forwardRef<Svg, Props>(function LogoImpl(props, ref) { 45 45 const {fill, size, style, gradient, ...rest} = useCommonSVGProps(props) 46 46 47 47 const hasStroke = strokeWidth > 0 ··· 72 72 } 73 73 74 74 export function createSinglePathSVG2({path}: {path: string}) { 75 - return React.forwardRef<Svg, Props>(function LogoImpl(props, ref) { 75 + return forwardRef<Svg, Props>(function LogoImpl(props, ref) { 76 76 const {fill, size, style, gradient, ...rest} = useCommonSVGProps(props) 77 77 78 78 return ( ··· 92 92 } 93 93 94 94 export function createMultiPathSVG({paths}: {paths: string[]}) { 95 - return React.forwardRef<Svg, Props>(function LogoImpl(props, ref) { 95 + return forwardRef<Svg, Props>(function LogoImpl(props, ref) { 96 96 const {fill, size, style, gradient, ...rest} = useCommonSVGProps(props) 97 97 98 98 return (
+2 -2
src/components/icons/VerifiedCheck.tsx
··· 1 - import React from 'react' 1 + import {forwardRef} from 'react' 2 2 import Svg, {Circle, Path} from 'react-native-svg' 3 3 4 4 import {type Props, useCommonSVGProps} from '#/components/icons/common' 5 5 6 - export const VerifiedCheck = React.forwardRef<Svg, Props>( 6 + export const VerifiedCheck = forwardRef<Svg, Props>( 7 7 function LogoImpl(props, ref) { 8 8 const {fill, size, style, ...rest} = useCommonSVGProps(props) 9 9
+2 -2
src/components/icons/VerifierCheck.tsx
··· 1 - import React from 'react' 1 + import {forwardRef} from 'react' 2 2 import Svg, {Path} from 'react-native-svg' 3 3 4 4 import {type Props, useCommonSVGProps} from '#/components/icons/common' 5 5 6 - export const VerifierCheck = React.forwardRef<Svg, Props>( 6 + export const VerifierCheck = forwardRef<Svg, Props>( 7 7 function LogoImpl(props, ref) { 8 8 const {fill, size, style, ...rest} = useCommonSVGProps(props) 9 9
+5 -5
src/components/intents/IntentDialogs.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext, useMemo, useState} from 'react' 2 2 3 3 import * as Dialog from '#/components/Dialog' 4 4 import {type DialogControlProps} from '#/components/Dialog' ··· 10 10 setVerifyEmailState: (state: {code: string} | undefined) => void 11 11 } 12 12 13 - const Context = React.createContext({} as Context) 13 + const Context = createContext({} as Context) 14 14 Context.displayName = 'IntentDialogsContext' 15 - export const useIntentDialogs = () => React.useContext(Context) 15 + export const useIntentDialogs = () => useContext(Context) 16 16 17 17 export function Provider({children}: {children: React.ReactNode}) { 18 18 const verifyEmailDialogControl = Dialog.useDialogControl() 19 - const [verifyEmailState, setVerifyEmailState] = React.useState< 19 + const [verifyEmailState, setVerifyEmailState] = useState< 20 20 {code: string} | undefined 21 21 >() 22 22 23 - const value = React.useMemo( 23 + const value = useMemo( 24 24 () => ({ 25 25 verifyEmailDialogControl, 26 26 verifyEmailState,
+2 -2
src/components/interstitials/Trending.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {ScrollView, View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 36 36 const {data: trending, error, isLoading} = useTrendingTopics() 37 37 const noTopics = !isLoading && !error && !trending?.topics?.length 38 38 39 - const onConfirmHide = React.useCallback(() => { 39 + const onConfirmHide = useCallback(() => { 40 40 ax.metric('trendingTopics:hide', {context: 'interstitial'}) 41 41 setTrendingDisabled(true) 42 42 }, [ax, setTrendingDisabled])
+4 -4
src/components/moderation/Hider.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext, useState} from 'react' 2 2 import {type ModerationUI} from '@atproto/api' 3 3 4 4 import { ··· 21 21 } 22 22 } 23 23 24 - const Context = React.createContext<Context>({} as Context) 24 + const Context = createContext<Context>({} as Context) 25 25 Context.displayName = 'HiderContext' 26 26 27 - export const useHider = () => React.useContext(Context) 27 + export const useHider = () => useContext(Context) 28 28 29 29 export function Outer({ 30 30 modui, ··· 38 38 }>) { 39 39 const control = useModerationDetailsDialogControl() 40 40 const blur = modui?.blurs[0] 41 - const [isContentVisible, setIsContentVisible] = React.useState( 41 + const [isContentVisible, setIsContentVisible] = useState( 42 42 isContentVisibleInitialState || !blur, 43 43 ) 44 44 const info = useModerationCauseDescription(blur)
+5 -5
src/components/moderation/LabelsOnMeDialog.tsx
··· 1 - import React, {useState} from 'react' 1 + import {useCallback, useMemo, useState} from 'react' 2 2 import {View} from 'react-native' 3 3 import {type ComAtprotoLabelDefs, ToolsOzoneReportDefs} from '@atproto/api' 4 4 import {XRPCError} from '@atproto/xrpc' ··· 47 47 function LabelsOnMeDialogInner(props: LabelsOnMeDialogProps) { 48 48 const {_} = useLingui() 49 49 const {currentAccount} = useSession() 50 - const [appealingLabel, setAppealingLabel] = React.useState< 50 + const [appealingLabel, setAppealingLabel] = useState< 51 51 ComAtprotoLabelDefs.Label | undefined 52 52 >(undefined) 53 53 const {labels} = props 54 54 const isAccount = props.type === 'account' 55 - const containsSelfLabel = React.useMemo( 55 + const containsSelfLabel = useMemo( 56 56 () => labels.some(l => l.src === currentAccount?.did), 57 57 [currentAccount?.did, labels], 58 58 ) ··· 224 224 const {_} = useLingui() 225 225 const {labeler, strings} = useLabelInfo(label) 226 226 const {gtMobile} = useBreakpoints() 227 - const [details, setDetails] = React.useState('') 227 + const [details, setDetails] = useState('') 228 228 const {subject} = useLabelSubject({label}) 229 229 const isAccountReport = 'did' in subject 230 230 const agent = useAgent() ··· 273 273 }, 274 274 }) 275 275 276 - const onSubmit = React.useCallback(() => mutate(), [mutate]) 276 + const onSubmit = useCallback(() => mutate(), [mutate]) 277 277 278 278 return ( 279 279 <>
+22 -14
src/components/moderation/ReportDialog/index.tsx
··· 1 - import React from 'react' 1 + import { 2 + useCallback, 3 + useEffect, 4 + useMemo, 5 + useReducer, 6 + useRef, 7 + useState, 8 + } from 'react' 2 9 import {Pressable, type ScrollView, View} from 'react-native' 3 10 import {type AppBskyLabelerDefs, BSKY_LABELER_DID} from '@atproto/api' 4 11 import {msg} from '@lingui/core/macro' ··· 66 73 }, 67 74 ) { 68 75 const ax = useAnalytics() 69 - const subject = React.useMemo( 76 + const subject = useMemo( 70 77 () => (props.subject ? parseReportSubject(props.subject) : undefined), 71 78 [props.subject], 72 79 ) 73 - const onClose = React.useCallback(() => { 80 + const onClose = useCallback(() => { 74 81 ax.metric('reportDialog:close', {}) 75 82 }, [ax]) 76 83 return ( ··· 108 115 const logger = ax.logger.useChild(ax.logger.Context.ReportDialog) 109 116 const t = useTheme() 110 117 const {_} = useLingui() 111 - const ref = React.useRef<ScrollView>(null) 118 + const ref = useRef<ScrollView>(null) 112 119 const { 113 120 data: allLabelers, 114 121 isLoading: isLabelerLoading, ··· 118 125 const isLoading = useDelayedLoading(500, isLabelerLoading) 119 126 const copy = useCopyForSubject(props.subject) 120 127 const {categories, getCategory} = useReportOptions() 121 - const [state, dispatch] = React.useReducer(reducer, initialState) 128 + const [state, dispatch] = useReducer(reducer, initialState) 122 129 123 130 /** 124 131 * Submission handling 125 132 */ 126 133 const {mutateAsync: submitReport} = useSubmitReportMutation() 127 - const [isPending, setPending] = React.useState(false) 128 - const [isSuccess, setSuccess] = React.useState(false) 134 + const [isPending, setPending] = useState(false) 135 + const [isSuccess, setSuccess] = useState(false) 129 136 130 137 // some reasons ONLY go to Bluesky 131 138 const isBskyOnlyReason = state?.selectedOption?.reason ··· 139 146 /** 140 147 * Labelers that support this `subject` and its NSID collection 141 148 */ 142 - const supportedLabelers = React.useMemo(() => { 149 + const supportedLabelers = useMemo(() => { 143 150 if (!allLabelers) return [] 144 151 return allLabelers 145 152 .filter(l => { ··· 169 176 if (supportedReasonTypes === undefined) return true 170 177 return ( 171 178 // supports new reason type 172 - supportedReasonTypes.includes(state.selectedOption.reason) || // supports old reason type (backwards compat) 179 + // supports old reason type (backwards compat) 180 + supportedReasonTypes.includes(state.selectedOption.reason) || 173 181 supportedReasonTypes.includes( 174 182 NEW_TO_OLD_REASONS_MAP[state.selectedOption.reason], 175 183 ) ··· 194 202 const isAlwaysBskyLabeler = 195 203 hasSingleSupportedLabeler && (isBskyOnlyReason || isBskyOnlySubject) 196 204 197 - const onSubmit = React.useCallback(async () => { 205 + const onSubmit = useCallback(async () => { 198 206 dispatch({type: 'clearError'}) 199 207 200 208 logger.info('submitting') ··· 587 595 check: () => boolean 588 596 callback: () => void 589 597 }) { 590 - React.useEffect(() => { 598 + useEffect(() => { 591 599 if (check()) { 592 600 callback() 593 601 } ··· 686 694 const t = useTheme() 687 695 const {_} = useLingui() 688 696 const gutters = useGutters(['compact']) 689 - const onPress = React.useCallback(() => { 697 + const onPress = useCallback(() => { 690 698 onSelect?.(option) 691 699 }, [onSelect, option]) 692 700 return ( ··· 731 739 const t = useTheme() 732 740 const {_} = useLingui() 733 741 const gutters = useGutters(['compact']) 734 - const onPress = React.useCallback(() => { 742 + const onPress = useCallback(() => { 735 743 onSelect?.(option) 736 744 }, [onSelect, option]) 737 745 return ( ··· 793 801 }) { 794 802 const t = useTheme() 795 803 const {_} = useLingui() 796 - const onPress = React.useCallback(() => { 804 + const onPress = useCallback(() => { 797 805 onSelect?.(labeler) 798 806 }, [onSelect, labeler]) 799 807 const title = getLabelingServiceTitle({
+2 -2
src/components/moderation/ScreenHider.tsx
··· 1 - import React from 'react' 1 + import {useState} from 'react' 2 2 import { 3 3 type StyleProp, 4 4 TouchableWithoutFeedback, ··· 39 39 }>) { 40 40 const t = useTheme() 41 41 const {_} = useLingui() 42 - const [override, setOverride] = React.useState(false) 42 + const [override, setOverride] = useState(false) 43 43 const navigation = useNavigation<NavigationProp>() 44 44 const {isMobile} = useWebMediaQueries() 45 45 const control = useModerationDetailsDialogControl()
+2 -2
src/lib/currency.ts
··· 1 - import React from 'react' 1 + import {useMemo} from 'react' 2 2 3 3 import {deviceLocales} from '#/locale/deviceLocales' 4 4 import {useLanguagePrefs} from '#/state/preferences' ··· 277 277 ) { 278 278 const geolocation = useGeolocation() 279 279 const {appLanguage} = useLanguagePrefs() 280 - return React.useMemo(() => { 280 + return useMemo(() => { 281 281 const locale = deviceLocales.at(0) 282 282 const languageTag = locale?.languageTag || appLanguage || 'en-US' 283 283 const countryCode = (
+5 -5
src/lib/custom-animations/CountWheel.tsx
··· 1 - import React from 'react' 1 + import {useEffect, useRef, useState} from 'react' 2 2 import {View} from 'react-native' 3 3 import Animated, { 4 4 Easing, ··· 105 105 // animation 106 106 // The initial entering/exiting animations will get skipped, since these will happen on screen mounts and would 107 107 // be unnecessary 108 - const [key, setKey] = React.useState(0) 109 - const [prevCount, setPrevCount] = React.useState(likeCount) 110 - const prevIsLiked = React.useRef(isLiked) 108 + const [key, setKey] = useState(0) 109 + const [prevCount, setPrevCount] = useState(likeCount) 110 + const prevIsLiked = useRef(isLiked) 111 111 const formatPostStatCount = useFormatPostStatCount() 112 112 const formattedCount = formatPostStatCount(likeCount) 113 113 const formattedPrevCount = formatPostStatCount(prevCount) 114 114 115 - React.useEffect(() => { 115 + useEffect(() => { 116 116 if (isLiked === prevIsLiked.current) { 117 117 return 118 118 }
+6 -6
src/lib/custom-animations/CountWheel.web.tsx
··· 1 - import React from 'react' 1 + import {useEffect, useRef, useState} from 'react' 2 2 import {View} from 'react-native' 3 3 import {useReducedMotion} from 'react-native-reanimated' 4 4 ··· 49 49 const shouldAnimate = !useReducedMotion() && hasBeenToggled 50 50 const shouldRoll = decideShouldRoll(isLiked, likeCount) 51 51 52 - const countView = React.useRef<HTMLDivElement>(null) 53 - const prevCountView = React.useRef<HTMLDivElement>(null) 52 + const countView = useRef<HTMLDivElement>(null) 53 + const prevCountView = useRef<HTMLDivElement>(null) 54 54 55 - const [prevCount, setPrevCount] = React.useState(likeCount) 56 - const prevIsLiked = React.useRef(isLiked) 55 + const [prevCount, setPrevCount] = useState(likeCount) 56 + const prevIsLiked = useRef(isLiked) 57 57 const formatPostStatCount = useFormatPostStatCount() 58 58 const formattedCount = formatPostStatCount(likeCount) 59 59 const formattedPrevCount = formatPostStatCount(prevCount) 60 60 61 - React.useEffect(() => { 61 + useEffect(() => { 62 62 if (isLiked === prevIsLiked.current) { 63 63 return 64 64 }
+7 -7
src/lib/custom-animations/GestureActionView.tsx
··· 1 - import React from 'react' 1 + import {useMemo, useState} from 'react' 2 2 import {type ColorValue, Dimensions, StyleSheet, View} from 'react-native' 3 3 import {Gesture, GestureDetector} from 'react-native-gesture-handler' 4 4 import Animated, { ··· 50 50 ) 51 51 } 52 52 53 - const [activeAction, setActiveAction] = React.useState< 53 + const [activeAction, setActiveAction] = useState< 54 54 'leftFirst' | 'leftSecond' | 'rightFirst' | 'rightSecond' | null 55 55 >(null) 56 56 ··· 231 231 } 232 232 }) 233 233 234 - const leftSideInterpolation = React.useMemo(() => { 234 + const leftSideInterpolation = useMemo(() => { 235 235 return createInterpolation({ 236 236 firstColor: actions.leftFirst?.color, 237 237 secondColor: actions.leftSecond?.color, ··· 241 241 }) 242 242 }, [actions.leftFirst, actions.leftSecond]) 243 243 244 - const rightSideInterpolation = React.useMemo(() => { 244 + const rightSideInterpolation = useMemo(() => { 245 245 return createInterpolation({ 246 246 firstColor: actions.rightFirst?.color, 247 247 secondColor: actions.rightSecond?.color, ··· 251 251 }) 252 252 }, [actions.rightFirst, actions.rightSecond]) 253 253 254 - const interpolation = React.useMemo<{ 254 + const interpolation = useMemo<{ 255 255 inputRange: number[] 256 256 outputRange: ColorValue[] 257 257 }>(() => { 258 258 if (!actions.leftFirst) { 259 - return rightSideInterpolation! 259 + return rightSideInterpolation 260 260 } else if (!actions.rightFirst) { 261 - return leftSideInterpolation! 261 + return leftSideInterpolation 262 262 } else { 263 263 return { 264 264 inputRange: [
-2
src/lib/custom-animations/GestureActionView.web.tsx
··· 1 - import type React from 'react' 2 - 3 1 export function GestureActionView({children}: {children: React.ReactNode}) { 4 2 return children 5 3 }
+6 -6
src/lib/custom-animations/LikeIcon.web.tsx
··· 1 - import React from 'react' 1 + import {useEffect, useRef} from 'react' 2 2 import {View} from 'react-native' 3 3 import {useReducedMotion} from 'react-native-reanimated' 4 4 ··· 50 50 const t = useTheme() 51 51 const size = big ? 22 : 18 52 52 const shouldAnimate = !useReducedMotion() && hasBeenToggled 53 - const prevIsLiked = React.useRef(isLiked) 53 + const prevIsLiked = useRef(isLiked) 54 54 55 - const likeIconRef = React.useRef<HTMLDivElement>(null) 56 - const circle1Ref = React.useRef<HTMLDivElement>(null) 57 - const circle2Ref = React.useRef<HTMLDivElement>(null) 55 + const likeIconRef = useRef<HTMLDivElement>(null) 56 + const circle1Ref = useRef<HTMLDivElement>(null) 57 + const circle2Ref = useRef<HTMLDivElement>(null) 58 58 59 - React.useEffect(() => { 59 + useEffect(() => { 60 60 if (prevIsLiked.current === isLiked) { 61 61 return 62 62 }
+2 -2
src/lib/haptics.ts
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import * as Device from 'expo-device' 3 3 import {impactAsync, ImpactFeedbackStyle} from 'expo-haptics' 4 4 ··· 8 8 export function useHaptics() { 9 9 const isHapticsDisabled = useHapticsDisabled() 10 10 11 - return React.useCallback( 11 + return useCallback( 12 12 (strength: 'Light' | 'Medium' | 'Heavy' = 'Medium') => { 13 13 if (isHapticsDisabled || IS_WEB) { 14 14 return
+4 -4
src/lib/hooks/useIntentHandler.ts
··· 1 - import React from 'react' 1 + import {useCallback, useEffect} from 'react' 2 2 import {Alert} from 'react-native' 3 3 import * as Linking from 'expo-linking' 4 4 import * as WebBrowser from 'expo-web-browser' ··· 28 28 const {currentAccount} = useSession() 29 29 const {tryApplyUpdate} = useApplyPullRequestOTAUpdate() 30 30 31 - React.useEffect(() => { 31 + useEffect(() => { 32 32 const handleIncomingURL = async (url: string) => { 33 33 if (IS_IOS) { 34 34 // Close in-app browser if it's open (iOS only) ··· 109 109 const {openComposer} = useOpenComposer() 110 110 const {hasSession} = useSession() 111 111 112 - return React.useCallback( 112 + return useCallback( 113 113 ({ 114 114 text, 115 115 imageUrisStr, ··· 166 166 const closeAllActiveElements = useCloseAllActiveElements() 167 167 const {verifyEmailDialogControl: control, setVerifyEmailState: setState} = 168 168 useIntentDialogs() 169 - return React.useCallback( 169 + return useCallback( 170 170 (code: string) => { 171 171 closeAllActiveElements() 172 172 setState({
+10 -10
src/lib/hooks/useOTAUpdates.ts
··· 1 - import React from 'react' 1 + import {useCallback, useEffect, useRef, useState} from 'react' 2 2 import {Alert, AppState, type AppStateStatus} from 'react-native' 3 3 import {nativeBuildVersion} from 'expo-application' 4 4 import { ··· 67 67 68 68 export function useApplyPullRequestOTAUpdate() { 69 69 const {currentlyRunning} = useUpdates() 70 - const [pending, setPending] = React.useState(false) 70 + const [pending, setPending] = useState(false) 71 71 const currentChannel = currentlyRunning?.channel 72 72 const isCurrentlyRunningPullRequestDeployment = 73 73 currentChannel?.startsWith('pull-request') ··· 124 124 export function useOTAUpdates() { 125 125 const shouldReceiveUpdates = isEnabled && !__DEV__ 126 126 127 - const appState = React.useRef<AppStateStatus>('active') 128 - const lastMinimize = React.useRef(0) 129 - const ranInitialCheck = React.useRef(false) 130 - const timeout = React.useRef<NodeJS.Timeout>(undefined) 127 + const appState = useRef<AppStateStatus>('active') 128 + const lastMinimize = useRef(0) 129 + const ranInitialCheck = useRef(false) 130 + const timeout = useRef<NodeJS.Timeout>(undefined) 131 131 const {currentlyRunning, isUpdatePending} = useUpdates() 132 132 const currentChannel = currentlyRunning?.channel 133 133 134 - const setCheckTimeout = React.useCallback(() => { 134 + const setCheckTimeout = useCallback(() => { 135 135 timeout.current = setTimeout(async () => { 136 136 try { 137 137 await setExtraParams() ··· 153 153 }, 10e3) 154 154 }, []) 155 155 156 - const onIsTestFlight = React.useCallback(async () => { 156 + const onIsTestFlight = useCallback(async () => { 157 157 try { 158 158 await updateTestflight() 159 159 } catch (err: any) { ··· 163 163 } 164 164 }, []) 165 165 166 - React.useEffect(() => { 166 + useEffect(() => { 167 167 // We don't need to check anything if the current update is a PR update 168 168 if (currentChannel?.startsWith('pull-request')) { 169 169 return ··· 186 186 187 187 // After the app has been minimized for 15 minutes, we want to either A. install an update if one has become available 188 188 // or B check for an update again. 189 - React.useEffect(() => { 189 + useEffect(() => { 190 190 // We also don't start this timeout if the user is on a pull request update 191 191 if (!isEnabled || currentChannel?.startsWith('pull-request')) { 192 192 return
+2 -2
src/lib/moderation.ts
··· 1 - import React from 'react' 1 + import {useMemo} from 'react' 2 2 import { 3 3 type AppBskyLabelerDefs, 4 4 BskyAgent, ··· 122 122 export function useLabelSubject({label}: {label: ComAtprotoLabelDefs.Label}): { 123 123 subject: Subject 124 124 } { 125 - return React.useMemo(() => { 125 + return useMemo(() => { 126 126 const {cid, uri} = label 127 127 if (cid) { 128 128 return {
+2 -2
src/lib/moderation/useModerationCauseDescription.ts
··· 1 - import React from 'react' 1 + import {useMemo} from 'react' 2 2 import { 3 3 BSKY_LABELER_DID, 4 4 type ModerationCause, ··· 39 39 const {labelDefs, labelers} = useLabelDefinitions() 40 40 const globalLabelStrings = useGlobalLabelStrings() 41 41 42 - return React.useMemo(() => { 42 + return useMemo(() => { 43 43 if (!cause) { 44 44 return { 45 45 icon: Warning,
-1
src/locale/i18nProvider.tsx
··· 2 2 import {i18n} from '@lingui/core' 3 3 import {I18nProvider as DefaultI18nProvider} from '@lingui/react' 4 4 import {type Locale} from 'date-fns' 5 - import type React from 'react' 6 5 7 6 import {useLocaleLanguage} from './i18n' 8 7
+7 -7
src/screens/Deactivated.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useState} from 'react' 2 2 import {View} from 'react-native' 3 3 import {useSafeAreaInsets} from 'react-native-safe-area-context' 4 4 import {msg} from '@lingui/core/macro' ··· 38 38 const hasOtherAccounts = accounts.length > 1 39 39 const {logoutCurrentAccount} = useSessionApi() 40 40 const agent = useAgent() 41 - const [pending, setPending] = React.useState(false) 42 - const [error, setError] = React.useState<string | undefined>() 41 + const [pending, setPending] = useState(false) 42 + const [error, setError] = useState<string | undefined>() 43 43 const queryClient = useQueryClient() 44 44 45 - const onSelectAccount = React.useCallback( 45 + const onSelectAccount = useCallback( 46 46 (account: SessionAccount) => { 47 47 if (account.did !== currentAccount?.did) { 48 48 onPressSwitchAccount(account, 'SwitchAccount') ··· 51 51 [currentAccount, onPressSwitchAccount], 52 52 ) 53 53 54 - const onPressAddAccount = React.useCallback(() => { 54 + const onPressAddAccount = useCallback(() => { 55 55 setShowLoggedOut(true) 56 56 }, [setShowLoggedOut]) 57 57 58 - const onPressLogout = React.useCallback(() => { 58 + const onPressLogout = useCallback(() => { 59 59 if (IS_WEB) { 60 60 // We're switching accounts, which remounts the entire app. 61 61 // On mobile, this gets us Home, but on the web we also need reset the URL. ··· 67 67 logoutCurrentAccount('Deactivated') 68 68 }, [logoutCurrentAccount]) 69 69 70 - const handleActivate = React.useCallback(async () => { 70 + const handleActivate = useCallback(async () => { 71 71 try { 72 72 setPending(true) 73 73 await agent.com.atproto.server.activateAccount()
+2 -2
src/screens/E2E/SharedPreferencesTesterScreen.tsx
··· 1 - import React from 'react' 1 + import {useState} from 'react' 2 2 import {View} from 'react-native' 3 3 4 4 import {ScrollView} from '#/view/com/util/Views' ··· 9 9 import {SharedPrefs} from '../../../modules/expo-bluesky-swiss-army' 10 10 11 11 export function SharedPreferencesTesterScreen() { 12 - const [currentTestOutput, setCurrentTestOutput] = React.useState<string>('') 12 + const [currentTestOutput, setCurrentTestOutput] = useState<string>('') 13 13 14 14 return ( 15 15 <Layout.Screen>
+15 -15
src/screens/Hashtag.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useMemo, useState} from 'react' 2 2 import {type ListRenderItemInfo, View} from 'react-native' 3 3 import {type AppBskyFeedDefs} from '@atproto/api' 4 4 import {msg} from '@lingui/core/macro' ··· 47 47 const {tag, author} = route.params 48 48 const {_} = useLingui() 49 49 50 - const decodedTag = React.useMemo(() => { 50 + const decodedTag = useMemo(() => { 51 51 return decodeURIComponent(tag) 52 52 }, [tag]) 53 53 54 54 const isCashtag = decodedTag.startsWith('$') 55 55 56 - const fullTag = React.useMemo(() => { 56 + const fullTag = useMemo(() => { 57 57 // Cashtags already include the $ prefix, hashtags need # added 58 58 return isCashtag ? decodedTag : `#${decodedTag}` 59 59 }, [decodedTag, isCashtag]) 60 60 61 - const headerTitle = React.useMemo(() => { 61 + const headerTitle = useMemo(() => { 62 62 // Keep cashtags uppercase, lowercase hashtags 63 63 const displayTag = isCashtag ? fullTag.toUpperCase() : fullTag.toLowerCase() 64 64 return enforceLen(displayTag, 24, true, 'middle') 65 65 }, [fullTag, isCashtag]) 66 66 67 - const sanitizedAuthor = React.useMemo(() => { 67 + const sanitizedAuthor = useMemo(() => { 68 68 if (!author) return '' 69 69 return sanitizeHandle(author) 70 70 }, [author]) 71 71 72 - const onShare = React.useCallback(() => { 72 + const onShare = useCallback(() => { 73 73 const url = new URL('https://bsky.app') 74 74 url.pathname = `/hashtag/${decodeURIComponent(tag)}` 75 75 if (author) { ··· 78 78 shareUrl(url.toString()) 79 79 }, [tag, author]) 80 80 81 - const [activeTab, setActiveTab] = React.useState(0) 81 + const [activeTab, setActiveTab] = useState(0) 82 82 const setMinimalShellMode = useSetMinimalShellMode() 83 83 84 84 useFocusEffect( 85 - React.useCallback(() => { 85 + useCallback(() => { 86 86 setMinimalShellMode(false) 87 87 }, [setMinimalShellMode]), 88 88 ) 89 89 90 - const onPageSelected = React.useCallback( 90 + const onPageSelected = useCallback( 91 91 (index: number) => { 92 92 setMinimalShellMode(false) 93 93 setActiveTab(index) ··· 95 95 [setMinimalShellMode], 96 96 ) 97 97 98 - const sections = React.useMemo(() => { 98 + const sections = useMemo(() => { 99 99 return [ 100 100 { 101 101 title: _(msg`Top`), ··· 177 177 }) { 178 178 const {_} = useLingui() 179 179 const initialNumToRender = useInitialNumToRender() 180 - const [isPTR, setIsPTR] = React.useState(false) 180 + const [isPTR, setIsPTR] = useState(false) 181 181 const t = useTheme() 182 182 const {hasSession} = useSession() 183 183 const trackPostView = usePostViewTracking('Hashtag') 184 184 185 185 const isCashtag = fullTag.startsWith('$') 186 186 187 - const queryParam = React.useMemo(() => { 187 + const queryParam = useMemo(() => { 188 188 // Cashtags need # prefix for search: "#$BTC" or "#$BTC from:author" 189 189 const searchTag = isCashtag ? `#${fullTag}` : fullTag 190 190 if (!author) return searchTag ··· 203 203 hasNextPage, 204 204 } = useSearchPostsQuery({query: queryParam, sort, enabled: active}) 205 205 206 - const posts = React.useMemo(() => { 206 + const posts = useMemo(() => { 207 207 return data?.pages.flatMap(page => page.posts) || [] 208 208 }, [data]) 209 209 210 - const onRefresh = React.useCallback(async () => { 210 + const onRefresh = useCallback(async () => { 211 211 setIsPTR(true) 212 212 await refetch() 213 213 setIsPTR(false) 214 214 }, [refetch]) 215 215 216 - const onEndReached = React.useCallback(() => { 216 + const onEndReached = useCallback(() => { 217 217 if (isFetchingNextPage || !hasNextPage || error) return 218 218 fetchNextPage() 219 219 }, [isFetchingNextPage, hasNextPage, error, fetchNextPage])
+2 -2
src/screens/Home/NoFeedsPinned.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {View} from 'react-native' 3 3 import {TID} from '@atproto/common-web' 4 4 import {msg} from '@lingui/core/macro' ··· 27 27 const {isPending, mutateAsync: overwriteSavedFeeds} = 28 28 useOverwriteSavedFeedsMutation() 29 29 30 - const addRecommendedFeeds = React.useCallback(async () => { 30 + const addRecommendedFeeds = useCallback(async () => { 31 31 let skippedTimeline = false 32 32 let skippedDiscover = false 33 33 let remainingSavedFeeds = []
+2 -2
src/screens/List/ListHiddenScreen.tsx
··· 1 - import React from 'react' 1 + import {useState} from 'react' 2 2 import {View} from 'react-native' 3 3 import {AppBskyGraphDefs} from '@atproto/api' 4 4 import {msg} from '@lingui/core/macro' ··· 45 45 46 46 const isModList = list.purpose === AppBskyGraphDefs.MODLIST 47 47 48 - const [isProcessing, setIsProcessing] = React.useState(false) 48 + const [isProcessing, setIsProcessing] = useState(false) 49 49 const listBlockMutation = useListBlockMutation() 50 50 const listMuteMutation = useListMuteMutation() 51 51 const {mutateAsync: removeSavedFeed} = useRemoveFeedMutation()
+2 -2
src/screens/Login/ForgotPasswordForm.tsx
··· 1 - import React, {useState} from 'react' 1 + import {useCallback, useState} from 'react' 2 2 import {Keyboard, View} from 'react-native' 3 3 import {type ComAtprotoServerDescribeServer} from '@atproto/api' 4 4 import {msg} from '@lingui/core/macro' ··· 44 44 const [email, setEmail] = useState<string>('') 45 45 const {_} = useLingui() 46 46 47 - const onPressSelectService = React.useCallback(() => { 47 + const onPressSelectService = useCallback(() => { 48 48 Keyboard.dismiss() 49 49 }, []) 50 50
-1
src/screens/Login/FormContainer.tsx
··· 1 1 import {type StyleProp, View, type ViewStyle} from 'react-native' 2 - import type React from 'react' 3 2 4 3 import {atoms as a, useBreakpoints, useGutters} from '#/alf' 5 4 import {Text} from '#/components/Typography'
+4 -4
src/screens/Messages/Conversation.tsx
··· 1 - import React, {useCallback, useEffect} from 'react' 1 + import {useCallback, useEffect, useMemo, useState} from 'react' 2 2 import {View} from 'react-native' 3 3 import { 4 4 type AppBskyActorDefs, ··· 105 105 }) 106 106 const recipient = useMaybeProfileShadow(recipientUnshadowed) 107 107 108 - const moderation = React.useMemo(() => { 108 + const moderation = useMemo(() => { 109 109 if (!recipient || !moderationOpts) return null 110 110 return moderateProfile(recipient, moderationOpts) 111 111 }, [recipient, moderationOpts]) ··· 113 113 // Because we want to give the list a chance to asynchronously scroll to the end before it is visible to the user, 114 114 // we use `hasScrolled` to determine when to render. With that said however, there is a chance that the chat will be 115 115 // empty. So, we also check for that possible state as well and render once we can. 116 - const [hasScrolled, setHasScrolled] = React.useState(false) 116 + const [hasScrolled, setHasScrolled] = useState(false) 117 117 const readyToShow = 118 118 hasScrolled || 119 119 (isConvoActive(convoState) && ··· 122 122 123 123 // Any time that we re-render the `Initializing` state, we have to reset `hasScrolled` to false. After entering this 124 124 // state, we know that we're resetting the list of messages and need to re-scroll to the bottom when they get added. 125 - React.useEffect(() => { 125 + useEffect(() => { 126 126 if (convoState.status === ConvoStatus.Initializing) { 127 127 setHasScrolled(false) 128 128 }
+3 -3
src/screens/Messages/components/ChatListItem.tsx
··· 1 - import React, {useCallback, useMemo, useState} from 'react' 1 + import {memo, useCallback, useMemo, useState} from 'react' 2 2 import {type GestureResponderEvent, View} from 'react-native' 3 3 import { 4 4 AppBskyEmbedRecord, ··· 80 80 ) 81 81 } 82 82 83 - ChatListItem = React.memo(ChatListItem) 83 + ChatListItem = memo(ChatListItem) 84 84 85 85 function ChatListItemReady({ 86 86 convo, ··· 104 104 const {gtMobile} = useBreakpoints() 105 105 const profile = useProfileShadow(profileUnshadowed) 106 106 const {mutate: markAsRead} = useMarkAsReadMutation() 107 - const moderation = React.useMemo( 107 + const moderation = useMemo( 108 108 () => moderateProfile(profile, moderationOpts), 109 109 [profile, moderationOpts], 110 110 )
+14 -17
src/screens/Messages/components/MessageInput.web.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useEffect, useRef, useState} from 'react' 2 2 import {Pressable, View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 43 43 const {_} = useLingui() 44 44 const t = useTheme() 45 45 const {getDraft, clearDraft} = useMessageDraft() 46 - const [message, setMessage] = React.useState(getDraft) 46 + const [message, setMessage] = useState(getDraft) 47 47 48 48 const inputStyles = useSharedInputStyles() 49 - const isComposing = React.useRef(false) 50 - const [isFocused, setIsFocused] = React.useState(false) 51 - const [isHovered, setIsHovered] = React.useState(false) 52 - const [textAreaHeight, setTextAreaHeight] = React.useState(38) 53 - const textAreaRef = React.useRef<HTMLTextAreaElement>(null) 49 + const isComposing = useRef(false) 50 + const [isFocused, setIsFocused] = useState(false) 51 + const [isHovered, setIsHovered] = useState(false) 52 + const [textAreaHeight, setTextAreaHeight] = useState(38) 53 + const textAreaRef = useRef<HTMLTextAreaElement>(null) 54 54 55 - const onSubmit = React.useCallback(() => { 55 + const onSubmit = useCallback(() => { 56 56 if (!hasEmbed && message.trim() === '') { 57 57 return 58 58 } ··· 66 66 setEmbed(undefined) 67 67 }, [message, onSendMessage, _, clearDraft, hasEmbed, setEmbed]) 68 68 69 - const onKeyDown = React.useCallback( 69 + const onKeyDown = useCallback( 70 70 (e: React.KeyboardEvent<HTMLTextAreaElement>) => { 71 71 // Don't submit the form when the Japanese or any other IME is composing 72 72 if (isComposing.current) return ··· 98 98 [onSubmit], 99 99 ) 100 100 101 - const onChange = React.useCallback( 102 - (e: React.ChangeEvent<HTMLTextAreaElement>) => { 103 - setMessage(e.target.value) 104 - }, 105 - [], 106 - ) 101 + const onChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => { 102 + setMessage(e.target.value) 103 + }, []) 107 104 108 - const onEmojiInserted = React.useCallback( 105 + const onEmojiInserted = useCallback( 109 106 (emoji: Emoji) => { 110 107 if (!textAreaRef.current) { 111 108 return ··· 123 120 }, 124 121 [setMessage], 125 122 ) 126 - React.useEffect(() => { 123 + useEffect(() => { 127 124 textInputWebEmitter.addListener('emoji-inserted', onEmojiInserted) 128 125 return () => { 129 126 textInputWebEmitter.removeListener('emoji-inserted', onEmojiInserted)
+2 -2
src/screens/Messages/components/MessageListError.tsx
··· 1 - import React from 'react' 1 + import {useMemo} from 'react' 2 2 import {View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 12 12 export function MessageListError({item}: {item: ConvoItem & {type: 'error'}}) { 13 13 const t = useTheme() 14 14 const {_} = useLingui() 15 - const {description, help, cta} = React.useMemo(() => { 15 + const {description, help, cta} = useMemo(() => { 16 16 return { 17 17 [ConvoItemError.FirehoseFailed]: { 18 18 description: _(msg`This chat was disconnected`),
+8 -8
src/screens/ModerationInteractionSettings/index.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useMemo, useState} from 'react' 2 2 import {View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 63 63 const {_} = useLingui() 64 64 const {mutateAsync: setPostInteractionSettings, isPending} = 65 65 usePostInteractionSettingsMutation() 66 - const [error, setError] = React.useState<string | undefined>(undefined) 66 + const [error, setError] = useState<string | undefined>(undefined) 67 67 68 - const allowUI = React.useMemo(() => { 68 + const allowUI = useMemo(() => { 69 69 return threadgateRecordToAllowUISetting({ 70 70 $type: 'app.bsky.feed.threadgate', 71 71 post: '', ··· 73 73 allow: preferences.postInteractionSettings.threadgateAllowRules, 74 74 }) 75 75 }, [preferences.postInteractionSettings.threadgateAllowRules]) 76 - const postgate = React.useMemo(() => { 76 + const postgate = useMemo(() => { 77 77 return createPostgateRecord({ 78 78 post: '', 79 79 embeddingRules: ··· 81 81 }) 82 82 }, [preferences.postInteractionSettings.postgateEmbeddingRules]) 83 83 84 - const [maybeEditedAllowUI, setAllowUI] = React.useState(allowUI) 85 - const [maybeEditedPostgate, setEditedPostgate] = React.useState(postgate) 84 + const [maybeEditedAllowUI, setAllowUI] = useState(allowUI) 85 + const [maybeEditedPostgate, setEditedPostgate] = useState(postgate) 86 86 87 - const wasEdited = React.useMemo(() => { 87 + const wasEdited = useMemo(() => { 88 88 return ( 89 89 !deepEqual(allowUI, maybeEditedAllowUI) || 90 90 !deepEqual(postgate.embeddingRules, maybeEditedPostgate.embeddingRules) 91 91 ) 92 92 }, [postgate, allowUI, maybeEditedAllowUI, maybeEditedPostgate]) 93 93 94 - const onSave = React.useCallback(async () => { 94 + const onSave = useCallback(async () => { 95 95 setError('') 96 96 97 97 try {
+2 -2
src/screens/Onboarding/StepInterests/InterestButton.tsx
··· 1 - import React from 'react' 1 + import {useMemo} from 'react' 2 2 import {type TextStyle, View, type ViewStyle} from 'react-native' 3 3 4 4 import {type Interest, useInterestsDisplayNames} from '#/lib/interests' ··· 12 12 const interestsDisplayNames = useInterestsDisplayNames() 13 13 const ctx = Toggle.useItemContext() 14 14 15 - const styles = React.useMemo(() => { 15 + const styles = useMemo(() => { 16 16 const hovered: ViewStyle[] = [ 17 17 { 18 18 backgroundColor:
+4 -4
src/screens/Onboarding/StepInterests/index.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useState} from 'react' 2 2 import {View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 27 27 const interestsDisplayNames = useInterestsDisplayNames() 28 28 29 29 const {state, dispatch} = useOnboardingInternalState() 30 - const [saving, setSaving] = React.useState(false) 31 - const [selectedInterests, setSelectedInterests] = React.useState<string[]>( 30 + const [saving, setSaving] = useState(false) 31 + const [selectedInterests, setSelectedInterests] = useState<string[]>( 32 32 state.interestsStepResults.selectedInterests.map(i => i), 33 33 ) 34 34 35 - const saveInterests = React.useCallback(async () => { 35 + const saveInterests = useCallback(async () => { 36 36 setSaving(true) 37 37 38 38 try {
+2 -2
src/screens/Onboarding/StepProfile/AvatarCircle.tsx
··· 1 - import React from 'react' 1 + import {useMemo} from 'react' 2 2 import {View} from 'react-native' 3 3 import {Image as ExpoImage} from 'expo-image' 4 4 import {msg} from '@lingui/core/macro' ··· 22 22 const t = useTheme() 23 23 const {avatar} = useAvatar() 24 24 25 - const styles = React.useMemo( 25 + const styles = useMemo( 26 26 () => ({ 27 27 imageContainer: [ 28 28 a.rounded_full,
+2 -2
src/screens/Onboarding/StepProfile/AvatarCreatorCircle.tsx
··· 1 - import React from 'react' 1 + import {useMemo} from 'react' 2 2 import {View} from 'react-native' 3 3 4 4 import {type Avatar} from '#/screens/Onboarding/StepProfile/index' ··· 14 14 const t = useTheme() 15 15 const Icon = avatar.placeholder.component 16 16 17 - const styles = React.useMemo( 17 + const styles = useMemo( 18 18 () => ({ 19 19 imageContainer: [ 20 20 a.rounded_full,
+3 -3
src/screens/Onboarding/StepProfile/AvatarCreatorItems.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 39 39 const t = useTheme() 40 40 const isEmojis = type === 'emojis' 41 41 42 - const onSelectEmoji = React.useCallback( 42 + const onSelectEmoji = useCallback( 43 43 (emoji: EmojiName) => { 44 44 setAvatar(prev => ({ 45 45 ...prev, ··· 49 49 [setAvatar], 50 50 ) 51 51 52 - const onSelectColor = React.useCallback( 52 + const onSelectColor = useCallback( 53 53 (color: AvatarColor) => { 54 54 setAvatar(prev => ({ 55 55 ...prev,
+15 -8
src/screens/Onboarding/StepProfile/PlaceholderCanvas.tsx
··· 1 - import React from 'react' 1 + import { 2 + forwardRef, 3 + lazy, 4 + Suspense, 5 + useImperativeHandle, 6 + useMemo, 7 + useRef, 8 + } from 'react' 2 9 import {View} from 'react-native' 3 10 import type ViewShot from 'react-native-view-shot' 4 11 5 12 import {useAvatar} from '#/screens/Onboarding/StepProfile/index' 6 13 import {atoms as a} from '#/alf' 7 14 8 - const LazyViewShot = React.lazy( 15 + const LazyViewShot = lazy( 9 16 // @ts-expect-error dynamic import 10 17 () => import('react-native-view-shot/src/index'), 11 18 ) ··· 18 25 19 26 // This component is supposed to be invisible to the user. We only need this for ViewShot to have something to 20 27 // "screenshot". 21 - export const PlaceholderCanvas = React.forwardRef<PlaceholderCanvasRef, {}>( 28 + export const PlaceholderCanvas = forwardRef<PlaceholderCanvasRef, {}>( 22 29 function PlaceholderCanvas({}, ref) { 23 30 const {avatar} = useAvatar() 24 - const viewshotRef = React.useRef<ViewShot>(null) 31 + const viewshotRef = useRef<ViewShot>(null) 25 32 const Icon = avatar.placeholder.component 26 33 27 - const styles = React.useMemo( 34 + const styles = useMemo( 28 35 () => ({ 29 36 container: [a.absolute, {top: -2000}], 30 37 imageContainer: [ ··· 36 43 [], 37 44 ) 38 45 39 - React.useImperativeHandle(ref, () => ({ 46 + useImperativeHandle(ref, () => ({ 40 47 capture: async () => { 41 48 if (viewshotRef.current?.capture) { 42 49 return await viewshotRef.current.capture() ··· 46 53 47 54 return ( 48 55 <View style={styles.container}> 49 - <React.Suspense fallback={null}> 56 + <Suspense fallback={null}> 50 57 <LazyViewShot 51 58 // @ts-ignore this library doesn't have types 52 59 ref={viewshotRef} ··· 70 77 /> 71 78 </View> 72 79 </LazyViewShot> 73 - </React.Suspense> 80 + </Suspense> 74 81 </View> 75 82 ) 76 83 },
+21 -13
src/screens/Onboarding/StepProfile/index.tsx
··· 1 - import React from 'react' 1 + import { 2 + createContext, 3 + useCallback, 4 + useContext, 5 + useEffect, 6 + useMemo, 7 + useRef, 8 + useState, 9 + } from 'react' 2 10 import {View} from 'react-native' 3 11 import {Image as ExpoImage} from 'expo-image' 4 12 import {ImageManipulator, SaveFormat} from 'expo-image-manipulator' ··· 60 68 setAvatar: React.Dispatch<React.SetStateAction<Avatar>> 61 69 } 62 70 63 - const AvatarContext = React.createContext<IAvatarContext>({} as IAvatarContext) 71 + const AvatarContext = createContext<IAvatarContext>({} as IAvatarContext) 64 72 AvatarContext.displayName = 'AvatarContext' 65 - export const useAvatar = () => React.useContext(AvatarContext) 73 + export const useAvatar = () => useContext(AvatarContext) 66 74 67 75 const randomColor = 68 76 avatarColors[Math.floor(Math.random() * avatarColors.length)] ··· 76 84 const requestNotificationsPermission = useRequestNotificationsPermission() 77 85 78 86 const creatorControl = Dialog.useDialogControl() 79 - const [error, setError] = React.useState('') 87 + const [error, setError] = useState('') 80 88 81 89 const {state, dispatch} = useOnboardingInternalState() 82 - const [avatar, setAvatar] = React.useState<Avatar>({ 90 + const [avatar, setAvatar] = useState<Avatar>({ 83 91 image: state.profileStepResults?.image, 84 92 placeholder: state.profileStepResults.creatorState?.emoji || emojiItems.at, 85 93 backgroundColor: ··· 87 95 useCreatedAvatar: state.profileStepResults.isCreatedAvatar, 88 96 }) 89 97 90 - const canvasRef = React.useRef<PlaceholderCanvasRef>(null) 98 + const canvasRef = useRef<PlaceholderCanvasRef>(null) 91 99 92 - React.useEffect(() => { 100 + useEffect(() => { 93 101 requestNotificationsPermission('StartOnboarding') 94 102 }, [requestNotificationsPermission]) 95 103 96 104 const sheetWrapper = useSheetWrapper() 97 - const openPicker = React.useCallback( 105 + const openPicker = useCallback( 98 106 async (opts?: ImagePickerOptions) => { 99 107 const response = await sheetWrapper( 100 108 launchImageLibraryAsync({ ··· 139 147 [_, setError, sheetWrapper], 140 148 ) 141 149 142 - const onContinue = React.useCallback(async () => { 150 + const onContinue = useCallback(async () => { 143 151 let imageUri = avatar?.image?.path 144 152 145 153 // In the event that view-shot didn't load in time and the user pressed continue, this will just be undefined ··· 167 175 ax.metric('onboarding:profile:nextPressed', {}) 168 176 }, [ax, avatar, dispatch]) 169 177 170 - const onDoneCreating = React.useCallback(() => { 178 + const onDoneCreating = useCallback(() => { 171 179 setAvatar(prev => ({ 172 180 ...prev, 173 181 image: undefined, ··· 176 184 creatorControl.close() 177 185 }, [creatorControl]) 178 186 179 - const openLibrary = React.useCallback(async () => { 187 + const openLibrary = useCallback(async () => { 180 188 if (!(await requestPhotoAccessIfNeeded())) { 181 189 return 182 190 } ··· 225 233 sheetWrapper, 226 234 ]) 227 235 228 - const onSecondaryPress = React.useCallback(() => { 236 + const onSecondaryPress = useCallback(() => { 229 237 if (avatar.useCreatedAvatar) { 230 238 openLibrary() 231 239 } else { ··· 233 241 } 234 242 }, [avatar.useCreatedAvatar, creatorControl, openLibrary]) 235 243 236 - const value = React.useMemo( 244 + const value = useMemo( 237 245 () => ({ 238 246 avatar, 239 247 setAvatar,
+2 -2
src/screens/Post/PostLikedBy.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {Plural, Trans} from '@lingui/react/macro' 3 3 import {useFocusEffect} from '@react-navigation/native' 4 4 ··· 25 25 } 26 26 27 27 useFocusEffect( 28 - React.useCallback(() => { 28 + useCallback(() => { 29 29 setMinimalShellMode(false) 30 30 }, [setMinimalShellMode]), 31 31 )
+2 -2
src/screens/Post/PostQuotes.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {Plural, Trans} from '@lingui/react/macro' 3 3 import {useFocusEffect} from '@react-navigation/native' 4 4 ··· 25 25 } 26 26 27 27 useFocusEffect( 28 - React.useCallback(() => { 28 + useCallback(() => { 29 29 setMinimalShellMode(false) 30 30 }, [setMinimalShellMode]), 31 31 )
+2 -2
src/screens/Post/PostRepostedBy.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {Plural, Trans} from '@lingui/react/macro' 3 3 import {useFocusEffect} from '@react-navigation/native' 4 4 ··· 25 25 } 26 26 27 27 useFocusEffect( 28 - React.useCallback(() => { 28 + useCallback(() => { 29 29 setMinimalShellMode(false) 30 30 }, [setMinimalShellMode]), 31 31 )
+5 -5
src/screens/PostThread/components/ThreadItemAnchorFollowButton.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useEffect, useMemo, useState} from 'react' 2 2 import {type AppBskyActorDefs} from '@atproto/api' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 71 71 72 72 const isFollowing = !!profile.viewer?.following 73 73 const isFollowedBy = !!profile.viewer?.followedBy 74 - const [wasFollowing, setWasFollowing] = React.useState<boolean>(isFollowing) 74 + const [wasFollowing, setWasFollowing] = useState<boolean>(isFollowing) 75 75 76 76 // This prevents the button from disappearing as soon as we follow. 77 - const showFollowBtn = React.useMemo( 77 + const showFollowBtn = useMemo( 78 78 () => !isFollowing || !wasFollowing, 79 79 [isFollowing, wasFollowing], 80 80 ) ··· 90 90 * sudden rendering of the button. However, on web if we do this, there's an obvious flicker once the 91 91 * button renders. So, we update the state in both cases. 92 92 */ 93 - React.useEffect(() => { 93 + useEffect(() => { 94 94 const updateWasFollowing = () => { 95 95 if (wasFollowing !== isFollowing) { 96 96 setWasFollowing(isFollowing) ··· 106 106 } 107 107 }, [isFollowing, wasFollowing, navigation]) 108 108 109 - const onPress = React.useCallback(() => { 109 + const onPress = useCallback(() => { 110 110 if (!isFollowing) { 111 111 requireAuth(async () => { 112 112 try {
+2 -2
src/screens/Profile/ErrorState.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 16 16 const {_} = useLingui() 17 17 const navigation = useNavigation<NavigationProp>() 18 18 19 - const onPressBack = React.useCallback(() => { 19 + const onPressBack = useCallback(() => { 20 20 if (navigation.canGoBack()) { 21 21 navigation.goBack() 22 22 } else {
-1
src/screens/Profile/Header/GrowableAvatar.tsx
··· 5 5 type SharedValue, 6 6 useAnimatedStyle, 7 7 } from 'react-native-reanimated' 8 - import type React from 'react' 9 8 10 9 import {usePagerHeaderContext} from '#/view/com/pager/PagerHeaderContext' 11 10 import {IS_IOS} from '#/env'
-1
src/screens/Profile/Header/GrowableBanner.tsx
··· 13 13 import {useSafeAreaInsets} from 'react-native-safe-area-context' 14 14 import {BlurView} from 'expo-blur' 15 15 import {useIsFetching} from '@tanstack/react-query' 16 - import type React from 'react' 17 16 18 17 import {RQKEY_ROOT as STARTERPACK_RQKEY_ROOT} from '#/state/queries/actor-starter-packs' 19 18 import {RQKEY_ROOT as FEED_RQKEY_ROOT} from '#/state/queries/post-feed'
+6 -6
src/screens/Profile/KnownFollowers.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useMemo, useState} from 'react' 2 2 import {type AppBskyActorDefs} from '@atproto/api' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 51 51 52 52 const {name} = route.params 53 53 54 - const [isPTRing, setIsPTRing] = React.useState(false) 54 + const [isPTRing, setIsPTRing] = useState(false) 55 55 const { 56 56 data: resolvedDid, 57 57 isLoading: isDidLoading, ··· 67 67 refetch, 68 68 } = useProfileKnownFollowersQuery(resolvedDid) 69 69 70 - const onRefresh = React.useCallback(async () => { 70 + const onRefresh = useCallback(async () => { 71 71 setIsPTRing(true) 72 72 try { 73 73 await refetch() ··· 77 77 setIsPTRing(false) 78 78 }, [refetch, setIsPTRing]) 79 79 80 - const onEndReached = React.useCallback(async () => { 80 + const onEndReached = useCallback(async () => { 81 81 if (isFetchingNextPage || !hasNextPage || !!error) return 82 82 try { 83 83 await fetchNextPage() ··· 86 86 } 87 87 }, [isFetchingNextPage, hasNextPage, error, fetchNextPage]) 88 88 89 - const followers = React.useMemo(() => { 89 + const followers = useMemo(() => { 90 90 if (data?.pages) { 91 91 return data.pages.flatMap(page => page.followers) 92 92 } ··· 96 96 const isError = Boolean(resolveError || error) 97 97 98 98 useFocusEffect( 99 - React.useCallback(() => { 99 + useCallback(() => { 100 100 setMinimalShellMode(false) 101 101 }, [setMinimalShellMode]), 102 102 )
+2 -2
src/screens/Profile/ProfileFollowers.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {Plural} from '@lingui/react/macro' 3 3 import {useFocusEffect} from '@react-navigation/native' 4 4 ··· 24 24 }) 25 25 26 26 useFocusEffect( 27 - React.useCallback(() => { 27 + useCallback(() => { 28 28 setMinimalShellMode(false) 29 29 }, [setMinimalShellMode]), 30 30 )
+2 -2
src/screens/Profile/ProfileFollows.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {Plural} from '@lingui/react/macro' 3 3 import {useFocusEffect} from '@react-navigation/native' 4 4 ··· 24 24 }) 25 25 26 26 useFocusEffect( 27 - React.useCallback(() => { 27 + useCallback(() => { 28 28 setMinimalShellMode(false) 29 29 }, [setMinimalShellMode]), 30 30 )
+2 -2
src/screens/Profile/ProfileLabelerLikedBy.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {msg} from '@lingui/core/macro' 3 3 import {useLingui} from '@lingui/react' 4 4 import {useFocusEffect} from '@react-navigation/native' ··· 22 22 const {_} = useLingui() 23 23 24 24 useFocusEffect( 25 - React.useCallback(() => { 25 + useCallback(() => { 26 26 setMinimalShellMode(false) 27 27 }, [setMinimalShellMode]), 28 28 )
+5 -5
src/screens/Profile/components/ProfileFeedHeader.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useMemo, useState} from 'react' 2 2 import {View} from 'react-native' 3 3 import {AtUri} from '@atproto/api' 4 4 import {msg} from '@lingui/core/macro' ··· 95 95 96 96 const {data: preferences} = usePreferencesQuery() 97 97 98 - const [likeUri, setLikeUri] = React.useState(info.likeUri || '') 98 + const [likeUri, setLikeUri] = useState(info.likeUri || '') 99 99 const likeCount = 100 100 (info.likeCount || 0) + 101 101 (likeUri && !info.likeUri ? 1 : !likeUri && info.likeUri ? -1 : 0) ··· 401 401 useUnlikeMutation() 402 402 403 403 const isLiked = !!likeUri 404 - const feedRkey = React.useMemo(() => new AtUri(info.uri).rkey, [info.uri]) 404 + const feedRkey = useMemo(() => new AtUri(info.uri).rkey, [info.uri]) 405 405 406 406 const onToggleLiked = async () => { 407 407 try { ··· 427 427 } 428 428 } 429 429 430 - const onPressShare = React.useCallback(() => { 430 + const onPressShare = useCallback(() => { 431 431 playHaptic() 432 432 const url = toShareUrl(info.route.href) 433 433 shareUrl(url) 434 434 ax.metric('feed:share', {feedUrl: info.uri}) 435 435 }, [info, playHaptic]) 436 436 437 - const onPressReport = React.useCallback(() => { 437 + const onPressReport = useCallback(() => { 438 438 reportDialogControl.open() 439 439 }, [reportDialogControl]) 440 440
+2 -2
src/screens/Search/components/StarterPackCard.tsx
··· 1 - import React from 'react' 1 + import {useState} from 'react' 2 2 import {View} from 'react-native' 3 3 import { 4 4 type AppBskyGraphDefs, ··· 143 143 const computedTotal = (total ?? numPending) - numPending 144 144 const circlesCount = numPending + 1 // add total at end 145 145 const widthPerc = 100 / circlesCount 146 - const [size, setSize] = React.useState<number | null>(null) 146 + const [size, setSize] = useState<number | null>(null) 147 147 148 148 const isPending = (numPending && profiles.length === 0) || !moderationOpts 149 149
+4 -4
src/screens/Settings/components/DeactivateAccountDialog.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useState} from 'react' 2 2 import {View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 36 36 const {_} = useLingui() 37 37 const agent = useAgent() 38 38 const {logoutCurrentAccount} = useSessionApi() 39 - const [pending, setPending] = React.useState(false) 40 - const [error, setError] = React.useState<string | undefined>() 39 + const [pending, setPending] = useState(false) 40 + const [error, setError] = useState<string | undefined>() 41 41 42 - const handleDeactivate = React.useCallback(async () => { 42 + const handleDeactivate = useCallback(async () => { 43 43 try { 44 44 setPending(true) 45 45 await agent.com.atproto.server.deactivateAccount({})
+2 -2
src/screens/Settings/components/Email2FAToggle.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {msg} from '@lingui/core/macro' 3 3 import {useLingui} from '@lingui/react' 4 4 ··· 17 17 const disableDialogControl = useDialogControl() 18 18 const emailDialogControl = useEmailDialogControl() 19 19 20 - const onToggle = React.useCallback(() => { 20 + const onToggle = useCallback(() => { 21 21 emailDialogControl.open({ 22 22 id: EmailDialogScreenID.Manage2FA, 23 23 })
+2 -2
src/screens/Settings/components/PwiOptOut.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {View} from 'react-native' 3 3 import {type $Typed, ComAtprotoLabelDefs} from '@atproto/api' 4 4 import {msg} from '@lingui/core/macro' ··· 26 26 profile?.labels?.some(l => l.val === '!no-unauthenticated') || false 27 27 const canToggle = profile && !updateProfile.isPending 28 28 29 - const onToggleOptOut = React.useCallback(() => { 29 + const onToggleOptOut = useCallback(() => { 30 30 if (!profile) { 31 31 return 32 32 }
+3 -3
src/screens/Signup/StepCaptcha/CaptchaWebView.web.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useEffect} from 'react' 2 2 import {StyleSheet} from 'react-native' 3 3 4 4 // @ts-ignore web only, we will always redirect to the app on web (CORS) ··· 15 15 onSuccess: (code: string) => void 16 16 onError: (error: unknown) => void 17 17 }) { 18 - React.useEffect(() => { 18 + useEffect(() => { 19 19 const timeout = setTimeout(() => { 20 20 onError({ 21 21 errorMessage: 'User did not complete the captcha within 30 seconds', ··· 27 27 } 28 28 }, [onError]) 29 29 30 - const onLoad = React.useCallback(() => { 30 + const onLoad = useCallback(() => { 31 31 // @ts-ignore web 32 32 const frame: HTMLIFrameElement = document.getElementById( 33 33 'captcha-iframe',
+7 -7
src/screens/Signup/StepCaptcha/index.tsx
··· 1 - import React, {useEffect, useState} from 'react' 1 + import {useCallback, useEffect, useMemo, useState} from 'react' 2 2 import {ActivityIndicator, Platform, View} from 'react-native' 3 3 import ReactNativeDeviceAttest from 'react-native-device-attest' 4 4 import {msg} from '@lingui/core/macro' ··· 75 75 const theme = useTheme() 76 76 const {state, dispatch} = useSignupContext() 77 77 78 - const [completed, setCompleted] = React.useState(false) 78 + const [completed, setCompleted] = useState(false) 79 79 80 - const stateParam = React.useMemo(() => nanoid(15), []) 81 - const url = React.useMemo(() => { 80 + const stateParam = useMemo(() => nanoid(15), []) 81 + const url = useMemo(() => { 82 82 const newUrl = new URL(state.serviceUrl) 83 83 newUrl.pathname = CAPTCHA_PATH 84 84 newUrl.searchParams.set( ··· 107 107 payload, 108 108 ]) 109 109 110 - const onSuccess = React.useCallback( 110 + const onSuccess = useCallback( 111 111 (code: string) => { 112 112 setCompleted(true) 113 113 ax.metric('signup:captchaSuccess', {}) ··· 119 119 [ax, dispatch], 120 120 ) 121 121 122 - const onError = React.useCallback( 122 + const onError = useCallback( 123 123 (error?: unknown) => { 124 124 dispatch({ 125 125 type: 'setError', ··· 134 134 [_, ax, dispatch, state.handle], 135 135 ) 136 136 137 - const onBackPress = React.useCallback(() => { 137 + const onBackPress = useCallback(() => { 138 138 logger.error('Signup Flow Error', { 139 139 errorMessage: 140 140 'User went back from captcha step. Possibly encountered an error.',
+4 -4
src/screens/Signup/StepInfo/index.tsx
··· 1 - import React, {useRef} from 'react' 1 + import {useEffect, useRef, useState} from 'react' 2 2 import {type TextInput, View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 89 89 : true 90 90 const isDeviceGeolocationGranted = useIsDeviceGeolocationGranted() 91 91 92 - const [hasWarnedEmail, setHasWarnedEmail] = React.useState<boolean>(false) 92 + const [hasWarnedEmail, setHasWarnedEmail] = useState<boolean>(false) 93 93 94 - const tldtsRef = React.useRef<typeof tldts>(undefined) 95 - React.useEffect(() => { 94 + const tldtsRef = useRef<typeof tldts>(undefined) 95 + useEffect(() => { 96 96 // @ts-expect-error - valid path 97 97 import('tldts/dist/index.cjs.min.js').then(tldts => { 98 98 tldtsRef.current = tldts
+3 -3
src/screens/Signup/state.ts
··· 1 - import React, {useCallback} from 'react' 1 + import {createContext, useCallback, useContext} from 'react' 2 2 import {LayoutAnimation} from 'react-native' 3 3 import { 4 4 ComAtprotoServerCreateAccount, ··· 250 250 state: SignupState 251 251 dispatch: React.Dispatch<SignupAction> 252 252 } 253 - export const SignupContext = React.createContext<IContext>({} as IContext) 253 + export const SignupContext = createContext<IContext>({} as IContext) 254 254 SignupContext.displayName = 'SignupContext' 255 - export const useSignupContext = () => React.useContext(SignupContext) 255 + export const useSignupContext = () => useContext(SignupContext) 256 256 257 257 export function useSubmitSignup() { 258 258 const ax = useAnalytics()
+6 -6
src/screens/SignupQueued.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useEffect, useState} from 'react' 2 2 import {Modal, ScrollView, View} from 'react-native' 3 3 import {SystemBars} from 'react-native-edge-to-edge' 4 4 import {useSafeAreaInsets} from 'react-native-safe-area-context' ··· 27 27 const {logoutCurrentAccount} = useSessionApi() 28 28 const agent = useAgent() 29 29 30 - const [isProcessing, setProcessing] = React.useState(false) 31 - const [estimatedTime, setEstimatedTime] = React.useState<string | undefined>( 30 + const [isProcessing, setProcessing] = useState(false) 31 + const [estimatedTime, setEstimatedTime] = useState<string | undefined>( 32 32 undefined, 33 33 ) 34 - const [placeInQueue, setPlaceInQueue] = React.useState<number | undefined>( 34 + const [placeInQueue, setPlaceInQueue] = useState<number | undefined>( 35 35 undefined, 36 36 ) 37 37 38 - const checkStatus = React.useCallback(async () => { 38 + const checkStatus = useCallback(async () => { 39 39 setProcessing(true) 40 40 try { 41 41 const res = await agent.com.atproto.temp.checkSignupQueue() ··· 65 65 agent, 66 66 ]) 67 67 68 - React.useEffect(() => { 68 + useEffect(() => { 69 69 checkStatus() 70 70 const interval = setInterval(checkStatus, 60e3) 71 71 return () => clearInterval(interval)
+3 -4
src/screens/StarterPack/StarterPackLandingScreen.tsx
··· 1 - import React from 'react' 1 + import {useEffect, useState} from 'react' 2 2 import {Pressable, View} from 'react-native' 3 3 import Animated, {FadeIn, FadeOut} from 'react-native-reanimated' 4 4 import { ··· 75 75 AppBskyGraphDefs.validateStarterPackView(starterPack) && 76 76 AppBskyGraphStarterpack.validateRecord(starterPack.record) 77 77 78 - React.useEffect(() => { 78 + useEffect(() => { 79 79 if (isErrorStarterPack || (starterPack && !isValid)) { 80 80 setScreenState(LoggedOutScreenState.S_LoginOrCreateAccount) 81 81 } ··· 128 128 const androidDialogControl = useDialogControl() 129 129 const [descriptionRt] = useRichText(record.description || '') 130 130 131 - const [appClipOverlayVisible, setAppClipOverlayVisible] = 132 - React.useState(false) 131 + const [appClipOverlayVisible, setAppClipOverlayVisible] = useState(false) 133 132 134 133 const listItemsCount = starterPack.list?.listItemCount ?? 0 135 134
+9 -9
src/screens/StarterPack/StarterPackScreen.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useEffect, useState} from 'react' 2 2 import {View} from 'react-native' 3 3 import {Image} from 'expo-image' 4 4 import { ··· 201 201 const shareDialogControl = useDialogControl() 202 202 203 203 const shortenLink = useShortenLink() 204 - const [link, setLink] = React.useState<string>() 205 - const [imageLoaded, setImageLoaded] = React.useState(false) 204 + const [link, setLink] = useState<string>() 205 + const [imageLoaded, setImageLoaded] = useState(false) 206 206 207 - React.useEffect(() => { 207 + useEffect(() => { 208 208 ax.metric('starterPack:opened', { 209 209 starterPack: starterPack.uri, 210 210 }) 211 211 }, [ax, starterPack.uri]) 212 212 213 - const onOpenShareDialog = React.useCallback(() => { 213 + const onOpenShareDialog = useCallback(() => { 214 214 const rkey = new AtUri(starterPack.uri).rkey 215 215 shortenLink(makeStarterPackLink(starterPack.creator.did, rkey)).then( 216 216 res => { ··· 227 227 shareDialogControl.open() 228 228 }, [shareDialogControl, shortenLink, starterPack]) 229 229 230 - React.useEffect(() => { 230 + useEffect(() => { 231 231 if (routeParams.new) { 232 232 onOpenShareDialog() 233 233 } ··· 316 316 const {requestSwitchToAccount} = useLoggedOutViewControls() 317 317 const {captureAction} = useProgressGuideControls() 318 318 319 - const [isProcessing, setIsProcessing] = React.useState(false) 319 + const [isProcessing, setIsProcessing] = useState(false) 320 320 321 321 const {record, creator} = starterPack 322 322 const isOwn = creator?.did === currentAccount?.did ··· 325 325 326 326 const navigation = useNavigation<NavigationProp>() 327 327 328 - React.useEffect(() => { 328 + useEffect(() => { 329 329 const onFocus = () => { 330 330 if (hasSession) return 331 331 setActiveStarterPack({ ··· 731 731 const t = useTheme() 732 732 const navigation = useNavigation<NavigationProp>() 733 733 const {gtMobile} = useBreakpoints() 734 - const [isProcessing, setIsProcessing] = React.useState(false) 734 + const [isProcessing, setIsProcessing] = useState(false) 735 735 736 736 const goBack = () => { 737 737 if (navigation.canGoBack()) {
+4 -4
src/screens/StarterPack/Wizard/State.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext, useReducer} from 'react' 2 2 import { 3 3 type AppBskyFeedDefs, 4 4 type AppBskyGraphDefs, ··· 41 41 42 42 type TStateContext = [State, (action: Action) => void] 43 43 44 - const StateContext = React.createContext<TStateContext>([ 44 + const StateContext = createContext<TStateContext>([ 45 45 {} as State, 46 46 (_: Action) => {}, 47 47 ]) 48 48 StateContext.displayName = 'StarterPackWizardStateContext' 49 - export const useWizardState = () => React.useContext(StateContext) 49 + export const useWizardState = () => useContext(StateContext) 50 50 51 51 function reducer(state: State, action: Action): State { 52 52 let updatedState = state ··· 157 157 } 158 158 } 159 159 160 - const [state, dispatch] = React.useReducer(reducer, null, createInitialState) 160 + const [state, dispatch] = useReducer(reducer, null, createInitialState) 161 161 162 162 return ( 163 163 <StateContext.Provider value={[state, dispatch]}>
+3 -3
src/screens/StarterPack/Wizard/index.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useEffect} from 'react' 2 2 import {Keyboard, View} from 'react-native' 3 3 import {KeyboardAwareScrollView} from 'react-native-keyboard-controller' 4 4 import {useSafeAreaInsets} from 'react-native-safe-area-context' ··· 179 179 }) 180 180 const parsed = parseStarterPackUri(currentStarterPack?.uri) 181 181 182 - React.useEffect(() => { 182 + useEffect(() => { 183 183 navigation.setOptions({ 184 184 gestureEnabled: false, 185 185 }) 186 186 }, [navigation]) 187 187 188 188 useFocusEffect( 189 - React.useCallback(() => { 189 + useCallback(() => { 190 190 setMinimalShellMode(true) 191 191 192 192 return () => {
+11 -11
src/screens/Topic.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useMemo, useState} from 'react' 2 2 import {type ListRenderItemInfo, View} from 'react-native' 3 3 import {type AppBskyFeedDefs} from '@atproto/api' 4 4 import {msg} from '@lingui/core/macro' ··· 39 39 const {topic} = route.params 40 40 const {_} = useLingui() 41 41 42 - const headerTitle = React.useMemo(() => { 42 + const headerTitle = useMemo(() => { 43 43 return enforceLen(decodeURIComponent(topic), 24, true, 'middle') 44 44 }, [topic]) 45 45 46 - const onShare = React.useCallback(() => { 46 + const onShare = useCallback(() => { 47 47 const url = new URL('https://bsky.app') 48 48 url.pathname = `/topic/${topic}` 49 49 shareUrl(url.toString()) 50 50 }, [topic]) 51 51 52 - const [activeTab, setActiveTab] = React.useState(0) 52 + const [activeTab, setActiveTab] = useState(0) 53 53 const setMinimalShellMode = useSetMinimalShellMode() 54 54 55 55 useFocusEffect( 56 - React.useCallback(() => { 56 + useCallback(() => { 57 57 setMinimalShellMode(false) 58 58 }, [setMinimalShellMode]), 59 59 ) 60 60 61 - const onPageSelected = React.useCallback( 61 + const onPageSelected = useCallback( 62 62 (index: number) => { 63 63 setMinimalShellMode(false) 64 64 setActiveTab(index) ··· 66 66 [setMinimalShellMode], 67 67 ) 68 68 69 - const sections = React.useMemo(() => { 69 + const sections = useMemo(() => { 70 70 return [ 71 71 { 72 72 title: _(msg`Top`), ··· 135 135 }) { 136 136 const {_} = useLingui() 137 137 const initialNumToRender = useInitialNumToRender() 138 - const [isPTR, setIsPTR] = React.useState(false) 138 + const [isPTR, setIsPTR] = useState(false) 139 139 const trackPostView = usePostViewTracking('Topic') 140 140 141 141 const { ··· 154 154 enabled: active, 155 155 }) 156 156 157 - const posts = React.useMemo(() => { 157 + const posts = useMemo(() => { 158 158 return data?.pages.flatMap(page => page.posts) || [] 159 159 }, [data]) 160 160 161 - const onRefresh = React.useCallback(async () => { 161 + const onRefresh = useCallback(async () => { 162 162 setIsPTR(true) 163 163 await refetch() 164 164 setIsPTR(false) 165 165 }, [refetch]) 166 166 167 - const onEndReached = React.useCallback(() => { 167 + const onEndReached = useCallback(() => { 168 168 if (isFetchingNextPage || !hasNextPage || error) return 169 169 fetchNextPage() 170 170 }, [isFetchingNextPage, hasNextPage, error, fetchNextPage])
+7 -7
src/state/a11y.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext, useEffect, useMemo, useState} from 'react' 2 2 import {AccessibilityInfo} from 'react-native' 3 3 4 4 import {IS_WEB} from '#/env' 5 5 import {PlatformInfo} from '../../modules/expo-bluesky-swiss-army' 6 6 7 - const Context = React.createContext({ 7 + const Context = createContext({ 8 8 reduceMotionEnabled: false, 9 9 screenReaderEnabled: false, 10 10 }) 11 11 Context.displayName = 'A11yContext' 12 12 13 13 export function useA11y() { 14 - return React.useContext(Context) 14 + return useContext(Context) 15 15 } 16 16 17 17 export function Provider({children}: React.PropsWithChildren<{}>) { 18 - const [reduceMotionEnabled, setReduceMotionEnabled] = React.useState(() => 18 + const [reduceMotionEnabled, setReduceMotionEnabled] = useState(() => 19 19 PlatformInfo.getIsReducedMotionEnabled(), 20 20 ) 21 - const [screenReaderEnabled, setScreenReaderEnabled] = React.useState(false) 21 + const [screenReaderEnabled, setScreenReaderEnabled] = useState(false) 22 22 23 - React.useEffect(() => { 23 + useEffect(() => { 24 24 const reduceMotionChangedSubscription = AccessibilityInfo.addEventListener( 25 25 'reduceMotionChanged', 26 26 enabled => { ··· 49 49 } 50 50 }, []) 51 51 52 - const ctx = React.useMemo(() => { 52 + const ctx = useMemo(() => { 53 53 return { 54 54 reduceMotionEnabled, 55 55 /**
+14 -10
src/state/cache/thread-mutes.tsx
··· 1 - import React, {useEffect} from 'react' 1 + import { 2 + createContext, 3 + useCallback, 4 + useContext, 5 + useEffect, 6 + useState, 7 + } from 'react' 2 8 3 9 import * as persisted from '#/state/persisted' 4 10 import {useAgent, useSession} from '../session' ··· 6 12 type StateContext = Map<string, boolean> 7 13 type SetStateContext = (uri: string, value: boolean) => void 8 14 9 - const stateContext = React.createContext<StateContext>(new Map()) 15 + const stateContext = createContext<StateContext>(new Map()) 10 16 stateContext.displayName = 'ThreadMutesStateContext' 11 - const setStateContext = React.createContext<SetStateContext>( 12 - (_: string) => false, 13 - ) 17 + const setStateContext = createContext<SetStateContext>((_: string) => false) 14 18 setStateContext.displayName = 'ThreadMutesSetStateContext' 15 19 16 20 export function Provider({children}: React.PropsWithChildren<{}>) { 17 - const [state, setState] = React.useState<StateContext>(() => new Map()) 21 + const [state, setState] = useState<StateContext>(() => new Map()) 18 22 19 - const setThreadMute = React.useCallback( 23 + const setThreadMute = useCallback( 20 24 (uri: string, value: boolean) => { 21 25 setState(prev => { 22 26 const next = new Map(prev) ··· 39 43 } 40 44 41 45 export function useMutedThreads() { 42 - return React.useContext(stateContext) 46 + return useContext(stateContext) 43 47 } 44 48 45 49 export function useIsThreadMuted(uri: string, defaultValue = false) { 46 - const state = React.useContext(stateContext) 50 + const state = useContext(stateContext) 47 51 return state.get(uri) ?? defaultValue 48 52 } 49 53 50 54 export function useSetThreadMute() { 51 - return React.useContext(setStateContext) 55 + return useContext(setStateContext) 52 56 } 53 57 54 58 function useMigrateMutes(setThreadMute: SetStateContext) {
+21 -14
src/state/dialogs/index.tsx
··· 1 - import React from 'react' 1 + import { 2 + createContext, 3 + useCallback, 4 + useContext, 5 + useMemo, 6 + useRef, 7 + useState, 8 + } from 'react' 2 9 3 10 import {type DialogControlRefProps} from '#/components/Dialog' 4 11 import {Provider as GlobalDialogsProvider} from '#/components/dialogs/Context' ··· 25 32 setFullyExpandedCount: React.Dispatch<React.SetStateAction<number>> 26 33 } 27 34 28 - const DialogContext = React.createContext<IDialogContext>({} as IDialogContext) 35 + const DialogContext = createContext<IDialogContext>({} as IDialogContext) 29 36 DialogContext.displayName = 'DialogContext' 30 37 31 - const DialogControlContext = React.createContext<IDialogControlContext>( 38 + const DialogControlContext = createContext<IDialogControlContext>( 32 39 {} as IDialogControlContext, 33 40 ) 34 41 DialogControlContext.displayName = 'DialogControlContext' ··· 37 44 * The number of dialogs that are fully expanded. This is used to determine the background color of the status bar 38 45 * on iOS. 39 46 */ 40 - const DialogFullyExpandedCountContext = React.createContext<number>(0) 47 + const DialogFullyExpandedCountContext = createContext<number>(0) 41 48 DialogFullyExpandedCountContext.displayName = 'DialogFullyExpandedCountContext' 42 49 43 50 export function useDialogStateContext() { 44 - return React.useContext(DialogContext) 51 + return useContext(DialogContext) 45 52 } 46 53 47 54 export function useDialogStateControlContext() { 48 - return React.useContext(DialogControlContext) 55 + return useContext(DialogControlContext) 49 56 } 50 57 51 58 /** The number of dialogs that are fully expanded */ 52 59 export function useDialogFullyExpandedCountContext() { 53 - return React.useContext(DialogFullyExpandedCountContext) 60 + return useContext(DialogFullyExpandedCountContext) 54 61 } 55 62 56 63 export function Provider({children}: React.PropsWithChildren<{}>) { 57 - const [fullyExpandedCount, setFullyExpandedCount] = React.useState(0) 64 + const [fullyExpandedCount, setFullyExpandedCount] = useState(0) 58 65 59 - const activeDialogs = React.useRef< 66 + const activeDialogs = useRef< 60 67 Map<string, React.MutableRefObject<DialogControlRefProps>> 61 68 >(new Map()) 62 - const openDialogs = React.useRef<Set<string>>(new Set()) 69 + const openDialogs = useRef<Set<string>>(new Set()) 63 70 64 - const closeAllDialogs = React.useCallback(() => { 71 + const closeAllDialogs = useCallback(() => { 65 72 if (IS_WEB) { 66 73 openDialogs.current.forEach(id => { 67 74 const dialog = activeDialogs.current.get(id) ··· 75 82 } 76 83 }, []) 77 84 78 - const setDialogIsOpen = React.useCallback((id: string, isOpen: boolean) => { 85 + const setDialogIsOpen = useCallback((id: string, isOpen: boolean) => { 79 86 if (isOpen) { 80 87 openDialogs.current.add(id) 81 88 } else { ··· 83 90 } 84 91 }, []) 85 92 86 - const context = React.useMemo<IDialogContext>( 93 + const context = useMemo<IDialogContext>( 87 94 () => ({ 88 95 activeDialogs, 89 96 openDialogs, 90 97 }), 91 98 [activeDialogs, openDialogs], 92 99 ) 93 - const controls = React.useMemo( 100 + const controls = useMemo( 94 101 () => ({ 95 102 closeAllDialogs, 96 103 setDialogIsOpen,
+6 -6
src/state/home-badge.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext, useState} from 'react' 2 2 3 3 type StateContext = boolean 4 4 type ApiContext = (hasNew: boolean) => void 5 5 6 - const stateContext = React.createContext<StateContext>(false) 6 + const stateContext = createContext<StateContext>(false) 7 7 stateContext.displayName = 'HomeBadgeStateContext' 8 - const apiContext = React.createContext<ApiContext>((_: boolean) => {}) 8 + const apiContext = createContext<ApiContext>((_: boolean) => {}) 9 9 apiContext.displayName = 'HomeBadgeApiContext' 10 10 11 11 export function Provider({children}: React.PropsWithChildren<{}>) { 12 - const [state, setState] = React.useState(false) 12 + const [state, setState] = useState(false) 13 13 return ( 14 14 <stateContext.Provider value={state}> 15 15 <apiContext.Provider value={setState}>{children}</apiContext.Provider> ··· 18 18 } 19 19 20 20 export function useHomeBadge() { 21 - return React.useContext(stateContext) 21 + return useContext(stateContext) 22 22 } 23 23 24 24 export function useSetHomeBadge() { 25 - return React.useContext(apiContext) 25 + return useContext(apiContext) 26 26 }
+8 -10
src/state/lightbox.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext, useMemo, useState} from 'react' 2 2 import {nanoid} from 'nanoid/non-secure' 3 3 4 4 import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' ··· 10 10 index: number 11 11 } 12 12 13 - const LightboxContext = React.createContext<{ 13 + const LightboxContext = createContext<{ 14 14 activeLightbox: Lightbox | null 15 15 }>({ 16 16 activeLightbox: null, 17 17 }) 18 18 LightboxContext.displayName = 'LightboxContext' 19 19 20 - const LightboxControlContext = React.createContext<{ 20 + const LightboxControlContext = createContext<{ 21 21 openLightbox: (lightbox: Omit<Lightbox, 'id'>) => void 22 22 closeLightbox: () => boolean 23 23 }>({ ··· 27 27 LightboxControlContext.displayName = 'LightboxControlContext' 28 28 29 29 export function Provider({children}: React.PropsWithChildren<{}>) { 30 - const [activeLightbox, setActiveLightbox] = React.useState<Lightbox | null>( 31 - null, 32 - ) 30 + const [activeLightbox, setActiveLightbox] = useState<Lightbox | null>(null) 33 31 34 32 const openLightbox = useNonReactiveCallback( 35 33 (lightbox: Omit<Lightbox, 'id'>) => { ··· 51 49 return wasActive 52 50 }) 53 51 54 - const state = React.useMemo( 52 + const state = useMemo( 55 53 () => ({ 56 54 activeLightbox, 57 55 }), 58 56 [activeLightbox], 59 57 ) 60 58 61 - const methods = React.useMemo( 59 + const methods = useMemo( 62 60 () => ({ 63 61 openLightbox, 64 62 closeLightbox, ··· 76 74 } 77 75 78 76 export function useLightbox() { 79 - return React.useContext(LightboxContext) 77 + return useContext(LightboxContext) 80 78 } 81 79 82 80 export function useLightboxControls() { 83 - return React.useContext(LightboxControlContext) 81 + return useContext(LightboxControlContext) 84 82 }
+11 -4
src/state/messages/convo/index.tsx
··· 1 - import React, {useContext, useState, useSyncExternalStore} from 'react' 1 + import { 2 + createContext, 3 + useCallback, 4 + useContext, 5 + useEffect, 6 + useState, 7 + useSyncExternalStore, 8 + } from 'react' 2 9 import {type ChatBskyConvoDefs} from '@atproto/api' 3 10 import {useFocusEffect} from '@react-navigation/native' 4 11 import {useQueryClient} from '@tanstack/react-query' ··· 25 32 26 33 export * from '#/state/messages/convo/util' 27 34 28 - const ChatContext = React.createContext<ConvoState | null>(null) 35 + const ChatContext = createContext<ConvoState | null>(null) 29 36 ChatContext.displayName = 'ChatContext' 30 37 31 38 export function useConvo() { ··· 82 89 const appState = useAppState() 83 90 const isActive = appState === 'active' 84 91 useFocusEffect( 85 - React.useCallback(() => { 92 + useCallback(() => { 86 93 if (isActive) { 87 94 convo.resume() 88 95 markAsRead({convoId}) ··· 95 102 }, [isActive, convo, convoId, markAsRead]), 96 103 ) 97 104 98 - React.useEffect(() => { 105 + useEffect(() => { 99 106 return convo.on(event => { 100 107 switch (event.type) { 101 108 case 'invalidate-block-state': {
+5 -7
src/state/messages/current-convo-id.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext, useMemo, useState} from 'react' 2 2 3 - const CurrentConvoIdContext = React.createContext<{ 3 + const CurrentConvoIdContext = createContext<{ 4 4 currentConvoId: string | undefined 5 5 setCurrentConvoId: (convoId: string | undefined) => void 6 6 }>({ ··· 10 10 CurrentConvoIdContext.displayName = 'CurrentConvoIdContext' 11 11 12 12 export function useCurrentConvoId() { 13 - const ctx = React.useContext(CurrentConvoIdContext) 13 + const ctx = useContext(CurrentConvoIdContext) 14 14 if (!ctx) { 15 15 throw new Error( 16 16 'useCurrentConvoId must be used within a CurrentConvoIdProvider', ··· 24 24 }: { 25 25 children: React.ReactNode 26 26 }) { 27 - const [currentConvoId, setCurrentConvoId] = React.useState< 28 - string | undefined 29 - >() 30 - const ctx = React.useMemo( 27 + const [currentConvoId, setCurrentConvoId] = useState<string | undefined>() 28 + const ctx = useMemo( 31 29 () => ({currentConvoId, setCurrentConvoId}), 32 30 [currentConvoId], 33 31 )
+6 -8
src/state/messages/events/index.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext, useEffect, useState} from 'react' 2 2 import {AppState} from 'react-native' 3 3 4 4 import {MessagesEventBus} from '#/state/messages/events/agent' 5 5 import {useAgent, useSession} from '#/state/session' 6 6 7 - const MessagesEventBusContext = React.createContext<MessagesEventBus | null>( 8 - null, 9 - ) 7 + const MessagesEventBusContext = createContext<MessagesEventBus | null>(null) 10 8 MessagesEventBusContext.displayName = 'MessagesEventBusContext' 11 9 12 10 export function useMessagesEventBus() { 13 - const ctx = React.useContext(MessagesEventBusContext) 11 + const ctx = useContext(MessagesEventBusContext) 14 12 if (!ctx) { 15 13 throw new Error( 16 14 'useMessagesEventBus must be used within a MessagesEventBusProvider', ··· 45 43 children: React.ReactNode 46 44 }) { 47 45 const agent = useAgent() 48 - const [bus] = React.useState( 46 + const [bus] = useState( 49 47 () => 50 48 new MessagesEventBus({ 51 49 agent, 52 50 }), 53 51 ) 54 52 55 - React.useEffect(() => { 53 + useEffect(() => { 56 54 bus.resume() 57 55 58 56 return () => { ··· 60 58 } 61 59 }, [bus]) 62 60 63 - React.useEffect(() => { 61 + useEffect(() => { 64 62 const handleAppStateChange = (nextAppState: string) => { 65 63 if (nextAppState === 'active') { 66 64 bus.resume()
-2
src/state/messages/index.tsx
··· 1 - import type React from 'react' 2 - 3 1 import {CurrentConvoIdProvider} from '#/state/messages/current-convo-id' 4 2 import {MessagesEventBusProvider} from '#/state/messages/events' 5 3 import {ListConvosProvider} from '#/state/queries/messages/list-conversations'
+10 -3
src/state/messages/message-drafts.tsx
··· 1 - import React, {useEffect, useMemo, useReducer, useRef} from 'react' 1 + import { 2 + createContext, 3 + useContext, 4 + useEffect, 5 + useMemo, 6 + useReducer, 7 + useRef, 8 + } from 'react' 2 9 3 10 import {useCurrentConvoId} from './current-convo-id' 4 11 5 - const MessageDraftsContext = React.createContext<{ 12 + const MessageDraftsContext = createContext<{ 6 13 state: State 7 14 dispatch: React.Dispatch<Actions> 8 15 } | null>(null) 9 16 MessageDraftsContext.displayName = 'MessageDraftsContext' 10 17 11 18 function useMessageDraftsContext() { 12 - const ctx = React.useContext(MessageDraftsContext) 19 + const ctx = useContext(MessageDraftsContext) 13 20 if (!ctx) { 14 21 throw new Error( 15 22 'useMessageDrafts must be used within a MessageDraftsContext',
+8 -8
src/state/modals/index.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext, useMemo, useState} from 'react' 2 2 3 3 import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' 4 4 ··· 25 25 // Lists 26 26 | UserAddRemoveListsModal 27 27 28 - const ModalContext = React.createContext<{ 28 + const ModalContext = createContext<{ 29 29 isModalActive: boolean 30 30 activeModals: Modal[] 31 31 }>({ ··· 34 34 }) 35 35 ModalContext.displayName = 'ModalContext' 36 36 37 - const ModalControlContext = React.createContext<{ 37 + const ModalControlContext = createContext<{ 38 38 openModal: (modal: Modal) => void 39 39 closeModal: () => boolean 40 40 closeAllModals: () => boolean ··· 46 46 ModalControlContext.displayName = 'ModalControlContext' 47 47 48 48 export function Provider({children}: React.PropsWithChildren<{}>) { 49 - const [activeModals, setActiveModals] = React.useState<Modal[]>([]) 49 + const [activeModals, setActiveModals] = useState<Modal[]>([]) 50 50 51 51 const openModal = useNonReactiveCallback((modal: Modal) => { 52 52 setActiveModals(modals => [...modals, modal]) ··· 66 66 return wasActive 67 67 }) 68 68 69 - const state = React.useMemo( 69 + const state = useMemo( 70 70 () => ({ 71 71 isModalActive: activeModals.length > 0, 72 72 activeModals, ··· 74 74 [activeModals], 75 75 ) 76 76 77 - const methods = React.useMemo( 77 + const methods = useMemo( 78 78 () => ({ 79 79 openModal, 80 80 closeModal, ··· 96 96 * @deprecated use the dialog system from `#/components/Dialog.tsx` 97 97 */ 98 98 export function useModals() { 99 - return React.useContext(ModalContext) 99 + return useContext(ModalContext) 100 100 } 101 101 102 102 /** 103 103 * @deprecated use the dialog system from `#/components/Dialog.tsx` 104 104 */ 105 105 export function useModalControls() { 106 - return React.useContext(ModalControlContext) 106 + return useContext(ModalControlContext) 107 107 }
+14 -10
src/state/preferences/alt-text-required.tsx
··· 1 - import React from 'react' 1 + import { 2 + createContext, 3 + useCallback, 4 + useContext, 5 + useEffect, 6 + useState, 7 + } from 'react' 2 8 3 9 import * as persisted from '#/state/persisted' 4 10 5 11 type StateContext = persisted.Schema['requireAltTextEnabled'] 6 12 type SetContext = (v: persisted.Schema['requireAltTextEnabled']) => void 7 13 8 - const stateContext = React.createContext<StateContext>( 14 + const stateContext = createContext<StateContext>( 9 15 persisted.defaults.requireAltTextEnabled, 10 16 ) 11 17 stateContext.displayName = 'AltTextRequiredStateContext' 12 - const setContext = React.createContext<SetContext>( 18 + const setContext = createContext<SetContext>( 13 19 (_: persisted.Schema['requireAltTextEnabled']) => {}, 14 20 ) 15 21 setContext.displayName = 'AltTextRequiredSetContext' 16 22 17 23 export function Provider({children}: React.PropsWithChildren<{}>) { 18 - const [state, setState] = React.useState( 19 - persisted.get('requireAltTextEnabled'), 20 - ) 24 + const [state, setState] = useState(persisted.get('requireAltTextEnabled')) 21 25 22 - const setStateWrapped = React.useCallback( 26 + const setStateWrapped = useCallback( 23 27 (requireAltTextEnabled: persisted.Schema['requireAltTextEnabled']) => { 24 28 setState(requireAltTextEnabled) 25 29 persisted.write('requireAltTextEnabled', requireAltTextEnabled) ··· 27 31 [setState], 28 32 ) 29 33 30 - React.useEffect(() => { 34 + useEffect(() => { 31 35 return persisted.onUpdate( 32 36 'requireAltTextEnabled', 33 37 nextRequireAltTextEnabled => { ··· 46 50 } 47 51 48 52 export function useRequireAltTextEnabled() { 49 - return React.useContext(stateContext) 53 + return useContext(stateContext) 50 54 } 51 55 52 56 export function useSetRequireAltTextEnabled() { 53 - return React.useContext(setContext) 57 + return useContext(setContext) 54 58 }
+14 -10
src/state/preferences/autoplay.tsx
··· 1 - import React from 'react' 1 + import { 2 + createContext, 3 + useCallback, 4 + useContext, 5 + useEffect, 6 + useState, 7 + } from 'react' 2 8 3 9 import * as persisted from '#/state/persisted' 4 10 5 11 type StateContext = boolean 6 12 type SetContext = (v: boolean) => void 7 13 8 - const stateContext = React.createContext<StateContext>( 14 + const stateContext = createContext<StateContext>( 9 15 Boolean(persisted.defaults.disableAutoplay), 10 16 ) 11 17 stateContext.displayName = 'AutoplayStateContext' 12 - const setContext = React.createContext<SetContext>((_: boolean) => {}) 18 + const setContext = createContext<SetContext>((_: boolean) => {}) 13 19 setContext.displayName = 'AutoplaySetContext' 14 20 15 21 export function Provider({children}: {children: React.ReactNode}) { 16 - const [state, setState] = React.useState( 17 - Boolean(persisted.get('disableAutoplay')), 18 - ) 22 + const [state, setState] = useState(Boolean(persisted.get('disableAutoplay'))) 19 23 20 - const setStateWrapped = React.useCallback( 24 + const setStateWrapped = useCallback( 21 25 (autoplayDisabled: persisted.Schema['disableAutoplay']) => { 22 26 setState(Boolean(autoplayDisabled)) 23 27 persisted.write('disableAutoplay', autoplayDisabled) ··· 25 29 [setState], 26 30 ) 27 31 28 - React.useEffect(() => { 32 + useEffect(() => { 29 33 return persisted.onUpdate('disableAutoplay', nextDisableAutoplay => { 30 34 setState(Boolean(nextDisableAutoplay)) 31 35 }) ··· 40 44 ) 41 45 } 42 46 43 - export const useAutoplayDisabled = () => React.useContext(stateContext) 44 - export const useSetAutoplayDisabled = () => React.useContext(setContext) 47 + export const useAutoplayDisabled = () => useContext(stateContext) 48 + export const useSetAutoplayDisabled = () => useContext(setContext)
+14 -10
src/state/preferences/disable-haptics.tsx
··· 1 - import React from 'react' 1 + import { 2 + createContext, 3 + useCallback, 4 + useContext, 5 + useEffect, 6 + useState, 7 + } from 'react' 2 8 3 9 import * as persisted from '#/state/persisted' 4 10 5 11 type StateContext = boolean 6 12 type SetContext = (v: boolean) => void 7 13 8 - const stateContext = React.createContext<StateContext>( 14 + const stateContext = createContext<StateContext>( 9 15 Boolean(persisted.defaults.disableHaptics), 10 16 ) 11 17 stateContext.displayName = 'DisableHapticsStateContext' 12 - const setContext = React.createContext<SetContext>((_: boolean) => {}) 18 + const setContext = createContext<SetContext>((_: boolean) => {}) 13 19 setContext.displayName = 'DisableHapticsSetContext' 14 20 15 21 export function Provider({children}: {children: React.ReactNode}) { 16 - const [state, setState] = React.useState( 17 - Boolean(persisted.get('disableHaptics')), 18 - ) 22 + const [state, setState] = useState(Boolean(persisted.get('disableHaptics'))) 19 23 20 - const setStateWrapped = React.useCallback( 24 + const setStateWrapped = useCallback( 21 25 (hapticsEnabled: persisted.Schema['disableHaptics']) => { 22 26 setState(Boolean(hapticsEnabled)) 23 27 persisted.write('disableHaptics', hapticsEnabled) ··· 25 29 [setState], 26 30 ) 27 31 28 - React.useEffect(() => { 32 + useEffect(() => { 29 33 return persisted.onUpdate('disableHaptics', nextDisableHaptics => { 30 34 setState(Boolean(nextDisableHaptics)) 31 35 }) ··· 40 44 ) 41 45 } 42 46 43 - export const useHapticsDisabled = () => React.useContext(stateContext) 44 - export const useSetHapticsDisabled = () => React.useContext(setContext) 47 + export const useHapticsDisabled = () => useContext(stateContext) 48 + export const useSetHapticsDisabled = () => useContext(setContext)
+14 -8
src/state/preferences/external-embeds-prefs.tsx
··· 1 - import React from 'react' 1 + import { 2 + createContext, 3 + useCallback, 4 + useContext, 5 + useEffect, 6 + useState, 7 + } from 'react' 2 8 3 9 import {type EmbedPlayerSource} from '#/lib/strings/embed-player' 4 10 import * as persisted from '#/state/persisted' ··· 9 15 value: 'show' | 'hide' | undefined, 10 16 ) => void 11 17 12 - const stateContext = React.createContext<StateContext>( 18 + const stateContext = createContext<StateContext>( 13 19 persisted.defaults.externalEmbeds, 14 20 ) 15 21 stateContext.displayName = 'ExternalEmbedsPrefsStateContext' 16 - const setContext = React.createContext<SetContext>({} as SetContext) 22 + const setContext = createContext<SetContext>({} as SetContext) 17 23 setContext.displayName = 'ExternalEmbedsPrefsSetContext' 18 24 19 25 export function Provider({children}: React.PropsWithChildren<{}>) { 20 - const [state, setState] = React.useState(persisted.get('externalEmbeds')) 26 + const [state, setState] = useState(persisted.get('externalEmbeds')) 21 27 22 - const setStateWrapped = React.useCallback( 28 + const setStateWrapped = useCallback( 23 29 (source: EmbedPlayerSource, value: 'show' | 'hide' | undefined) => { 24 30 setState(prev => { 25 31 persisted.write('externalEmbeds', { ··· 36 42 [setState], 37 43 ) 38 44 39 - React.useEffect(() => { 45 + useEffect(() => { 40 46 return persisted.onUpdate('externalEmbeds', nextExternalEmbeds => { 41 47 setState(nextExternalEmbeds) 42 48 }) ··· 52 58 } 53 59 54 60 export function useExternalEmbedsPrefs() { 55 - return React.useContext(stateContext) 61 + return useContext(stateContext) 56 62 } 57 63 58 64 export function useSetExternalEmbedPref() { 59 - return React.useContext(setContext) 65 + return useContext(setContext) 60 66 }
+16 -11
src/state/preferences/hidden-posts.tsx
··· 1 - import React from 'react' 1 + import { 2 + createContext, 3 + useCallback, 4 + useContext, 5 + useEffect, 6 + useMemo, 7 + useState, 8 + } from 'react' 2 9 3 10 import * as persisted from '#/state/persisted' 4 11 ··· 11 18 unhidePost: ({uri}: {uri: string}) => void 12 19 } 13 20 14 - const stateContext = React.createContext<StateContext>( 15 - persisted.defaults.hiddenPosts, 16 - ) 21 + const stateContext = createContext<StateContext>(persisted.defaults.hiddenPosts) 17 22 stateContext.displayName = 'HiddenPostsStateContext' 18 - const apiContext = React.createContext<ApiContext>({ 23 + const apiContext = createContext<ApiContext>({ 19 24 hidePost: () => {}, 20 25 unhidePost: () => {}, 21 26 }) 22 27 apiContext.displayName = 'HiddenPostsApiContext' 23 28 24 29 export function Provider({children}: React.PropsWithChildren<{}>) { 25 - const [state, setState] = React.useState(persisted.get('hiddenPosts')) 30 + const [state, setState] = useState(persisted.get('hiddenPosts')) 26 31 27 - const setStateWrapped = React.useCallback( 32 + const setStateWrapped = useCallback( 28 33 (fn: SetStateCb) => { 29 34 const s = fn(persisted.get('hiddenPosts')) 30 35 setState(s) ··· 33 38 [setState], 34 39 ) 35 40 36 - const api = React.useMemo( 41 + const api = useMemo( 37 42 () => ({ 38 43 hidePost: ({uri}: {uri: string}) => { 39 44 setStateWrapped(s => [...(s || []), uri]) ··· 45 50 [setStateWrapped], 46 51 ) 47 52 48 - React.useEffect(() => { 53 + useEffect(() => { 49 54 return persisted.onUpdate('hiddenPosts', nextHiddenPosts => { 50 55 setState(nextHiddenPosts) 51 56 }) ··· 59 64 } 60 65 61 66 export function useHiddenPosts() { 62 - return React.useContext(stateContext) 67 + return useContext(stateContext) 63 68 } 64 69 65 70 export function useHiddenPostsApi() { 66 - return React.useContext(apiContext) 71 + return useContext(apiContext) 67 72 }
+14 -8
src/state/preferences/in-app-browser.tsx
··· 1 - import React from 'react' 1 + import { 2 + createContext, 3 + useCallback, 4 + useContext, 5 + useEffect, 6 + useState, 7 + } from 'react' 2 8 3 9 import * as persisted from '#/state/persisted' 4 10 5 11 type StateContext = persisted.Schema['useInAppBrowser'] 6 12 type SetContext = (v: persisted.Schema['useInAppBrowser']) => void 7 13 8 - const stateContext = React.createContext<StateContext>( 14 + const stateContext = createContext<StateContext>( 9 15 persisted.defaults.useInAppBrowser, 10 16 ) 11 17 stateContext.displayName = 'InAppBrowserStateContext' 12 - const setContext = React.createContext<SetContext>( 18 + const setContext = createContext<SetContext>( 13 19 (_: persisted.Schema['useInAppBrowser']) => {}, 14 20 ) 15 21 setContext.displayName = 'InAppBrowserSetContext' 16 22 17 23 export function Provider({children}: React.PropsWithChildren<{}>) { 18 - const [state, setState] = React.useState(persisted.get('useInAppBrowser')) 24 + const [state, setState] = useState(persisted.get('useInAppBrowser')) 19 25 20 - const setStateWrapped = React.useCallback( 26 + const setStateWrapped = useCallback( 21 27 (inAppBrowser: persisted.Schema['useInAppBrowser']) => { 22 28 setState(inAppBrowser) 23 29 persisted.write('useInAppBrowser', inAppBrowser) ··· 25 31 [setState], 26 32 ) 27 33 28 - React.useEffect(() => { 34 + useEffect(() => { 29 35 return persisted.onUpdate('useInAppBrowser', nextUseInAppBrowser => { 30 36 setState(nextUseInAppBrowser) 31 37 }) ··· 41 47 } 42 48 43 49 export function useInAppBrowser() { 44 - return React.useContext(stateContext) 50 + return useContext(stateContext) 45 51 } 46 52 47 53 export function useSetInAppBrowser() { 48 - return React.useContext(setContext) 54 + return useContext(setContext) 49 55 }
-2
src/state/preferences/index.tsx
··· 1 - import type React from 'react' 2 - 3 1 import {Provider as AltTextRequiredProvider} from './alt-text-required' 4 2 import {Provider as AutoplayProvider} from './autoplay' 5 3 import {Provider as DisableHapticsProvider} from './disable-haptics'
+13 -9
src/state/preferences/kawaii.tsx
··· 1 - import React from 'react' 1 + import { 2 + createContext, 3 + useCallback, 4 + useContext, 5 + useEffect, 6 + useState, 7 + } from 'react' 2 8 3 9 import * as persisted from '#/state/persisted' 4 10 import {IS_WEB} from '#/env' 5 11 6 12 type StateContext = persisted.Schema['kawaii'] 7 13 8 - const stateContext = React.createContext<StateContext>( 9 - persisted.defaults.kawaii, 10 - ) 14 + const stateContext = createContext<StateContext>(persisted.defaults.kawaii) 11 15 stateContext.displayName = 'KawaiiStateContext' 12 16 13 17 export function Provider({children}: React.PropsWithChildren<{}>) { 14 - const [state, setState] = React.useState(persisted.get('kawaii')) 18 + const [state, setState] = useState(persisted.get('kawaii')) 15 19 16 - const setStateWrapped = React.useCallback( 20 + const setStateWrapped = useCallback( 17 21 (kawaii: persisted.Schema['kawaii']) => { 18 22 setState(kawaii) 19 23 persisted.write('kawaii', kawaii) ··· 21 25 [setState], 22 26 ) 23 27 24 - React.useEffect(() => { 28 + useEffect(() => { 25 29 return persisted.onUpdate('kawaii', nextKawaii => { 26 30 setState(nextKawaii) 27 31 }) 28 32 }, [setStateWrapped]) 29 33 30 - React.useEffect(() => { 34 + useEffect(() => { 31 35 // dumb and stupid but it's web only so just refresh the page if you want to change it 32 36 33 37 if (IS_WEB) { ··· 47 51 } 48 52 49 53 export function useKawaiiMode() { 50 - return React.useContext(stateContext) 54 + return useContext(stateContext) 51 55 }
+3 -3
src/state/preferences/label-defs.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext} from 'react' 2 2 import { 3 3 type AppBskyLabelerDefs, 4 4 type InterpretedLabelValueDefinition, ··· 11 11 labelers: AppBskyLabelerDefs.LabelerViewDetailed[] 12 12 } 13 13 14 - const stateContext = React.createContext<StateContext>({ 14 + const stateContext = createContext<StateContext>({ 15 15 labelDefs: {}, 16 16 labelers: [], 17 17 }) ··· 23 23 } 24 24 25 25 export function useLabelDefinitions() { 26 - return React.useContext(stateContext) 26 + return useContext(stateContext) 27 27 }
+16 -9
src/state/preferences/languages.tsx
··· 1 - import React from 'react' 1 + import { 2 + createContext, 3 + useCallback, 4 + useContext, 5 + useEffect, 6 + useMemo, 7 + useState, 8 + } from 'react' 2 9 3 10 import {type AppLanguage} from '#/locale/languages' 4 11 import * as persisted from '#/state/persisted' ··· 16 23 setAppLanguage: (code2: AppLanguage) => void 17 24 } 18 25 19 - const stateContext = React.createContext<StateContext>( 26 + const stateContext = createContext<StateContext>( 20 27 persisted.defaults.languagePrefs, 21 28 ) 22 29 stateContext.displayName = 'LanguagePrefsStateContext' 23 - const apiContext = React.createContext<ApiContext>({ 30 + const apiContext = createContext<ApiContext>({ 24 31 setPrimaryLanguage: (_: string) => {}, 25 32 setPostLanguage: (_: string) => {}, 26 33 setContentLanguages: (_: string[]) => {}, ··· 30 37 apiContext.displayName = 'LanguagePrefsApiContext' 31 38 32 39 export function Provider({children}: React.PropsWithChildren<{}>) { 33 - const [state, setState] = React.useState(() => persisted.get('languagePrefs')) 40 + const [state, setState] = useState(() => persisted.get('languagePrefs')) 34 41 35 - const setStateWrapped = React.useCallback( 42 + const setStateWrapped = useCallback( 36 43 (fn: SetStateCb) => { 37 44 const s = fn(persisted.get('languagePrefs')) 38 45 setState(s) ··· 41 48 [setState], 42 49 ) 43 50 44 - React.useEffect(() => { 51 + useEffect(() => { 45 52 return persisted.onUpdate('languagePrefs', nextLanguagePrefs => { 46 53 setState(nextLanguagePrefs) 47 54 }) 48 55 }, [setStateWrapped]) 49 56 50 - const api = React.useMemo( 57 + const api = useMemo( 51 58 () => ({ 52 59 setPrimaryLanguage(code2: string) { 53 60 setStateWrapped(s => ({...s, primaryLanguage: code2})) ··· 102 109 } 103 110 104 111 export function useLanguagePrefs() { 105 - return React.useContext(stateContext) 112 + return useContext(stateContext) 106 113 } 107 114 108 115 export function useLanguagePrefsApi() { 109 - return React.useContext(apiContext) 116 + return useContext(apiContext) 110 117 } 111 118 112 119 export function getContentLanguages() {
+14 -10
src/state/preferences/large-alt-badge.tsx
··· 1 - import React from 'react' 1 + import { 2 + createContext, 3 + useCallback, 4 + useContext, 5 + useEffect, 6 + useState, 7 + } from 'react' 2 8 3 9 import * as persisted from '#/state/persisted' 4 10 5 11 type StateContext = persisted.Schema['largeAltBadgeEnabled'] 6 12 type SetContext = (v: persisted.Schema['largeAltBadgeEnabled']) => void 7 13 8 - const stateContext = React.createContext<StateContext>( 14 + const stateContext = createContext<StateContext>( 9 15 persisted.defaults.largeAltBadgeEnabled, 10 16 ) 11 17 stateContext.displayName = 'LargeAltBadgeStateContext' 12 - const setContext = React.createContext<SetContext>( 18 + const setContext = createContext<SetContext>( 13 19 (_: persisted.Schema['largeAltBadgeEnabled']) => {}, 14 20 ) 15 21 setContext.displayName = 'LargeAltBadgeSetContext' 16 22 17 23 export function Provider({children}: React.PropsWithChildren<{}>) { 18 - const [state, setState] = React.useState( 19 - persisted.get('largeAltBadgeEnabled'), 20 - ) 24 + const [state, setState] = useState(persisted.get('largeAltBadgeEnabled')) 21 25 22 - const setStateWrapped = React.useCallback( 26 + const setStateWrapped = useCallback( 23 27 (largeAltBadgeEnabled: persisted.Schema['largeAltBadgeEnabled']) => { 24 28 setState(largeAltBadgeEnabled) 25 29 persisted.write('largeAltBadgeEnabled', largeAltBadgeEnabled) ··· 27 31 [setState], 28 32 ) 29 33 30 - React.useEffect(() => { 34 + useEffect(() => { 31 35 return persisted.onUpdate( 32 36 'largeAltBadgeEnabled', 33 37 nextLargeAltBadgeEnabled => { ··· 46 50 } 47 51 48 52 export function useLargeAltBadgeEnabled() { 49 - return React.useContext(stateContext) 53 + return useContext(stateContext) 50 54 } 51 55 52 56 export function useSetLargeAltBadgeEnabled() { 53 - return React.useContext(setContext) 57 + return useContext(setContext) 54 58 }
+14 -10
src/state/preferences/subtitles.tsx
··· 1 - import React from 'react' 1 + import { 2 + createContext, 3 + useCallback, 4 + useContext, 5 + useEffect, 6 + useState, 7 + } from 'react' 2 8 3 9 import * as persisted from '#/state/persisted' 4 10 5 11 type StateContext = boolean 6 12 type SetContext = (v: boolean) => void 7 13 8 - const stateContext = React.createContext<StateContext>( 14 + const stateContext = createContext<StateContext>( 9 15 Boolean(persisted.defaults.subtitlesEnabled), 10 16 ) 11 17 stateContext.displayName = 'SubtitlesStateContext' 12 - const setContext = React.createContext<SetContext>((_: boolean) => {}) 18 + const setContext = createContext<SetContext>((_: boolean) => {}) 13 19 setContext.displayName = 'SubtitlesSetContext' 14 20 15 21 export function Provider({children}: {children: React.ReactNode}) { 16 - const [state, setState] = React.useState( 17 - Boolean(persisted.get('subtitlesEnabled')), 18 - ) 22 + const [state, setState] = useState(Boolean(persisted.get('subtitlesEnabled'))) 19 23 20 - const setStateWrapped = React.useCallback( 24 + const setStateWrapped = useCallback( 21 25 (subtitlesEnabled: persisted.Schema['subtitlesEnabled']) => { 22 26 setState(Boolean(subtitlesEnabled)) 23 27 persisted.write('subtitlesEnabled', subtitlesEnabled) ··· 25 29 [setState], 26 30 ) 27 31 28 - React.useEffect(() => { 32 + useEffect(() => { 29 33 return persisted.onUpdate('subtitlesEnabled', nextSubtitlesEnabled => { 30 34 setState(Boolean(nextSubtitlesEnabled)) 31 35 }) ··· 40 44 ) 41 45 } 42 46 43 - export const useSubtitlesEnabled = () => React.useContext(stateContext) 44 - export const useSetSubtitlesEnabled = () => React.useContext(setContext) 47 + export const useSubtitlesEnabled = () => useContext(stateContext) 48 + export const useSetSubtitlesEnabled = () => useContext(setContext)
+17 -10
src/state/preferences/trending.tsx
··· 1 - import React from 'react' 1 + import { 2 + createContext, 3 + useCallback, 4 + useContext, 5 + useEffect, 6 + useMemo, 7 + useState, 8 + } from 'react' 2 9 3 10 import * as persisted from '#/state/persisted' 4 11 ··· 18 25 ): void 19 26 } 20 27 21 - const StateContext = React.createContext<StateContext>({ 28 + const StateContext = createContext<StateContext>({ 22 29 trendingDisabled: Boolean(persisted.defaults.trendingDisabled), 23 30 trendingVideoDisabled: Boolean(persisted.defaults.trendingVideoDisabled), 24 31 }) 25 32 StateContext.displayName = 'TrendingStateContext' 26 - const ApiContext = React.createContext<ApiContext>({ 33 + const ApiContext = createContext<ApiContext>({ 27 34 setTrendingDisabled() {}, 28 35 setTrendingVideoDisabled() {}, 29 36 }) 30 37 ApiContext.displayName = 'TrendingApiContext' 31 38 32 39 function usePersistedBooleanValue<T extends keyof persisted.Schema>(key: T) { 33 - const [value, _set] = React.useState(() => { 40 + const [value, _set] = useState(() => { 34 41 return Boolean(persisted.get(key)) 35 42 }) 36 - const set = React.useCallback< 43 + const set = useCallback< 37 44 (value: Exclude<persisted.Schema[T], undefined>) => void 38 45 >( 39 46 hidden => { ··· 42 49 }, 43 50 [key, _set], 44 51 ) 45 - React.useEffect(() => { 52 + useEffect(() => { 46 53 return persisted.onUpdate(key, hidden => { 47 54 _set(Boolean(hidden)) 48 55 }) ··· 60 67 /* 61 68 * Context 62 69 */ 63 - const state = React.useMemo( 70 + const state = useMemo( 64 71 () => ({trendingDisabled, trendingVideoDisabled}), 65 72 [trendingDisabled, trendingVideoDisabled], 66 73 ) 67 - const api = React.useMemo( 74 + const api = useMemo( 68 75 () => ({setTrendingDisabled, setTrendingVideoDisabled}), 69 76 [setTrendingDisabled, setTrendingVideoDisabled], 70 77 ) ··· 77 84 } 78 85 79 86 export function useTrendingSettings() { 80 - return React.useContext(StateContext) 87 + return useContext(StateContext) 81 88 } 82 89 83 90 export function useTrendingSettingsApi() { 84 - return React.useContext(ApiContext) 91 + return useContext(ApiContext) 85 92 }
+7 -7
src/state/preferences/used-starter-packs.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext, useEffect, useState} from 'react' 2 2 3 3 import * as persisted from '#/state/persisted' 4 4 5 5 type StateContext = boolean | undefined 6 6 type SetContext = (v: boolean) => void 7 7 8 - const stateContext = React.createContext<StateContext>(false) 8 + const stateContext = createContext<StateContext>(false) 9 9 stateContext.displayName = 'UsedStarterPacksStateContext' 10 - const setContext = React.createContext<SetContext>((_: boolean) => {}) 10 + const setContext = createContext<SetContext>((_: boolean) => {}) 11 11 setContext.displayName = 'UsedStarterPacksSetContext' 12 12 13 13 export function Provider({children}: {children: React.ReactNode}) { 14 - const [state, setState] = React.useState<StateContext>(() => 14 + const [state, setState] = useState<StateContext>(() => 15 15 persisted.get('hasCheckedForStarterPack'), 16 16 ) 17 17 ··· 20 20 persisted.write('hasCheckedForStarterPack', v) 21 21 } 22 22 23 - React.useEffect(() => { 23 + useEffect(() => { 24 24 return persisted.onUpdate( 25 25 'hasCheckedForStarterPack', 26 26 nextHasCheckedForStarterPack => { ··· 38 38 ) 39 39 } 40 40 41 - export const useHasCheckedForStarterPack = () => React.useContext(stateContext) 42 - export const useSetHasCheckedForStarterPack = () => React.useContext(setContext) 41 + export const useHasCheckedForStarterPack = () => useContext(stateContext) 42 + export const useSetHasCheckedForStarterPack = () => useContext(setContext)
+3 -3
src/state/queries/actor-autocomplete.ts
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import { 3 3 type AppBskyActorDefs, 4 4 moderateProfile, ··· 47 47 : undefined 48 48 return res?.data.actors || [] 49 49 }, 50 - select: React.useCallback( 50 + select: useCallback( 51 51 (data: AppBskyActorDefs.ProfileViewBasic[]) => { 52 52 return computeSuggestions({ 53 53 q: prefix, ··· 67 67 const moderationOpts = useModerationOpts() 68 68 const agent = useAgent() 69 69 70 - return React.useCallback( 70 + return useCallback( 71 71 async ({query, limit = 8}: {query: string; limit?: number}) => { 72 72 query = query.toLowerCase() 73 73 let res
+3 -3
src/state/queries/handle.ts
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {useMutation, useQueryClient} from '@tanstack/react-query' 3 3 4 4 import {STALE} from '#/state/queries' ··· 16 16 const queryClient = useQueryClient() 17 17 const agent = useAgent() 18 18 19 - return React.useCallback( 19 + return useCallback( 20 20 async (handleOrDid: string) => { 21 21 if (handleOrDid.startsWith('did:')) { 22 22 const res = await queryClient.fetchQuery({ ··· 55 55 const queryClient = useQueryClient() 56 56 const agent = useAgent() 57 57 58 - return React.useCallback( 58 + return useCallback( 59 59 async (handleOrDid: string) => { 60 60 return queryClient.fetchQuery({ 61 61 staleTime: STALE.INFINITY,
+19 -12
src/state/queries/notifications/unread.tsx
··· 2 2 * A kind of companion API to ./feed.ts. See that file for more info. 3 3 */ 4 4 5 - import React, {useRef} from 'react' 5 + import { 6 + createContext, 7 + useContext, 8 + useEffect, 9 + useMemo, 10 + useRef, 11 + useState, 12 + } from 'react' 6 13 import {AppState} from 'react-native' 7 14 import {useQueryClient} from '@tanstack/react-query' 8 15 import EventEmitter from 'eventemitter3' ··· 33 40 getCachedUnreadPage: () => FeedPage | undefined 34 41 } 35 42 36 - const stateContext = React.createContext<StateContext>('') 43 + const stateContext = createContext<StateContext>('') 37 44 stateContext.displayName = 'NotificationsUnreadStateContext' 38 45 39 - const apiContext = React.createContext<ApiContext>({ 46 + const apiContext = createContext<ApiContext>({ 40 47 async markAllRead() {}, 41 48 async checkUnread() {}, 42 49 getCachedUnreadPage: () => undefined, ··· 49 56 const queryClient = useQueryClient() 50 57 const moderationOpts = useModerationOpts() 51 58 52 - const [numUnread, setNumUnread] = React.useState('') 59 + const [numUnread, setNumUnread] = useState('') 53 60 54 - const checkUnreadRef = React.useRef<ApiContext['checkUnread'] | null>(null) 55 - const cacheRef = React.useRef<CachedFeedPage>({ 61 + const checkUnreadRef = useRef<ApiContext['checkUnread'] | null>(null) 62 + const cacheRef = useRef<CachedFeedPage>({ 56 63 usableInFeed: false, 57 64 syncedAt: new Date(), 58 65 data: undefined, 59 66 unreadCount: 0, 60 67 }) 61 68 62 - React.useEffect(() => { 69 + useEffect(() => { 63 70 function markAsUnusable() { 64 71 if (cacheRef.current) { 65 72 cacheRef.current.usableInFeed = false ··· 72 79 }, []) 73 80 74 81 // periodic sync 75 - React.useEffect(() => { 82 + useEffect(() => { 76 83 if (!hasSession || !checkUnreadRef.current) { 77 84 return 78 85 } ··· 85 92 }, [hasSession]) 86 93 87 94 // listen for broadcasts 88 - React.useEffect(() => { 95 + useEffect(() => { 89 96 const listener = ({data}: MessageEvent) => { 90 97 cacheRef.current = { 91 98 usableInFeed: false, ··· 109 116 const isFetchingRef = useRef(false) 110 117 111 118 // create API 112 - const api = React.useMemo<ApiContext>(() => { 119 + const api = useMemo<ApiContext>(() => { 113 120 return { 114 121 async markAllRead() { 115 122 // update server ··· 211 218 } 212 219 213 220 export function useUnreadNotifications() { 214 - return React.useContext(stateContext) 221 + return useContext(stateContext) 215 222 } 216 223 217 224 export function useUnreadNotificationsApi() { 218 - return React.useContext(apiContext) 225 + return useContext(apiContext) 219 226 } 220 227 221 228 function countUnread(page: FeedPage) {
+2 -2
src/state/queries/post-feed.ts
··· 1 - import React, {useCallback, useEffect, useRef} from 'react' 1 + import {useCallback, useEffect, useMemo, useRef} from 'react' 2 2 import {AppState} from 'react-native' 3 3 import { 4 4 type AppBskyActorDefs, ··· 164 164 const fetchLimit = MIN_POSTS 165 165 166 166 // Make sure this doesn't invalidate unless really needed. 167 - const selectArgs = React.useMemo( 167 + const selectArgs = useMemo( 168 168 () => ({ 169 169 feedTuners, 170 170 moderationOpts,
+2 -2
src/state/queries/postgate/index.ts
··· 1 - import React from 'react' 1 + import {useRef} from 'react' 2 2 import { 3 3 AppBskyEmbedRecord, 4 4 AppBskyEmbedRecordWithMedia, ··· 174 174 const agent = useAgent() 175 175 const queryClient = useQueryClient() 176 176 const getPosts = useGetPosts() 177 - const prevEmbed = React.useRef<AppBskyFeedDefs.PostView['embed']>(undefined) 177 + const prevEmbed = useRef<AppBskyFeedDefs.PostView['embed']>(undefined) 178 178 179 179 return useMutation({ 180 180 mutationFn: async ({
+3 -3
src/state/queries/preferences/moderation.ts
··· 1 - import React from 'react' 1 + import {useMemo} from 'react' 2 2 import { 3 3 BskyAgent, 4 4 DEFAULT_LABEL_SETTINGS, ··· 36 36 const labelers = useLabelersDetailedInfoQuery({dids}) 37 37 const isLoading = prefs.isLoading || labelers.isLoading 38 38 const error = prefs.error || labelers.error 39 - return React.useMemo(() => { 39 + return useMemo(() => { 40 40 return { 41 41 isLoading, 42 42 error, ··· 48 48 49 49 export function useLabelDefinitionsQuery() { 50 50 const labelers = useMyLabelersQuery() 51 - return React.useMemo(() => { 51 + return useMemo(() => { 52 52 return { 53 53 labelDefs: Object.fromEntries( 54 54 (labelers.data || []).map(labeler => [
+4 -4
src/state/queries/search-posts.ts
··· 1 - import React from 'react' 1 + import {useCallback, useMemo, useRef} from 'react' 2 2 import { 3 3 type AppBskyActorDefs, 4 4 type AppBskyFeedDefs, ··· 39 39 }) { 40 40 const agent = useAgent() 41 41 const moderationOpts = useModerationOpts() 42 - const selectArgs = React.useMemo( 42 + const selectArgs = useMemo( 43 43 () => ({ 44 44 isSearchingSpecificUser: /from:(\w+)/.test(query), 45 45 moderationOpts, 46 46 }), 47 47 [query, moderationOpts], 48 48 ) 49 - const lastRun = React.useRef<{ 49 + const lastRun = useRef<{ 50 50 data: InfiniteData<AppBskyFeedSearchPosts.OutputSchema> 51 51 args: typeof selectArgs 52 52 result: InfiniteData<AppBskyFeedSearchPosts.OutputSchema> ··· 72 72 initialPageParam: undefined, 73 73 getNextPageParam: lastPage => lastPage.cursor, 74 74 enabled: enabled ?? !!moderationOpts, 75 - select: React.useCallback( 75 + select: useCallback( 76 76 (data: InfiniteData<AppBskyFeedSearchPosts.OutputSchema>) => { 77 77 const {moderationOpts, isSearchingSpecificUser} = selectArgs 78 78
+3 -3
src/state/queries/trending/useGetTrendsQuery.ts
··· 1 - import React from 'react' 1 + import {useCallback, useMemo} from 'react' 2 2 import {type AppBskyUnspeccedGetTrends, hasMutedWord} from '@atproto/api' 3 3 import {useQuery} from '@tanstack/react-query' 4 4 ··· 18 18 export function useGetTrendsQuery() { 19 19 const agent = useAgent() 20 20 const {data: preferences} = usePreferencesQuery() 21 - const mutedWords = React.useMemo(() => { 21 + const mutedWords = useMemo(() => { 22 22 return preferences?.moderationPrefs?.mutedWords || [] 23 23 }, [preferences?.moderationPrefs]) 24 24 ··· 41 41 ) 42 42 return data 43 43 }, 44 - select: React.useCallback( 44 + select: useCallback( 45 45 (data: AppBskyUnspeccedGetTrends.OutputSchema) => { 46 46 return { 47 47 trends: (data.trends ?? []).filter(t => {
+10 -14
src/state/shell/color-mode.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext, useEffect, useMemo, useState} from 'react' 2 2 3 3 import * as persisted from '#/state/persisted' 4 4 ··· 11 11 setDarkTheme: (v: persisted.Schema['darkTheme']) => void 12 12 } 13 13 14 - const stateContext = React.createContext<StateContext>({ 14 + const stateContext = createContext<StateContext>({ 15 15 colorMode: 'system', 16 16 darkTheme: 'dark', 17 17 }) 18 18 stateContext.displayName = 'ColorModeStateContext' 19 - const setContext = React.createContext<SetContext>({} as SetContext) 19 + const setContext = createContext<SetContext>({} as SetContext) 20 20 setContext.displayName = 'ColorModeSetContext' 21 21 22 22 export function Provider({children}: React.PropsWithChildren<{}>) { 23 - const [colorMode, setColorMode] = React.useState(() => 24 - persisted.get('colorMode'), 25 - ) 26 - const [darkTheme, setDarkTheme] = React.useState(() => 27 - persisted.get('darkTheme'), 28 - ) 23 + const [colorMode, setColorMode] = useState(() => persisted.get('colorMode')) 24 + const [darkTheme, setDarkTheme] = useState(() => persisted.get('darkTheme')) 29 25 30 - const stateContextValue = React.useMemo( 26 + const stateContextValue = useMemo( 31 27 () => ({ 32 28 colorMode, 33 29 darkTheme, ··· 35 31 [colorMode, darkTheme], 36 32 ) 37 33 38 - const setContextValue = React.useMemo( 34 + const setContextValue = useMemo( 39 35 () => ({ 40 36 setColorMode: (_colorMode: persisted.Schema['colorMode']) => { 41 37 setColorMode(_colorMode) ··· 49 45 [], 50 46 ) 51 47 52 - React.useEffect(() => { 48 + useEffect(() => { 53 49 const unsub1 = persisted.onUpdate('darkTheme', nextDarkTheme => { 54 50 setDarkTheme(nextDarkTheme) 55 51 }) ··· 72 68 } 73 69 74 70 export function useThemePrefs() { 75 - return React.useContext(stateContext) 71 + return useContext(stateContext) 76 72 } 77 73 78 74 export function useSetThemePrefs() { 79 - return React.useContext(setContext) 75 + return useContext(setContext) 80 76 }
+10 -10
src/state/shell/composer/index.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext, useMemo, useState} from 'react' 2 2 import { 3 3 type AppBskyActorDefs, 4 4 type AppBskyFeedDefs, ··· 65 65 closeComposer: () => boolean 66 66 } 67 67 68 - const stateContext = React.createContext<StateContext>(undefined) 68 + const stateContext = createContext<StateContext>(undefined) 69 69 stateContext.displayName = 'ComposerStateContext' 70 - const controlsContext = React.createContext<ControlsContext>({ 70 + const controlsContext = createContext<ControlsContext>({ 71 71 openComposer(_opts: ComposerOpts) {}, 72 72 closeComposer() { 73 73 return false ··· 77 77 78 78 export function Provider({children}: React.PropsWithChildren<{}>) { 79 79 const {_} = useLingui() 80 - const [state, setState] = React.useState<StateContext>() 80 + const [state, setState] = useState<StateContext>() 81 81 const queryClient = useQueryClient() 82 82 83 83 const openComposer = useNonReactiveCallback((opts: ComposerOpts) => { ··· 135 135 return wasOpen 136 136 }) 137 137 138 - const api = React.useMemo( 138 + const api = useMemo( 139 139 () => ({ 140 140 openComposer, 141 141 closeComposer, ··· 153 153 } 154 154 155 155 export function useComposerState() { 156 - return React.useContext(stateContext) 156 + return useContext(stateContext) 157 157 } 158 158 159 159 export function useComposerControls() { 160 - const {closeComposer} = React.useContext(controlsContext) 161 - return React.useMemo(() => ({closeComposer}), [closeComposer]) 160 + const {closeComposer} = useContext(controlsContext) 161 + return useMemo(() => ({closeComposer}), [closeComposer]) 162 162 } 163 163 164 164 /** ··· 168 168 * @deprecated use `#/lib/hooks/useOpenComposer` instead 169 169 */ 170 170 export function useOpenComposer() { 171 - const {openComposer} = React.useContext(controlsContext) 172 - return React.useMemo(() => ({openComposer}), [openComposer]) 171 + const {openComposer} = useContext(controlsContext) 172 + return useMemo(() => ({openComposer}), [openComposer]) 173 173 }
+2 -2
src/state/shell/composer/useComposerKeyboardShortcut.tsx
··· 1 - import React from 'react' 1 + import {useEffect} from 'react' 2 2 3 3 import {useOpenComposer} from '#/lib/hooks/useOpenComposer' 4 4 import {useDialogStateContext} from '#/state/dialogs' ··· 46 46 const isDrawerOpen = useIsDrawerOpen() 47 47 const {hasSession} = useSession() 48 48 49 - React.useEffect(() => { 49 + useEffect(() => { 50 50 if (!hasSession) { 51 51 return 52 52 }
+6 -6
src/state/shell/drawer-swipe-disabled.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext, useState} from 'react' 2 2 3 3 type StateContext = boolean 4 4 type SetContext = (v: boolean) => void 5 5 6 - const stateContext = React.createContext<StateContext>(false) 6 + const stateContext = createContext<StateContext>(false) 7 7 stateContext.displayName = 'DrawerSwipeDisabledStateContext' 8 - const setContext = React.createContext<SetContext>((_: boolean) => {}) 8 + const setContext = createContext<SetContext>((_: boolean) => {}) 9 9 setContext.displayName = 'DrawerSwipeDisabledSetContext' 10 10 11 11 export function Provider({children}: React.PropsWithChildren<{}>) { 12 - const [state, setState] = React.useState(false) 12 + const [state, setState] = useState(false) 13 13 return ( 14 14 <stateContext.Provider value={state}> 15 15 <setContext.Provider value={setState}>{children}</setContext.Provider> ··· 18 18 } 19 19 20 20 export function useIsDrawerSwipeDisabled() { 21 - return React.useContext(stateContext) 21 + return useContext(stateContext) 22 22 } 23 23 24 24 export function useSetDrawerSwipeDisabled() { 25 - return React.useContext(setContext) 25 + return useContext(setContext) 26 26 }
+7 -7
src/state/shell/logged-out.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext, useMemo, useState} from 'react' 2 2 3 3 import {useSession} from '#/state/session' 4 4 import {useActiveStarterPack} from '#/state/shell/starter-pack' ··· 35 35 clearRequestedAccount: () => void 36 36 } 37 37 38 - const StateContext = React.createContext<State>({ 38 + const StateContext = createContext<State>({ 39 39 showLoggedOut: false, 40 40 requestedAccountSwitchTo: undefined, 41 41 }) 42 42 StateContext.displayName = 'LoggedOutStateContext' 43 43 44 - const ControlsContext = React.createContext<Controls>({ 44 + const ControlsContext = createContext<Controls>({ 45 45 setShowLoggedOut: () => {}, 46 46 requestSwitchToAccount: () => {}, 47 47 clearRequestedAccount: () => {}, ··· 52 52 const activeStarterPack = useActiveStarterPack() 53 53 const {hasSession} = useSession() 54 54 const shouldShowStarterPack = Boolean(activeStarterPack?.uri) && !hasSession 55 - const [state, setState] = React.useState<State>({ 55 + const [state, setState] = useState<State>({ 56 56 showLoggedOut: shouldShowStarterPack, 57 57 requestedAccountSwitchTo: shouldShowStarterPack 58 58 ? IS_WEB ··· 61 61 : undefined, 62 62 }) 63 63 64 - const controls = React.useMemo<Controls>( 64 + const controls = useMemo<Controls>( 65 65 () => ({ 66 66 setShowLoggedOut(show) { 67 67 setState(s => ({ ··· 96 96 } 97 97 98 98 export function useLoggedOutView() { 99 - return React.useContext(StateContext) 99 + return useContext(StateContext) 100 100 } 101 101 102 102 export function useLoggedOutViewControls() { 103 - return React.useContext(ControlsContext) 103 + return useContext(ControlsContext) 104 104 }
+7 -7
src/state/shell/minimal-mode.tsx
··· 1 - import React from 'react' 1 + import {createContext, useCallback, useContext, useMemo} from 'react' 2 2 import { 3 3 type SharedValue, 4 4 useSharedValue, ··· 11 11 } 12 12 type SetContext = (v: boolean) => void 13 13 14 - const stateContext = React.createContext<StateContext>({ 14 + const stateContext = createContext<StateContext>({ 15 15 headerMode: { 16 16 value: 0, 17 17 addListener() {}, ··· 34 34 }, 35 35 }) 36 36 stateContext.displayName = 'MinimalModeStateContext' 37 - const setContext = React.createContext<SetContext>((_: boolean) => {}) 37 + const setContext = createContext<SetContext>((_: boolean) => {}) 38 38 setContext.displayName = 'MinimalModeSetContext' 39 39 40 40 export function Provider({children}: React.PropsWithChildren<{}>) { 41 41 const headerMode = useSharedValue(0) 42 42 const footerMode = useSharedValue(0) 43 - const setMode = React.useCallback( 43 + const setMode = useCallback( 44 44 (v: boolean) => { 45 45 'worklet' 46 46 headerMode.set(() => ··· 56 56 }, 57 57 [headerMode, footerMode], 58 58 ) 59 - const value = React.useMemo( 59 + const value = useMemo( 60 60 () => ({ 61 61 headerMode, 62 62 footerMode, ··· 71 71 } 72 72 73 73 export function useMinimalShellMode() { 74 - return React.useContext(stateContext) 74 + return useContext(stateContext) 75 75 } 76 76 77 77 export function useSetMinimalShellMode() { 78 - return React.useContext(setContext) 78 + return useContext(setContext) 79 79 }
+7 -7
src/state/shell/onboarding.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext, useEffect, useReducer} from 'react' 2 2 3 3 import * as persisted from '#/state/persisted' 4 4 ··· 26 26 } 27 27 export type DispatchContext = (action: Action) => void 28 28 29 - const stateContext = React.createContext<StateContext>( 29 + const stateContext = createContext<StateContext>( 30 30 compute(persisted.defaults.onboarding), 31 31 ) 32 32 stateContext.displayName = 'OnboardingStateContext' 33 - const dispatchContext = React.createContext<DispatchContext>((_: Action) => {}) 33 + const dispatchContext = createContext<DispatchContext>((_: Action) => {}) 34 34 dispatchContext.displayName = 'OnboardingDispatchContext' 35 35 36 36 function reducer(state: StateContext, action: Action): StateContext { ··· 74 74 } 75 75 76 76 export function Provider({children}: React.PropsWithChildren<{}>) { 77 - const [state, dispatch] = React.useReducer( 77 + const [state, dispatch] = useReducer( 78 78 reducer, 79 79 compute(persisted.get('onboarding')), 80 80 ) 81 81 82 - React.useEffect(() => { 82 + useEffect(() => { 83 83 return persisted.onUpdate('onboarding', nextOnboarding => { 84 84 const next = nextOnboarding.step 85 85 // TODO we've introduced a footgun ··· 102 102 } 103 103 104 104 export function useOnboardingState() { 105 - return React.useContext(stateContext) 105 + return useContext(stateContext) 106 106 } 107 107 108 108 export function useOnboardingDispatch() { 109 - return React.useContext(dispatchContext) 109 + return useContext(dispatchContext) 110 110 } 111 111 112 112 export function isOnboardingActive() {
+3 -3
src/state/shell/post-progress.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext} from 'react' 2 2 3 3 interface PostProgressState { 4 4 progress: number ··· 6 6 error?: string 7 7 } 8 8 9 - const PostProgressContext = React.createContext<PostProgressState>({ 9 + const PostProgressContext = createContext<PostProgressState>({ 10 10 progress: 0, 11 11 status: 'idle', 12 12 }) ··· 15 15 export function Provider() {} 16 16 17 17 export function usePostProgress() { 18 - return React.useContext(PostProgressContext) 18 + return useContext(PostProgressContext) 19 19 }
+12 -12
src/state/shell/progress-guide.tsx
··· 1 - import React, {useMemo} from 'react' 1 + import {createContext, useContext, useMemo, useRef, useState} from 'react' 2 2 import {msg} from '@lingui/core/macro' 3 3 import {useLingui} from '@lingui/react' 4 4 ··· 44 44 | Follow10ProgressGuide 45 45 | undefined 46 46 47 - const ProgressGuideContext = React.createContext<ProgressGuide>(undefined) 47 + const ProgressGuideContext = createContext<ProgressGuide>(undefined) 48 48 ProgressGuideContext.displayName = 'ProgressGuideContext' 49 49 50 - const ProgressGuideControlContext = React.createContext<{ 50 + const ProgressGuideControlContext = createContext<{ 51 51 startProgressGuide(guide: ProgressGuideName): void 52 52 endProgressGuide(): void 53 53 captureAction(action: ProgressGuideAction, count?: number): void ··· 59 59 ProgressGuideControlContext.displayName = 'ProgressGuideControlContext' 60 60 61 61 export function useProgressGuide(guide: ProgressGuideName) { 62 - const ctx = React.useContext(ProgressGuideContext) 62 + const ctx = useContext(ProgressGuideContext) 63 63 if (ctx?.guide === guide) { 64 64 return ctx 65 65 } ··· 67 67 } 68 68 69 69 export function useProgressGuideControls() { 70 - return React.useContext(ProgressGuideControlContext) 70 + return useContext(ProgressGuideControlContext) 71 71 } 72 72 73 73 export function Provider({children}: React.PropsWithChildren<{}>) { ··· 101 101 }, [isPending, variables, preferences]) 102 102 103 103 const [localGuideState, setLocalGuideState] = 104 - React.useState<ProgressGuide>(undefined) 104 + useState<ProgressGuide>(undefined) 105 105 106 106 if (activeProgressGuide && !localGuideState) { 107 107 // hydrate from the server if needed 108 108 setLocalGuideState(activeProgressGuide) 109 109 } 110 110 111 - const firstLikeToastRef = React.useRef<ProgressGuideToastRef | null>(null) 112 - const fifthLikeToastRef = React.useRef<ProgressGuideToastRef | null>(null) 113 - const tenthLikeToastRef = React.useRef<ProgressGuideToastRef | null>(null) 111 + const firstLikeToastRef = useRef<ProgressGuideToastRef | null>(null) 112 + const fifthLikeToastRef = useRef<ProgressGuideToastRef | null>(null) 113 + const tenthLikeToastRef = useRef<ProgressGuideToastRef | null>(null) 114 114 115 - const fifthFollowToastRef = React.useRef<ProgressGuideToastRef | null>(null) 116 - const tenthFollowToastRef = React.useRef<ProgressGuideToastRef | null>(null) 115 + const fifthFollowToastRef = useRef<ProgressGuideToastRef | null>(null) 116 + const tenthFollowToastRef = useRef<ProgressGuideToastRef | null>(null) 117 117 118 - const controls = React.useMemo(() => { 118 + const controls = useMemo(() => { 119 119 return { 120 120 startProgressGuide(guide: ProgressGuideName) { 121 121 if (guide === 'like-10-and-follow-7') {
+4 -4
src/state/shell/shell-layout.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext, useMemo} from 'react' 2 2 import {type SharedValue, useSharedValue} from 'react-native-reanimated' 3 3 4 4 type StateContext = { ··· 6 6 footerHeight: SharedValue<number> 7 7 } 8 8 9 - const stateContext = React.createContext<StateContext>({ 9 + const stateContext = createContext<StateContext>({ 10 10 headerHeight: { 11 11 value: 0, 12 12 addListener() {}, ··· 34 34 const headerHeight = useSharedValue(0) 35 35 const footerHeight = useSharedValue(0) 36 36 37 - const value = React.useMemo( 37 + const value = useMemo( 38 38 () => ({ 39 39 headerHeight, 40 40 footerHeight, ··· 46 46 } 47 47 48 48 export function useShellLayout() { 49 - return React.useContext(stateContext) 49 + return useContext(stateContext) 50 50 }
+6 -6
src/state/shell/starter-pack.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext, useState} from 'react' 2 2 3 3 type StateContext = 4 4 | { ··· 8 8 | undefined 9 9 type SetContext = (v: StateContext) => void 10 10 11 - const stateContext = React.createContext<StateContext>(undefined) 11 + const stateContext = createContext<StateContext>(undefined) 12 12 stateContext.displayName = 'ActiveStarterPackStateContext' 13 - const setContext = React.createContext<SetContext>((_: StateContext) => {}) 13 + const setContext = createContext<SetContext>((_: StateContext) => {}) 14 14 setContext.displayName = 'ActiveStarterPackSetContext' 15 15 16 16 export function Provider({children}: {children: React.ReactNode}) { 17 - const [state, setState] = React.useState<StateContext>() 17 + const [state, setState] = useState<StateContext>() 18 18 19 19 return ( 20 20 <stateContext.Provider value={state}> ··· 23 23 ) 24 24 } 25 25 26 - export const useActiveStarterPack = () => React.useContext(stateContext) 27 - export const useSetActiveStarterPack = () => React.useContext(setContext) 26 + export const useActiveStarterPack = () => useContext(stateContext) 27 + export const useSetActiveStarterPack = () => useContext(setContext)
+5 -5
src/state/shell/tick-every-minute.tsx
··· 1 - import React from 'react' 1 + import {createContext, useContext, useEffect, useState} from 'react' 2 2 3 3 type StateContext = number 4 4 5 - const stateContext = React.createContext<StateContext>(0) 5 + const stateContext = createContext<StateContext>(0) 6 6 stateContext.displayName = 'TickEveryMinuteContext' 7 7 8 8 export function Provider({children}: React.PropsWithChildren<{}>) { 9 - const [tick, setTick] = React.useState(Date.now()) 10 - React.useEffect(() => { 9 + const [tick, setTick] = useState(Date.now()) 10 + useEffect(() => { 11 11 const i = setInterval(() => { 12 12 setTick(Date.now()) 13 13 }, 60_000) ··· 17 17 } 18 18 19 19 export function useTickEveryMinute() { 20 - return React.useContext(stateContext) 20 + return useContext(stateContext) 21 21 }
+13 -13
src/state/threadgate-hidden-replies.tsx
··· 1 - import React from 'react' 1 + import {createContext, useCallback, useContext, useMemo, useState} from 'react' 2 2 import {type AppBskyFeedThreadgate} from '@atproto/api' 3 3 4 4 type StateContext = { ··· 10 10 removeHiddenReplyUri: (uri: string) => void 11 11 } 12 12 13 - const StateContext = React.createContext<StateContext>({ 13 + const StateContext = createContext<StateContext>({ 14 14 uris: new Set(), 15 15 recentlyUnhiddenUris: new Set(), 16 16 }) 17 17 StateContext.displayName = 'ThreadgateHiddenRepliesStateContext' 18 18 19 - const ApiContext = React.createContext<ApiContext>({ 19 + const ApiContext = createContext<ApiContext>({ 20 20 addHiddenReplyUri: () => {}, 21 21 removeHiddenReplyUri: () => {}, 22 22 }) 23 23 ApiContext.displayName = 'ThreadgateHiddenRepliesApiContext' 24 24 25 25 export function Provider({children}: {children: React.ReactNode}) { 26 - const [uris, setHiddenReplyUris] = React.useState<Set<string>>(new Set()) 27 - const [recentlyUnhiddenUris, setRecentlyUnhiddenUris] = React.useState< 28 - Set<string> 29 - >(new Set()) 26 + const [uris, setHiddenReplyUris] = useState<Set<string>>(new Set()) 27 + const [recentlyUnhiddenUris, setRecentlyUnhiddenUris] = useState<Set<string>>( 28 + new Set(), 29 + ) 30 30 31 - const stateCtx = React.useMemo( 31 + const stateCtx = useMemo( 32 32 () => ({ 33 33 uris, 34 34 recentlyUnhiddenUris, ··· 36 36 [uris, recentlyUnhiddenUris], 37 37 ) 38 38 39 - const apiCtx = React.useMemo( 39 + const apiCtx = useMemo( 40 40 () => ({ 41 41 addHiddenReplyUri(uri: string) { 42 42 setHiddenReplyUris(prev => new Set(prev.add(uri))) ··· 64 64 } 65 65 66 66 export function useThreadgateHiddenReplyUris() { 67 - return React.useContext(StateContext) 67 + return useContext(StateContext) 68 68 } 69 69 70 70 export function useThreadgateHiddenReplyUrisAPI() { 71 - return React.useContext(ApiContext) 71 + return useContext(ApiContext) 72 72 } 73 73 74 74 export function useMergedThreadgateHiddenReplies({ ··· 77 77 threadgateRecord?: AppBskyFeedThreadgate.Record 78 78 }) { 79 79 const {uris, recentlyUnhiddenUris} = useThreadgateHiddenReplyUris() 80 - return React.useMemo(() => { 80 + return useMemo(() => { 81 81 const set = new Set([...(threadgateRecord?.hiddenReplies || []), ...uris]) 82 82 for (const uri of recentlyUnhiddenUris) { 83 83 set.delete(uri) ··· 88 88 89 89 export function useMergeThreadgateHiddenReplies() { 90 90 const {uris, recentlyUnhiddenUris} = useThreadgateHiddenReplyUris() 91 - return React.useCallback( 91 + return useCallback( 92 92 (threadgate?: AppBskyFeedThreadgate.Record) => { 93 93 const set = new Set([...(threadgate?.hiddenReplies || []), ...uris]) 94 94 for (const uri of recentlyUnhiddenUris) {
+2 -2
src/state/userActionHistory.ts
··· 1 - import React from 'react' 1 + import {useState} from 'react' 2 2 3 3 const LIKE_WINDOW = 100 4 4 const FOLLOW_WINDOW = 100 ··· 45 45 } 46 46 47 47 export function useActionHistorySnapshot() { 48 - return React.useState(() => getActionHistory())[0] 48 + return useState(() => getActionHistory())[0] 49 49 } 50 50 51 51 export function like(postUris: string[]) {
+3 -3
src/view/com/auth/SplashScreen.web.tsx
··· 1 - import React from 'react' 1 + import {useEffect, useState} from 'react' 2 2 import {Pressable, View} from 'react-native' 3 3 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 4 4 import {msg} from '@lingui/core/macro' ··· 33 33 const {_} = useLingui() 34 34 const t = useTheme() 35 35 const {isTabletOrMobile: IS_WEB_MOBILE} = useWebMediaQueries() 36 - const [showClipOverlay, setShowClipOverlay] = React.useState(false) 36 + const [showClipOverlay, setShowClipOverlay] = useState(false) 37 37 38 - React.useEffect(() => { 38 + useEffect(() => { 39 39 const getParams = new URLSearchParams(window.location.search) 40 40 const clip = getParams.get('clip') 41 41 if (clip === 'true') {
-1
src/view/com/composer/AltTextCounterWrapper.tsx
··· 1 1 import {View} from 'react-native' 2 - import type React from 'react' 3 2 4 3 import {MAX_ALT_TEXT} from '#/lib/constants' 5 4 import {CharProgress} from '#/view/com/composer/char-progress/CharProgress'
+17 -15
src/view/com/composer/Composer.tsx
··· 1 - import React, { 1 + import { 2 + Fragment, 3 + memo, 2 4 useCallback, 3 5 useEffect, 4 6 useImperativeHandle, ··· 299 301 [activePost.id], 300 302 ) 301 303 302 - const selectVideo = React.useCallback( 304 + const selectVideo = useCallback( 303 305 (postId: string, asset: ImagePickerAsset) => { 304 306 const abortController = new AbortController() 305 307 composerDispatch({ ··· 485 487 [_, agent, currentDid, composerDispatch], 486 488 ) 487 489 488 - const handleSelectDraft = React.useCallback( 490 + const handleSelectDraft = useCallback( 489 491 async (draftSummary: DraftSummary) => { 490 492 logger.debug('loading draft for editing', { 491 493 draftId: draftSummary.id, ··· 553 555 revokeAllMediaUrls() 554 556 }, [closeComposer, queryClient]) 555 557 556 - const getDraftSaveError = React.useCallback( 558 + const getDraftSaveError = useCallback( 557 559 (e: unknown): string => { 558 560 if (e instanceof AppBskyDraftCreateDraft.DraftLimitReachedError) { 559 561 return _(msg`You've reached the maximum number of drafts`) ··· 563 565 [_], 564 566 ) 565 567 566 - const validateDraftTextOrError = React.useCallback((): boolean => { 568 + const validateDraftTextOrError = useCallback((): boolean => { 567 569 const tooLong = composerState.thread.posts.some( 568 570 post => post.richtext.graphemeLength > MAX_DRAFT_GRAPHEME_LENGTH, 569 571 ) ··· 578 580 return true 579 581 }, [composerState.thread.posts, _]) 580 582 581 - const handleSaveDraft = React.useCallback(async () => { 583 + const handleSaveDraft = useCallback(async () => { 582 584 setError('') 583 585 if (!validateDraftTextOrError()) { 584 586 return ··· 621 623 ]) 622 624 623 625 // Save without closing - for use by DraftsButton 624 - const saveCurrentDraft = React.useCallback(async (): Promise<{ 626 + const saveCurrentDraft = useCallback(async (): Promise<{ 625 627 success: boolean 626 628 }> => { 627 629 setError('') ··· 648 650 ]) 649 651 650 652 // Handle discard action - fires metric and closes composer 651 - const handleDiscard = React.useCallback(() => { 653 + const handleDiscard = useCallback(() => { 652 654 const posts = thread.posts 653 655 const hasContent = posts.some( 654 656 post => ··· 665 667 }, [thread.posts, ax, onClose]) 666 668 667 669 // Check if composer is empty (no content to save) 668 - const isComposerEmpty = React.useMemo(() => { 670 + const isComposerEmpty = useMemo(() => { 669 671 // Has multiple posts means it's not empty 670 672 if (thread.posts.length > 1) return false 671 673 ··· 683 685 }, [thread.posts]) 684 686 685 687 // Clear the composer (discard current content) 686 - const handleClearComposer = React.useCallback(() => { 688 + const handleClearComposer = useCallback(() => { 687 689 composerDispatch({ 688 690 type: 'clear', 689 691 initInteractionSettings: preferences?.postInteractionSettings, ··· 794 796 ), 795 797 ) 796 798 797 - const onPressPublish = React.useCallback(async () => { 799 + const onPressPublish = useCallback(async () => { 798 800 if (isPublishing) { 799 801 return 800 802 } ··· 1015 1017 onPressPublish() 1016 1018 }) 1017 1019 1018 - React.useEffect(() => { 1020 + useEffect(() => { 1019 1021 if (publishOnUpload) { 1020 1022 let erroredVideos = 0 1021 1023 let uploadingVideos = 0 ··· 1181 1183 onLayout={onScrollViewLayout}> 1182 1184 {replyTo ? <ComposerReplyTo replyTo={replyTo} /> : undefined} 1183 1185 {thread.posts.map((post, index) => ( 1184 - <React.Fragment key={post.id + (composerState.draftId ?? '')}> 1186 + <Fragment key={post.id + (composerState.draftId ?? '')}> 1185 1187 <ComposerPost 1186 1188 post={post} 1187 1189 dispatch={composerDispatch} ··· 1201 1203 {IS_WEBFooterSticky && post.id === activePost.id && ( 1202 1204 <View style={styles.stickyFooterWeb}>{footer}</View> 1203 1205 )} 1204 - </React.Fragment> 1206 + </Fragment> 1205 1207 ))} 1206 1208 </Animated.ScrollView> 1207 1209 {!IS_WEBFooterSticky && footer} ··· 1273 1275 ) 1274 1276 } 1275 1277 1276 - let ComposerPost = React.memo(function ComposerPost({ 1278 + let ComposerPost = memo(function ComposerPost({ 1277 1279 post, 1278 1280 dispatch, 1279 1281 textInput,
-1
src/view/com/composer/KeyboardAccessory.tsx
··· 1 1 import {View} from 'react-native' 2 2 import {KeyboardStickyView} from 'react-native-keyboard-controller' 3 3 import {useSafeAreaInsets} from 'react-native-safe-area-context' 4 - import type React from 'react' 5 4 6 5 import {atoms as a, useTheme} from '#/alf' 7 6 import {IS_WEB} from '#/env'
+32 -33
src/view/com/composer/photos/Gallery.tsx
··· 1 - import React, {useState} from 'react' 1 + import {memo, useMemo, useState} from 'react' 2 2 import { 3 3 findNodeHandle, 4 4 type ImageStyle, ··· 37 37 } 38 38 39 39 export let Gallery = (props: GalleryProps): React.ReactNode => { 40 - const [containerInfo, setContainerInfo] = React.useState<Dimensions>() 40 + const [containerInfo, setContainerInfo] = useState<Dimensions>() 41 41 42 42 const onLayout = (evt: LayoutChangeEvent) => { 43 43 const {width, height} = evt.nativeEvent.layout ··· 55 55 </View> 56 56 ) 57 57 } 58 - Gallery = React.memo(Gallery) 58 + Gallery = memo(Gallery) 59 59 60 60 interface GalleryInnerProps extends GalleryProps { 61 61 containerInfo: Dimensions ··· 64 64 const GalleryInner = ({images, containerInfo, dispatch}: GalleryInnerProps) => { 65 65 const {isMobile} = useWebMediaQueries() 66 66 67 - const {altTextControlStyle, imageControlsStyle, imageStyle} = 68 - React.useMemo(() => { 69 - const side = 70 - images.length === 1 71 - ? 250 72 - : (containerInfo.width - IMAGE_GAP * (images.length - 1)) / 73 - images.length 67 + const {altTextControlStyle, imageControlsStyle, imageStyle} = useMemo(() => { 68 + const side = 69 + images.length === 1 70 + ? 250 71 + : (containerInfo.width - IMAGE_GAP * (images.length - 1)) / 72 + images.length 74 73 75 - const isOverflow = isMobile && images.length > 2 74 + const isOverflow = isMobile && images.length > 2 76 75 77 - return { 78 - altTextControlStyle: isOverflow 79 - ? {left: 4, bottom: 4} 76 + return { 77 + altTextControlStyle: isOverflow 78 + ? {left: 4, bottom: 4} 79 + : !isMobile && images.length < 3 80 + ? {left: 8, top: 8} 81 + : {left: 4, top: 4}, 82 + imageControlsStyle: { 83 + display: 'flex' as const, 84 + flexDirection: 'row' as const, 85 + position: 'absolute' as const, 86 + ...(isOverflow 87 + ? {top: 4, right: 4, gap: 4} 80 88 : !isMobile && images.length < 3 81 - ? {left: 8, top: 8} 82 - : {left: 4, top: 4}, 83 - imageControlsStyle: { 84 - display: 'flex' as const, 85 - flexDirection: 'row' as const, 86 - position: 'absolute' as const, 87 - ...(isOverflow 88 - ? {top: 4, right: 4, gap: 4} 89 - : !isMobile && images.length < 3 90 - ? {top: 8, right: 8, gap: 8} 91 - : {top: 4, right: 4, gap: 4}), 92 - zIndex: 1, 93 - }, 94 - imageStyle: { 95 - height: side, 96 - width: side, 97 - }, 98 - } 99 - }, [images.length, containerInfo, isMobile]) 89 + ? {top: 8, right: 8, gap: 8} 90 + : {top: 4, right: 4, gap: 4}), 91 + zIndex: 1, 92 + }, 93 + imageStyle: { 94 + height: side, 95 + width: side, 96 + }, 97 + } 98 + }, [images.length, containerInfo, isMobile]) 100 99 101 100 return images.length !== 0 ? ( 102 101 <>
+4 -4
src/view/com/composer/text-input/web/EmojiPicker.web.tsx
··· 1 - import React from 'react' 1 + import {useEffect, useMemo, useRef} from 'react' 2 2 import {Pressable, useWindowDimensions, View} from 'react-native' 3 3 import Picker from '@emoji-mart/react' 4 4 import {msg} from '@lingui/core/macro' ··· 52 52 const {_} = useLingui() 53 53 const {height, width} = useWindowDimensions() 54 54 55 - const isShiftDown = React.useRef(false) 55 + const isShiftDown = useRef(false) 56 56 57 - const position = React.useMemo(() => { 57 + const position = useMemo(() => { 58 58 if (pinToTop) { 59 59 return { 60 60 top: state.pos.top - PICKER_HEIGHT + HEIGHT_OFFSET - 10, ··· 86 86 } 87 87 }, [state.pos, height, width, pinToTop]) 88 88 89 - React.useEffect(() => { 89 + useEffect(() => { 90 90 if (!state.isOpen) return 91 91 92 92 const onKeyDown = (e: KeyboardEvent) => {
+2 -2
src/view/com/composer/text-input/web/useWebPreloadEmoji.ts
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {init} from 'emoji-mart' 3 3 4 4 /** ··· 11 11 * {@link https://github.com/missive/emoji-mart/blob/16978d04a766eec6455e2e8bb21cd8dc0b3c7436/README.md?plain=1#L194} 12 12 */ 13 13 export function useWebPreloadEmoji({immediate}: {immediate?: boolean} = {}) { 14 - const preload = React.useCallback(async () => { 14 + const preload = useCallback(async () => { 15 15 if (loadRequested) return 16 16 loadRequested = true 17 17 try {
-1
src/view/com/composer/videos/SubtitleFilePicker.tsx
··· 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' 5 5 import {Trans} from '@lingui/react/macro' 6 - import type React from 'react' 7 6 8 7 import {logger} from '#/logger' 9 8 import * as Toast from '#/view/com/util/Toast'
+5 -5
src/view/com/home/HomeHeader.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useMemo} from 'react' 2 2 import {useNavigation} from '@react-navigation/native' 3 3 4 4 import {type NavigationProp} from '#/lib/routes/types' ··· 19 19 const {hasSession} = useSession() 20 20 const navigation = useNavigation<NavigationProp>() 21 21 22 - const hasPinnedCustom = React.useMemo<boolean>(() => { 22 + const hasPinnedCustom = useMemo<boolean>(() => { 23 23 if (!hasSession) return false 24 24 return feeds.some(tab => { 25 25 const isFollowing = tab.uri === 'following' ··· 27 27 }) 28 28 }, [feeds, hasSession]) 29 29 30 - const items = React.useMemo(() => { 30 + const items = useMemo(() => { 31 31 const pinnedNames = feeds.map(f => f.displayName) 32 32 if (!hasPinnedCustom) { 33 33 return pinnedNames.concat('Feeds ✨') ··· 35 35 return pinnedNames 36 36 }, [hasPinnedCustom, feeds]) 37 37 38 - const onPressFeedsLink = React.useCallback(() => { 38 + const onPressFeedsLink = useCallback(() => { 39 39 navigation.navigate('Feeds') 40 40 }, [navigation]) 41 41 42 - const onSelect = React.useCallback( 42 + const onSelect = useCallback( 43 43 (index: number) => { 44 44 if (!hasPinnedCustom && index === items.length - 1) { 45 45 onPressFeedsLink()
-1
src/view/com/home/HomeHeaderLayout.web.tsx
··· 2 2 import {View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' 5 - import type React from 'react' 6 5 7 6 import {HITSLOP_10} from '#/lib/constants' 8 7 import {useKawaiiMode} from '#/state/preferences/kawaii'
+2 -2
src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx
··· 1 - import React, {useState} from 'react' 1 + import {memo, useState} from 'react' 2 2 import {ActivityIndicator, StyleSheet} from 'react-native' 3 3 import { 4 4 Gesture, ··· 472 472 return withSpring(value, {overshootClamping: true}) 473 473 } 474 474 475 - export default React.memo(ImageItem) 475 + export default memo(ImageItem)
+2 -2
src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx
··· 6 6 * 7 7 */ 8 8 9 - import React, {useState} from 'react' 9 + import {memo, useState} from 'react' 10 10 import {ActivityIndicator, StyleSheet} from 'react-native' 11 11 import { 12 12 Gesture, ··· 363 363 } 364 364 } 365 365 366 - export default React.memo(ImageItem) 366 + export default memo(ImageItem)
+2 -2
src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.tsx
··· 1 1 // default implementation fallback for web 2 2 3 - import React from 'react' 3 + import {memo} from 'react' 4 4 import {View} from 'react-native' 5 5 import {type PanGesture} from 'react-native-gesture-handler' 6 6 import {type SharedValue} from 'react-native-reanimated' ··· 44 44 return <View /> 45 45 } 46 46 47 - export default React.memo(ImageItem) 47 + export default memo(ImageItem)
+7 -7
src/view/com/lightbox/ImageViewing/index.tsx
··· 7 7 */ 8 8 // Original code copied and simplified from the link below as the codebase is currently not maintained: 9 9 // https://github.com/jobtoday/react-native-image-viewing 10 - import React, {useCallback, useEffect, useMemo, useState} from 'react' 10 + import {useCallback, useEffect, useMemo, useRef, useState} from 'react' 11 11 import { 12 12 LayoutAnimation, 13 13 PixelRatio, ··· 104 104 setActiveLightbox(nextLightbox) 105 105 } 106 106 107 - React.useEffect(() => { 107 + useEffect(() => { 108 108 if (!nextLightbox) { 109 109 return 110 110 } ··· 150 150 }, 151 151 ) 152 152 153 - const onFlyAway = React.useCallback(() => { 153 + const onFlyAway = useCallback(() => { 154 154 'worklet' 155 155 openProgress.set(0) 156 156 runOnJS(onRequestClose)() ··· 216 216 const [isDragging, setIsDragging] = useState(false) 217 217 const [imageIndex, setImageIndex] = useState(initialImageIndex) 218 218 const [showControls, setShowControls] = useState(true) 219 - const [isAltExpanded, setAltExpanded] = React.useState(false) 219 + const [isAltExpanded, setAltExpanded] = useState(false) 220 220 const dismissSwipeTranslateY = useSharedValue(0) 221 221 const isFlyingAway = useSharedValue(false) 222 222 ··· 418 418 openProgress: SharedValue<number> 419 419 dismissSwipeTranslateY: SharedValue<number> 420 420 }) { 421 - const [fetchedDims, setFetchedDims] = React.useState<Dimensions | null>(null) 421 + const [fetchedDims, setFetchedDims] = useState<Dimensions | null>(null) 422 422 const dims = fetchedDims ?? imageSrc.dimensions ?? imageSrc.thumbDimensions 423 423 let imageAspect: number | undefined 424 424 if (dims) { ··· 432 432 width: widthDelayedForJSThreadOnly, 433 433 height: heightDelayedForJSThreadOnly, 434 434 } = useWindowDimensions() 435 - const measureSafeArea = React.useCallback(() => { 435 + const measureSafeArea = useCallback(() => { 436 436 'worklet' 437 437 let safeArea: Rect | null = measure(safeAreaRef) 438 438 if (!safeArea) { ··· 563 563 onPressShare: (uri: string) => void 564 564 }) { 565 565 const {alt: altText, uri} = images[index] 566 - const isMomentumScrolling = React.useRef(false) 566 + const isMomentumScrolling = useRef(false) 567 567 return ( 568 568 <ScrollView 569 569 style={styles.footerScrollView}
+8 -8
src/view/com/lists/ListMembers.tsx
··· 1 - import React, {type JSX, useCallback} from 'react' 1 + import {type JSX, useCallback, useMemo, useState} from 'react' 2 2 import { 3 3 Dimensions, 4 4 type GestureResponderEvent, ··· 57 57 }) { 58 58 const t = useTheme() 59 59 const {_} = useLingui() 60 - const [isRefreshing, setIsRefreshing] = React.useState(false) 60 + const [isRefreshing, setIsRefreshing] = useState(false) 61 61 const {openModal} = useModalControls() 62 62 const {currentAccount} = useSession() 63 63 const moderationOpts = useModerationOpts() ··· 77 77 const isOwner = 78 78 currentAccount && data?.pages[0].list.creator.did === currentAccount.did 79 79 80 - const items = React.useMemo(() => { 80 + const items = useMemo(() => { 81 81 let items: any[] = [] 82 82 if (isFetched) { 83 83 if (isEmpty && isError) { ··· 102 102 // events 103 103 // = 104 104 105 - const onRefresh = React.useCallback(async () => { 105 + const onRefresh = useCallback(async () => { 106 106 setIsRefreshing(true) 107 107 try { 108 108 await refetch() ··· 112 112 setIsRefreshing(false) 113 113 }, [refetch, setIsRefreshing]) 114 114 115 - const onEndReached = React.useCallback(async () => { 115 + const onEndReached = useCallback(async () => { 116 116 if (isFetching || !hasNextPage || isError) return 117 117 try { 118 118 await fetchNextPage() ··· 121 121 } 122 122 }, [isFetching, hasNextPage, isError, fetchNextPage]) 123 123 124 - const onPressRetryLoadMore = React.useCallback(() => { 124 + const onPressRetryLoadMore = useCallback(() => { 125 125 fetchNextPage() 126 126 }, [fetchNextPage]) 127 127 128 - const onPressEditMembership = React.useCallback( 128 + const onPressEditMembership = useCallback( 129 129 (e: GestureResponderEvent, profile: bsky.profile.AnyProfileView) => { 130 130 e.preventDefault() 131 131 openModal({ ··· 141 141 // rendering 142 142 // = 143 143 144 - const renderItem = React.useCallback( 144 + const renderItem = useCallback( 145 145 ({item}: {item: any}) => { 146 146 if (item === EMPTY_ITEM) { 147 147 return renderEmptyState()
+5 -5
src/view/com/lists/MyLists.tsx
··· 1 - import React, {type JSX} from 'react' 1 + import {type JSX, useCallback, useMemo, useState} from 'react' 2 2 import { 3 3 ActivityIndicator, 4 4 FlatList as RNFlatList, ··· 45 45 const t = useTheme() 46 46 const {_} = useLingui() 47 47 const moderationOpts = useModerationOpts() 48 - const [isPTRing, setIsPTRing] = React.useState(false) 48 + const [isPTRing, setIsPTRing] = useState(false) 49 49 const {data, isFetching, isFetched, isError, error, refetch} = 50 50 useMyListsQuery(filter) 51 51 const isEmpty = !isFetching && !data?.length 52 52 53 - const items = React.useMemo(() => { 53 + const items = useMemo(() => { 54 54 let items: any[] = [] 55 55 if (isError && isEmpty) { 56 56 items = items.concat([ERROR_ITEM]) ··· 85 85 // events 86 86 // = 87 87 88 - const onRefresh = React.useCallback(async () => { 88 + const onRefresh = useCallback(async () => { 89 89 setIsPTRing(true) 90 90 try { 91 91 await refetch() ··· 98 98 // rendering 99 99 // = 100 100 101 - const renderItemInner = React.useCallback( 101 + const renderItemInner = useCallback( 102 102 ({item, index}: {item: any; index: number}) => { 103 103 if (item === EMPTY) { 104 104 return (
+4 -4
src/view/com/modals/UserAddRemoveLists.tsx
··· 1 - import React, {useCallback} from 'react' 1 + import {useCallback, useMemo, useState} from 'react' 2 2 import { 3 3 ActivityIndicator, 4 4 StyleSheet, ··· 56 56 closeModal() 57 57 }, [closeModal]) 58 58 59 - const listStyle = React.useMemo(() => { 59 + const listStyle = useMemo(() => { 60 60 if (IS_WEB_MOBILE) { 61 61 return [pal.border, {height: screenHeight / 2}] 62 62 } else if (IS_WEB) { ··· 141 141 const pal = usePalette('default') 142 142 const {_} = useLingui() 143 143 const {currentAccount} = useSession() 144 - const [isProcessing, setIsProcessing] = React.useState(false) 145 - const membership = React.useMemo( 144 + const [isProcessing, setIsProcessing] = useState(false) 145 + const membership = useMemo( 146 146 () => getMembership(memberships, list.uri, subject), 147 147 [memberships, list.uri, subject], 148 148 )
+9 -9
src/view/com/notifications/NotificationFeed.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useEffect, useMemo, useState} from 'react' 2 2 import { 3 3 ActivityIndicator, 4 4 type ListRenderItemInfo, ··· 45 45 refreshNotifications: () => Promise<void> 46 46 }) { 47 47 const initialNumToRender = useInitialNumToRender() 48 - const [isPTRing, setIsPTRing] = React.useState(false) 48 + const [isPTRing, setIsPTRing] = useState(false) 49 49 const {_} = useLingui() 50 50 const moderationOpts = useModerationOpts() 51 51 const trackPostView = usePostViewTracking('Notifications') ··· 70 70 const isEmpty = 71 71 !isFetching && !data?.pages.find(page => page.items.length > 0) 72 72 73 - const items = React.useMemo(() => { 73 + const items = useMemo(() => { 74 74 let arr: any[] = [] 75 75 if (isFetched) { 76 76 if (isEmpty) { ··· 89 89 return arr 90 90 }, [isFetched, isError, isEmpty, data]) 91 91 92 - const onRefresh = React.useCallback(async () => { 92 + const onRefresh = useCallback(async () => { 93 93 try { 94 94 setIsPTRing(true) 95 95 await refreshNotifications() ··· 102 102 } 103 103 }, [refreshNotifications, setIsPTRing]) 104 104 105 - const onEndReached = React.useCallback(async () => { 105 + const onEndReached = useCallback(async () => { 106 106 if (isFetching || !hasNextPage || isError) return 107 107 108 108 try { ··· 112 112 } 113 113 }, [isFetching, hasNextPage, isError, fetchNextPage]) 114 114 115 - const onPressRetryLoadMore = React.useCallback(() => { 115 + const onPressRetryLoadMore = useCallback(() => { 116 116 fetchNextPage() 117 117 }, [fetchNextPage]) 118 118 119 - const renderItem = React.useCallback( 119 + const renderItem = useCallback( 120 120 ({item, index}: ListRenderItemInfo<any>) => { 121 121 if (item === EMPTY_FEED_ITEM) { 122 122 return ( ··· 150 150 [moderationOpts, _, onPressRetryLoadMore, filter], 151 151 ) 152 152 153 - const FeedFooter = React.useCallback( 153 + const FeedFooter = useCallback( 154 154 () => 155 155 isFetchingNextPage ? ( 156 156 <View style={styles.feedFooter}> ··· 162 162 [isFetchingNextPage], 163 163 ) 164 164 165 - React.useEffect(() => { 165 + useEffect(() => { 166 166 if (!enabled) { 167 167 setIsPTRing(false) 168 168 }
+3 -3
src/view/com/pager/PagerHeaderContext.tsx
··· 1 - import React, {useContext} from 'react' 1 + import {createContext, useContext, useMemo} from 'react' 2 2 import {type SharedValue} from 'react-native-reanimated' 3 3 4 4 import {IS_NATIVE} from '#/env' 5 5 6 - export const PagerHeaderContext = React.createContext<{ 6 + export const PagerHeaderContext = createContext<{ 7 7 scrollY: SharedValue<number> 8 8 headerHeight: number 9 9 } | null>(null) ··· 24 24 headerHeight: number 25 25 children: React.ReactNode 26 26 }) { 27 - const value = React.useMemo( 27 + const value = useMemo( 28 28 () => ({scrollY, headerHeight}), 29 29 [scrollY, headerHeight], 30 30 )
+3 -3
src/view/com/posts/CustomFeedEmptyState.tsx
··· 1 - import React, {useEffect} from 'react' 1 + import {useCallback, useEffect, useRef} from 'react' 2 2 import {StyleSheet, View} from 'react-native' 3 3 import { 4 4 FontAwesomeIcon, ··· 23 23 const ax = useAnalytics() 24 24 const feedFeedback = useFeedFeedbackContext() 25 25 const {currentAccount} = useSession() 26 - const hasLoggedDiscoverEmptyErrorRef = React.useRef(false) 26 + const hasLoggedDiscoverEmptyErrorRef = useRef(false) 27 27 28 28 useEffect(() => { 29 29 // Log the empty feed error event ··· 44 44 const palInverted = usePalette('inverted') 45 45 const navigation = useNavigation<NavigationProp>() 46 46 47 - const onPressFindAccounts = React.useCallback(() => { 47 + const onPressFindAccounts = useCallback(() => { 48 48 if (IS_WEB) { 49 49 navigation.navigate('Search', {}) 50 50 } else {
+3 -3
src/view/com/posts/FeedShutdownMsg.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 38 38 const hasFeedPinned = Boolean(feedConfig) 39 39 const hasDiscoverPinned = Boolean(discoverFeedConfig?.pinned) 40 40 41 - const onRemoveFeed = React.useCallback(async () => { 41 + const onRemoveFeed = useCallback(async () => { 42 42 try { 43 43 if (feedConfig) { 44 44 await removeFeed(feedConfig) ··· 58 58 } 59 59 }, [removeFeed, feedConfig, _, hasDiscoverPinned, setSelectedFeed]) 60 60 61 - const onReplaceFeed = React.useCallback(async () => { 61 + const onReplaceFeed = useCallback(async () => { 62 62 try { 63 63 await replaceFeedWithDiscover({ 64 64 forYouFeedConfig: feedConfig,
+3 -3
src/view/com/posts/FollowingEmptyState.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {StyleSheet, View} from 'react-native' 3 3 import { 4 4 FontAwesomeIcon, ··· 20 20 const palInverted = usePalette('inverted') 21 21 const navigation = useNavigation<NavigationProp>() 22 22 23 - const onPressFindAccounts = React.useCallback(() => { 23 + const onPressFindAccounts = useCallback(() => { 24 24 if (IS_WEB) { 25 25 navigation.navigate('Search', {}) 26 26 } else { ··· 29 29 } 30 30 }, [navigation]) 31 31 32 - const onPressDiscoverFeeds = React.useCallback(() => { 32 + const onPressDiscoverFeeds = useCallback(() => { 33 33 navigation.navigate('Feeds') 34 34 }, [navigation]) 35 35
+3 -3
src/view/com/posts/FollowingEndOfFeed.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {Dimensions, StyleSheet, View} from 'react-native' 3 3 import { 4 4 FontAwesomeIcon, ··· 19 19 const palInverted = usePalette('inverted') 20 20 const navigation = useNavigation<NavigationProp>() 21 21 22 - const onPressFindAccounts = React.useCallback(() => { 22 + const onPressFindAccounts = useCallback(() => { 23 23 if (IS_WEB) { 24 24 navigation.navigate('Search', {}) 25 25 } else { ··· 28 28 } 29 29 }, [navigation]) 30 30 31 - const onPressDiscoverFeeds = React.useCallback(() => { 31 + const onPressDiscoverFeeds = useCallback(() => { 32 32 navigation.navigate('Feeds') 33 33 }, [navigation]) 34 34
+7 -7
src/view/com/posts/PostFeedErrorMessage.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useMemo} from 'react' 2 2 import {View} from 'react-native' 3 3 import { 4 4 type AppBskyActorDefs, ··· 48 48 savedFeedConfig?: AppBskyActorDefs.SavedFeed 49 49 }) { 50 50 const {_: _l} = useLingui() 51 - const knownError = React.useMemo( 51 + const knownError = useMemo( 52 52 () => detectKnownError(feedDesc, error), 53 53 [feedDesc, error], 54 54 ) ··· 101 101 const pal = usePalette('default') 102 102 const {_: _l} = useLingui() 103 103 const navigation = useNavigation<NavigationProp>() 104 - const msg = React.useMemo( 104 + const msg = useMemo( 105 105 () => 106 106 ({ 107 107 [KnownError.Unknown]: '', ··· 135 135 const removePromptControl = Prompt.usePromptControl() 136 136 const {mutateAsync: removeFeed} = useRemoveFeedMutation() 137 137 138 - const onViewProfile = React.useCallback(() => { 138 + const onViewProfile = useCallback(() => { 139 139 navigation.navigate('Profile', {name: ownerDid}) 140 140 }, [navigation, ownerDid]) 141 141 142 - const onPressRemoveFeed = React.useCallback(() => { 142 + const onPressRemoveFeed = useCallback(() => { 143 143 removePromptControl.open() 144 144 }, [removePromptControl]) 145 145 146 - const onRemoveFeed = React.useCallback(async () => { 146 + const onRemoveFeed = useCallback(async () => { 147 147 try { 148 148 if (!savedFeedConfig) return 149 149 await removeFeed(savedFeedConfig) ··· 158 158 } 159 159 }, [removeFeed, _l, savedFeedConfig]) 160 160 161 - const cta = React.useMemo(() => { 161 + const cta = useMemo(() => { 162 162 switch (knownError) { 163 163 case KnownError.FeedSignedInOnly: { 164 164 return null
+2 -2
src/view/com/posts/ViewFullThread.tsx
··· 1 - import React from 'react' 1 + import {useMemo} from 'react' 2 2 import {StyleSheet, View} from 'react-native' 3 3 import Svg, {Circle, Line} from 'react-native-svg' 4 4 import {AtUri} from '@atproto/api' ··· 19 19 onOut: onHoverOut, 20 20 } = useInteractionState() 21 21 const pal = usePalette('default') 22 - const itemHref = React.useMemo(() => { 22 + const itemHref = useMemo(() => { 23 23 const urip = new AtUri(uri) 24 24 return makeProfileLink({did: urip.hostname, handle: ''}, 'post', urip.rkey) 25 25 }, [uri])
+12 -12
src/view/com/profile/ProfileFollowers.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useEffect, useMemo, useRef, useState} from 'react' 2 2 import {type AppBskyActorDefs as ActorDefs} from '@atproto/api' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 47 47 const initialNumToRender = useInitialNumToRender() 48 48 const {currentAccount} = useSession() 49 49 50 - const [isPTRing, setIsPTRing] = React.useState(false) 50 + const [isPTRing, setIsPTRing] = useState(false) 51 51 const { 52 52 data: resolvedDid, 53 53 isLoading: isDidLoading, ··· 66 66 const isError = !!resolveError || !!error 67 67 const isMe = resolvedDid === currentAccount?.did 68 68 69 - const followers = React.useMemo(() => { 69 + const followers = useMemo(() => { 70 70 if (data?.pages) { 71 71 return data.pages.flatMap(page => page.followers) 72 72 } ··· 74 74 }, [data]) 75 75 76 76 // Track pagination events - fire for page 3+ (pages 1-2 may auto-load) 77 - const paginationTrackingRef = React.useRef<{ 77 + const paginationTrackingRef = useRef<{ 78 78 did: string | undefined 79 79 page: number 80 80 }>({did: undefined, page: 0}) 81 - React.useEffect(() => { 81 + useEffect(() => { 82 82 const currentPageCount = data?.pages?.length || 0 83 83 // Reset tracking when profile changes 84 84 if (paginationTrackingRef.current.did !== resolvedDid) { ··· 99 99 paginationTrackingRef.current.page = currentPageCount 100 100 }, [ax, data?.pages?.length, resolvedDid, followers.length]) 101 101 102 - const onRefresh = React.useCallback(async () => { 102 + const onRefresh = useCallback(async () => { 103 103 setIsPTRing(true) 104 104 try { 105 105 await refetch() ··· 109 109 setIsPTRing(false) 110 110 }, [refetch, setIsPTRing]) 111 111 112 - const onEndReached = React.useCallback(async () => { 112 + const onEndReached = useCallback(async () => { 113 113 if (isFetchingNextPage || !hasNextPage || !!error) return 114 114 try { 115 115 await fetchNextPage() ··· 118 118 } 119 119 }, [isFetchingNextPage, hasNextPage, error, fetchNextPage]) 120 120 121 - const renderItemWithContext = React.useCallback( 121 + const renderItemWithContext = useCallback( 122 122 ({item, index}: {item: ActorDefs.ProfileView; index: number}) => 123 123 renderItem({item, index, contextProfileDid: resolvedDid}), 124 124 [resolvedDid], 125 125 ) 126 126 127 127 // track pageview 128 - React.useEffect(() => { 128 + useEffect(() => { 129 129 if (resolvedDid) { 130 130 ax.metric('profile:followers:view', { 131 131 contextProfileDid: resolvedDid, ··· 135 135 }, [ax, resolvedDid, isMe]) 136 136 137 137 // track seen items 138 - const seenItemsRef = React.useRef<Set<string>>(new Set()) 139 - React.useEffect(() => { 138 + const seenItemsRef = useRef<Set<string>>(new Set()) 139 + useEffect(() => { 140 140 seenItemsRef.current.clear() 141 141 }, [resolvedDid]) 142 - const onItemSeen = React.useCallback( 142 + const onItemSeen = useCallback( 143 143 (item: ActorDefs.ProfileView) => { 144 144 if (seenItemsRef.current.has(item.did)) { 145 145 return
+13 -13
src/view/com/profile/ProfileFollows.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useEffect, useMemo, useRef, useState} from 'react' 2 2 import {type AppBskyActorDefs as ActorDefs} from '@atproto/api' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 50 50 const {currentAccount} = useSession() 51 51 const navigation = useNavigation<NavigationProp>() 52 52 53 - const onPressFindAccounts = React.useCallback(() => { 53 + const onPressFindAccounts = useCallback(() => { 54 54 if (IS_WEB) { 55 55 navigation.navigate('Search', {}) 56 56 } else { ··· 59 59 } 60 60 }, [navigation]) 61 61 62 - const [isPTRing, setIsPTRing] = React.useState(false) 62 + const [isPTRing, setIsPTRing] = useState(false) 63 63 const { 64 64 data: resolvedDid, 65 65 isLoading: isDidLoading, ··· 78 78 const isError = !!resolveError || !!error 79 79 const isMe = resolvedDid === currentAccount?.did 80 80 81 - const follows = React.useMemo(() => { 81 + const follows = useMemo(() => { 82 82 if (data?.pages) { 83 83 return data.pages.flatMap(page => page.follows) 84 84 } ··· 86 86 }, [data]) 87 87 88 88 // Track pagination events - fire for page 3+ (pages 1-2 may auto-load) 89 - const paginationTrackingRef = React.useRef<{ 89 + const paginationTrackingRef = useRef<{ 90 90 did: string | undefined 91 91 page: number 92 92 }>({did: undefined, page: 0}) 93 - React.useEffect(() => { 93 + useEffect(() => { 94 94 const currentPageCount = data?.pages?.length || 0 95 95 // Reset tracking when profile changes 96 96 if (paginationTrackingRef.current.did !== resolvedDid) { ··· 111 111 paginationTrackingRef.current.page = currentPageCount 112 112 }, [ax, data?.pages?.length, resolvedDid, follows.length]) 113 113 114 - const onRefresh = React.useCallback(async () => { 114 + const onRefresh = useCallback(async () => { 115 115 setIsPTRing(true) 116 116 try { 117 117 await refetch() ··· 121 121 setIsPTRing(false) 122 122 }, [refetch, setIsPTRing]) 123 123 124 - const onEndReached = React.useCallback(async () => { 124 + const onEndReached = useCallback(async () => { 125 125 if (isFetchingNextPage || !hasNextPage || !!error) return 126 126 try { 127 127 await fetchNextPage() ··· 130 130 } 131 131 }, [isFetchingNextPage, hasNextPage, error, fetchNextPage]) 132 132 133 - const renderItemWithContext = React.useCallback( 133 + const renderItemWithContext = useCallback( 134 134 ({item, index}: {item: ActorDefs.ProfileView; index: number}) => 135 135 renderItem({item, index, contextProfileDid: resolvedDid}), 136 136 [resolvedDid], 137 137 ) 138 138 139 139 // track pageview 140 - React.useEffect(() => { 140 + useEffect(() => { 141 141 if (resolvedDid) { 142 142 ax.metric('profile:following:view', { 143 143 contextProfileDid: resolvedDid, ··· 147 147 }, [ax, resolvedDid, isMe]) 148 148 149 149 // track seen items 150 - const seenItemsRef = React.useRef<Set<string>>(new Set()) 151 - React.useEffect(() => { 150 + const seenItemsRef = useRef<Set<string>>(new Set()) 151 + useEffect(() => { 152 152 seenItemsRef.current.clear() 153 153 }, [resolvedDid]) 154 - const onItemSeen = React.useCallback( 154 + const onItemSeen = useCallback( 155 155 (item: ActorDefs.ProfileView) => { 156 156 if (seenItemsRef.current.has(item.did)) { 157 157 return
+14 -14
src/view/com/profile/ProfileMenu.tsx
··· 1 - import React, {memo} from 'react' 1 + import {memo, useCallback, useMemo} from 'react' 2 2 import {type AppBskyActorDefs} from '@atproto/api' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 108 108 const goLiveDisabledDialogControl = useDialogControl() 109 109 const addToStarterPacksDialogControl = useDialogControl() 110 110 111 - const showLoggedOutWarning = React.useMemo(() => { 111 + const showLoggedOutWarning = useMemo(() => { 112 112 return ( 113 113 profile.did !== currentAccount?.did && 114 114 !!profile.labels?.find(label => label.val === '!no-unauthenticated') 115 115 ) 116 116 }, [currentAccount, profile]) 117 117 118 - const invalidateProfileQuery = React.useCallback(() => { 118 + const invalidateProfileQuery = useCallback(() => { 119 119 queryClient.invalidateQueries({ 120 120 queryKey: profileQueryKey(profile.did), 121 121 }) 122 122 }, [queryClient, profile.did]) 123 123 124 - const onPressAddToStarterPacks = React.useCallback(() => { 124 + const onPressAddToStarterPacks = useCallback(() => { 125 125 ax.metric('profile:addToStarterPack', {}) 126 126 addToStarterPacksDialogControl.open() 127 127 }, [addToStarterPacksDialogControl]) 128 128 129 - const onPressShare = React.useCallback(() => { 129 + const onPressShare = useCallback(() => { 130 130 shareUrl(toShareUrl(makeProfileLink(profile))) 131 131 }, [profile]) 132 132 133 - const onPressAddRemoveLists = React.useCallback(() => { 133 + const onPressAddRemoveLists = useCallback(() => { 134 134 openModal({ 135 135 name: 'user-add-remove-lists', 136 136 subject: profile.did, ··· 141 141 }) 142 142 }, [profile, openModal, invalidateProfileQuery]) 143 143 144 - const onPressMuteAccount = React.useCallback(async () => { 144 + const onPressMuteAccount = useCallback(async () => { 145 145 if (profile.viewer?.muted) { 146 146 try { 147 147 await queueUnmute() ··· 165 165 } 166 166 }, [ax, profile.viewer?.muted, queueUnmute, _, queueMute]) 167 167 168 - const blockAccount = React.useCallback(async () => { 168 + const blockAccount = useCallback(async () => { 169 169 if (profile.viewer?.blocking) { 170 170 try { 171 171 await queueUnblock() ··· 189 189 } 190 190 }, [ax, profile.viewer?.blocking, _, queueUnblock, queueBlock]) 191 191 192 - const onPressFollowAccount = React.useCallback(async () => { 192 + const onPressFollowAccount = useCallback(async () => { 193 193 try { 194 194 await queueFollow() 195 195 Toast.show(_(msg({message: 'Account followed', context: 'toast'}))) ··· 201 201 } 202 202 }, [_, ax, queueFollow]) 203 203 204 - const onPressUnfollowAccount = React.useCallback(async () => { 204 + const onPressUnfollowAccount = useCallback(async () => { 205 205 try { 206 206 await queueUnfollow() 207 207 Toast.show(_(msg({message: 'Account unfollowed', context: 'toast'}))) ··· 213 213 } 214 214 }, [_, ax, queueUnfollow]) 215 215 216 - const onPressReportAccount = React.useCallback(() => { 216 + const onPressReportAccount = useCallback(() => { 217 217 reportDialogControl.open() 218 218 }, [reportDialogControl]) 219 219 220 - const onPressShareATUri = React.useCallback(() => { 220 + const onPressShareATUri = useCallback(() => { 221 221 shareText(`at://${profile.did}`) 222 222 }, [profile.did]) 223 223 224 - const onPressShareDID = React.useCallback(() => { 224 + const onPressShareDID = useCallback(() => { 225 225 shareText(profile.did) 226 226 }, [profile.did]) 227 227 228 - const onPressSearch = React.useCallback(() => { 228 + const onPressSearch = useCallback(() => { 229 229 navigation.navigate('ProfileSearch', {name: profile.handle}) 230 230 }, [navigation, profile.handle]) 231 231
+3 -3
src/view/com/profile/ProfileSubpageHeader.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {Pressable, View} from 'react-native' 3 3 import Animated, { 4 4 measure, ··· 60 60 const canGoBack = navigation.canGoBack() 61 61 const aviRef = useAnimatedRef() 62 62 63 - const _openLightbox = React.useCallback( 63 + const _openLightbox = useCallback( 64 64 (uri: string, thumbRect: MeasuredDimensions | null) => { 65 65 openLightbox({ 66 66 images: [ ··· 83 83 [openLightbox], 84 84 ) 85 85 86 - const onPressAvi = React.useCallback(() => { 86 + const onPressAvi = useCallback(() => { 87 87 if ( 88 88 avatar // TODO && !(view.moderation.avatar.blur && view.moderation.avatar.noOverride) 89 89 ) {
-1
src/view/com/util/BottomSheetCustomBackdrop.tsx
··· 8 8 import {type BottomSheetBackdropProps} from '@discord/bottom-sheet/src' 9 9 import {msg} from '@lingui/core/macro' 10 10 import {useLingui} from '@lingui/react' 11 - import type React from 'react' 12 11 13 12 export function createCustomBackdrop( 14 13 onClose?: () => void,
+3 -3
src/view/com/util/EmptyState.tsx
··· 1 - import React from 'react' 1 + import {isValidElement} from 'react' 2 2 import {type StyleProp, type TextStyle, type ViewStyle} from 'react-native' 3 3 import {View} from 'react-native' 4 4 ··· 45 45 return placeholderIcon 46 46 } 47 47 48 - if (React.isValidElement(icon)) { 48 + if (isValidElement(icon)) { 49 49 return icon 50 50 } 51 51 ··· 77 77 a.rounded_full, 78 78 a.mt_5xl, 79 79 {height: 64, width: 64}, 80 - React.isValidElement(icon) 80 + isValidElement(icon) 81 81 ? a.bg_transparent 82 82 : [isTabletOrDesktop && {marginTop: 50}], 83 83 ]}>
+2 -2
src/view/com/util/MainScrollProvider.tsx
··· 1 - import React, {useCallback, useEffect} from 'react' 1 + import {useCallback, useEffect} from 'react' 2 2 import {type NativeScrollEvent} from 'react-native' 3 3 import { 4 4 clamp, ··· 25 25 const startMode = useSharedValue<number | null>(null) 26 26 const didJustRestoreScroll = useSharedValue<boolean>(false) 27 27 28 - const setMode = React.useCallback( 28 + const setMode = useCallback( 29 29 (v: boolean) => { 30 30 'worklet' 31 31 headerMode.set(() =>
+17 -11
src/view/com/util/ViewSelector.tsx
··· 1 - import React, {type JSX, useEffect, useState} from 'react' 1 + import { 2 + forwardRef, 3 + type JSX, 4 + useCallback, 5 + useEffect, 6 + useImperativeHandle, 7 + useMemo, 8 + useRef, 9 + useState, 10 + } from 'react' 2 11 import { 3 12 type NativeScrollEvent, 4 13 type NativeSyntheticEvent, ··· 25 34 scrollToTop: () => void 26 35 } 27 36 28 - export const ViewSelector = React.forwardRef< 37 + export const ViewSelector = forwardRef< 29 38 ViewSelectorHandle, 30 39 { 31 40 sections: string[] ··· 61 70 ) { 62 71 const pal = usePalette('default') 63 72 const [selectedIndex, setSelectedIndex] = useState<number>(0) 64 - const flatListRef = React.useRef<FlatList_INTERNAL>(null) 73 + const flatListRef = useRef<FlatList_INTERNAL>(null) 65 74 66 75 // events 67 76 // = 68 77 69 - const keyExtractor = React.useCallback((item: any) => item._reactKey, []) 78 + const keyExtractor = useCallback((item: any) => item._reactKey, []) 70 79 71 - const onPressSelection = React.useCallback( 80 + const onPressSelection = useCallback( 72 81 (index: number) => setSelectedIndex(clamp(index, 0, sections.length)), 73 82 [setSelectedIndex, sections], 74 83 ) ··· 76 85 onSelectView?.(selectedIndex) 77 86 }, [selectedIndex, onSelectView]) 78 87 79 - React.useImperativeHandle(ref, () => ({ 88 + useImperativeHandle(ref, () => ({ 80 89 scrollToTop: () => { 81 90 flatListRef.current?.scrollToOffset({offset: 0}) 82 91 }, ··· 85 94 // rendering 86 95 // = 87 96 88 - const renderItemInternal = React.useCallback( 97 + const renderItemInternal = useCallback( 89 98 ({item}: {item: any}) => { 90 99 if (item === HEADER_ITEM) { 91 100 if (renderHeader) { ··· 107 116 [sections, selectedIndex, onPressSelection, renderHeader, renderItem], 108 117 ) 109 118 110 - const data = React.useMemo( 111 - () => [HEADER_ITEM, SELECTOR_ITEM, ...items], 112 - [items], 113 - ) 119 + const data = useMemo(() => [HEADER_ITEM, SELECTOR_ITEM, ...items], [items]) 114 120 return ( 115 121 <FlatList_INTERNAL 116 122 // @ts-expect-error FlatList_INTERNAL ref type is wrong -sfn
+4 -4
src/view/com/util/Views.web.tsx
··· 12 12 * need to match layout but which aren't scrolled. 13 13 */ 14 14 15 - import React from 'react' 15 + import {forwardRef} from 'react' 16 16 import { 17 17 type FlatList, 18 18 type FlatListProps, ··· 37 37 /** 38 38 * @deprecated use `Layout` components 39 39 */ 40 - export const CenteredView = React.forwardRef(function CenteredView( 40 + export const CenteredView = forwardRef(function CenteredView( 41 41 { 42 42 style, 43 43 topBorder, ··· 66 66 return <View ref={ref} style={style} {...props} /> 67 67 }) 68 68 69 - export const FlatList_INTERNAL = React.forwardRef(function FlatListImpl<ItemT>( 69 + export const FlatList_INTERNAL = forwardRef(function FlatListImpl<ItemT>( 70 70 { 71 71 contentContainerStyle, 72 72 style, ··· 141 141 /** 142 142 * @deprecated use `Layout` components 143 143 */ 144 - export const ScrollView = React.forwardRef(function ScrollViewImpl( 144 + export const ScrollView = forwardRef(function ScrollViewImpl( 145 145 {contentContainerStyle, ...props}: React.PropsWithChildren<ScrollViewProps>, 146 146 ref: React.Ref<Animated.ScrollView>, 147 147 ) {
-1
src/view/com/util/WebAuxClickWrapper.tsx
··· 1 1 import {Platform} from 'react-native' 2 - import type React from 'react' 3 2 4 3 const onMouseUp = (e: React.MouseEvent & {target: HTMLElement}) => { 5 4 // Only handle whenever it is the middle button
+5 -5
src/view/com/util/forms/Button.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useState} from 'react' 2 2 import { 3 3 ActivityIndicator, 4 4 type GestureResponderEvent, ··· 148 148 }, 149 149 ) 150 150 151 - const [isLoading, setIsLoading] = React.useState(false) 152 - const onPressWrapped = React.useCallback( 151 + const [isLoading, setIsLoading] = useState(false) 152 + const onPressWrapped = useCallback( 153 153 async (event: GestureResponderEvent) => { 154 154 event.stopPropagation() 155 155 event.preventDefault() ··· 160 160 [onPress, withLoading], 161 161 ) 162 162 163 - const getStyle = React.useCallback( 163 + const getStyle = useCallback( 164 164 (state: PressableStateCallbackType) => { 165 165 const arr = [typeOuterStyle, styles.outer, style] 166 166 if (state.pressed) { ··· 173 173 [typeOuterStyle, style], 174 174 ) 175 175 176 - const renderChildern = React.useCallback(() => { 176 + const renderChildern = useCallback(() => { 177 177 if (!label) { 178 178 return children 179 179 }
-1
src/view/com/util/layouts/LoggedOutLayout.tsx
··· 1 1 import {ScrollView, StyleSheet, View} from 'react-native' 2 - import type React from 'react' 3 2 4 3 import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle' 5 4 import {useIsKeyboardVisible} from '#/lib/hooks/useIsKeyboardVisible'
+2 -2
src/view/com/util/text/Text.tsx
··· 1 - import React from 'react' 1 + import {useMemo} from 'react' 2 2 import {StyleSheet, type TextProps} from 'react-native' 3 3 import {UITextView} from 'react-native-uitextview' 4 4 ··· 57 57 } 58 58 } 59 59 60 - const textProps = React.useMemo(() => { 60 + const textProps = useMemo(() => { 61 61 const typography = theme.typography[type] 62 62 const lineHeightStyle = lineHeight ? lh(theme, type, lineHeight) : undefined 63 63
+2 -2
src/view/icons/Logo.tsx
··· 1 - import React from 'react' 1 + import {forwardRef} from 'react' 2 2 import {type TextProps} from 'react-native' 3 3 import Svg, { 4 4 Defs, ··· 20 20 style?: TextProps['style'] 21 21 } & Omit<SvgProps, 'style'> 22 22 23 - export const Logo = React.forwardRef(function LogoImpl(props: Props, ref) { 23 + export const Logo = forwardRef(function LogoImpl(props: Props, ref) { 24 24 const t = useTheme() 25 25 const {fill, ...rest} = props 26 26 const gradient = fill === 'sky'
+2 -2
src/view/screens/CommunityGuidelines.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 28 28 const setMinimalShellMode = useSetMinimalShellMode() 29 29 30 30 useFocusEffect( 31 - React.useCallback(() => { 31 + useCallback(() => { 32 32 setMinimalShellMode(false) 33 33 }, [setMinimalShellMode]), 34 34 )
+2 -2
src/view/screens/CopyrightPolicy.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 25 25 const setMinimalShellMode = useSetMinimalShellMode() 26 26 27 27 useFocusEffect( 28 - React.useCallback(() => { 28 + useCallback(() => { 29 29 setMinimalShellMode(false) 30 30 }, [setMinimalShellMode]), 31 31 )
+3 -5
src/view/screens/Debug.tsx
··· 1 - import React from 'react' 1 + import {useState} from 'react' 2 2 import {ScrollView, View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 28 28 CommonNavigatorParams, 29 29 'Debug' 30 30 >) => { 31 - const [colorScheme, setColorScheme] = React.useState<'light' | 'dark'>( 32 - 'light', 33 - ) 31 + const [colorScheme, setColorScheme] = useState<'light' | 'dark'>('light') 34 32 const onToggleColorScheme = () => { 35 33 setColorScheme(colorScheme === 'light' ? 'dark' : 'light') 36 34 } ··· 50 48 colorScheme: 'light' | 'dark' 51 49 onToggleColorScheme: () => void 52 50 }) { 53 - const [currentView, setCurrentView] = React.useState<number>(0) 51 + const [currentView, setCurrentView] = useState<number>(0) 54 52 const pal = usePalette('default') 55 53 const {_} = useLingui() 56 54
+17 -17
src/view/screens/DebugMod.tsx
··· 1 - import React from 'react' 1 + import {useMemo, useState} from 'react' 2 2 import {View} from 'react-native' 3 3 import { 4 4 type AppBskyActorDefs, ··· 65 65 'DebugMod' 66 66 >) => { 67 67 const t = useTheme() 68 - const [scenario, setScenario] = React.useState<string[]>(['label']) 69 - const [scenarioSwitches, setScenarioSwitches] = React.useState<string[]>([]) 70 - const [label, setLabel] = React.useState<string[]>([LABEL_VALUES[0]]) 71 - const [target, setTarget] = React.useState<string[]>(['account']) 72 - const [visibility, setVisiblity] = React.useState<string[]>(['warn']) 68 + const [scenario, setScenario] = useState<string[]>(['label']) 69 + const [scenarioSwitches, setScenarioSwitches] = useState<string[]>([]) 70 + const [label, setLabel] = useState<string[]>([LABEL_VALUES[0]]) 71 + const [target, setTarget] = useState<string[]>(['account']) 72 + const [visibility, setVisiblity] = useState<string[]>(['warn']) 73 73 const [customLabelDef, setCustomLabelDef] = 74 - React.useState<ComAtprotoLabelDefs.LabelValueDefinition>({ 74 + useState<ComAtprotoLabelDefs.LabelValueDefinition>({ 75 75 identifier: 'custom', 76 76 blurs: 'content', 77 77 severity: 'alert', ··· 84 84 }, 85 85 ], 86 86 }) 87 - const [view, setView] = React.useState<string[]>(['post']) 87 + const [view, setView] = useState<string[]>(['post']) 88 88 const labelStrings = useGlobalLabelStrings() 89 89 const {currentAccount} = useSession() 90 90 ··· 101 101 const did = 102 102 isTargetMe && currentAccount ? currentAccount.did : 'did:web:bob.test' 103 103 104 - const profile = React.useMemo(() => { 104 + const profile = useMemo(() => { 105 105 const mockedProfile = mock.profileViewBasic({ 106 106 handle: `bob.test`, 107 107 displayName: 'Bob Robertson', ··· 146 146 return mockedProfile 147 147 }, [scenario, target, label, isSelfLabel, did, isFollowing, currentAccount]) 148 148 149 - const post = React.useMemo(() => { 149 + const post = useMemo(() => { 150 150 return mock.postView({ 151 151 record: mock.post({ 152 152 text: "This is the body of the post. It's where the text goes. You get the idea.", ··· 195 195 }) 196 196 }, [scenario, label, target, profile, isSelfLabel, did]) 197 197 198 - const replyNotif = React.useMemo(() => { 198 + const replyNotif = useMemo(() => { 199 199 const notif = mock.replyNotification({ 200 200 record: mock.post({ 201 201 text: "This is the body of the post. It's where the text goes. You get the idea.", ··· 231 231 return item 232 232 }, [scenario, label, target, profile, isSelfLabel, did]) 233 233 234 - const followNotif = React.useMemo(() => { 234 + const followNotif = useMemo(() => { 235 235 const notif = mock.followNotification({ 236 236 author: profile, 237 237 subjectDid: currentAccount?.did || '', ··· 240 240 return item 241 241 }, [profile, currentAccount]) 242 242 243 - const modOpts = React.useMemo(() => { 243 + const modOpts = useMemo(() => { 244 244 return { 245 245 userDid: isLoggedOut ? '' : isTargetMe ? did : 'did:web:alice.test', 246 246 prefs: { ··· 265 265 } 266 266 }, [label, visibility, noAdult, isLoggedOut, isTargetMe, did, customLabelDef]) 267 267 268 - const profileModeration = React.useMemo(() => { 268 + const profileModeration = useMemo(() => { 269 269 return moderateProfile(profile, modOpts) 270 270 }, [profile, modOpts]) 271 - const postModeration = React.useMemo(() => { 271 + const postModeration = useMemo(() => { 272 272 return moderatePost(post, modOpts) 273 273 }, [post, modOpts]) 274 274 ··· 706 706 707 707 function Toggler({label, children}: React.PropsWithChildren<{label: string}>) { 708 708 const t = useTheme() 709 - const [show, setShow] = React.useState(false) 709 + const [show, setShow] = useState(false) 710 710 return ( 711 711 <View style={a.mb_md}> 712 712 <View ··· 738 738 label, 739 739 children, 740 740 }: React.PropsWithChildren<{label: string}>) { 741 - const [show, setShow] = React.useState(false) 741 + const [show, setShow] = useState(false) 742 742 return ( 743 743 <View> 744 744 <View style={[a.flex_row]}>
+15 -15
src/view/screens/Feeds.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useMemo, useRef, useState} from 'react' 2 2 import {ActivityIndicator, StyleSheet, View} from 'react-native' 3 3 import {type AppBskyFeedDefs} from '@atproto/api' 4 4 import {msg} from '@lingui/core/macro' ··· 108 108 const pal = usePalette('default') 109 109 const {openComposer} = useOpenComposer() 110 110 const {isMobile} = useWebMediaQueries() 111 - const [query, setQuery] = React.useState('') 112 - const [isPTR, setIsPTR] = React.useState(false) 111 + const [query, setQuery] = useState('') 112 + const [isPTR, setIsPTR] = useState(false) 113 113 const { 114 114 data: savedFeeds, 115 115 isPlaceholderData: isSavedFeedsPlaceholder, ··· 135 135 error: searchError, 136 136 } = useSearchPopularFeedsMutation() 137 137 const {hasSession} = useSession() 138 - const listRef = React.useRef<ListMethods>(null) 138 + const listRef = useRef<ListMethods>(null) 139 139 140 140 /** 141 141 * A search query is present. We may not have search results yet. 142 142 */ 143 143 const isUserSearching = query.length > 1 144 - const debouncedSearch = React.useMemo( 144 + const debouncedSearch = useMemo( 145 145 () => debounce(q => search(q), 500), // debounce for 500ms 146 146 [search], 147 147 ) 148 - const onPressCompose = React.useCallback(() => { 148 + const onPressCompose = useCallback(() => { 149 149 openComposer({logContext: 'Fab'}) 150 150 }, [openComposer]) 151 - const onChangeQuery = React.useCallback( 151 + const onChangeQuery = useCallback( 152 152 (text: string) => { 153 153 setQuery(text) 154 154 if (text.length > 1) { ··· 160 160 }, 161 161 [setQuery, refetchPopularFeeds, debouncedSearch, resetSearch], 162 162 ) 163 - const onPressCancelSearch = React.useCallback(() => { 163 + const onPressCancelSearch = useCallback(() => { 164 164 setQuery('') 165 165 refetchPopularFeeds() 166 166 resetSearch() 167 167 }, [refetchPopularFeeds, setQuery, resetSearch]) 168 - const onSubmitQuery = React.useCallback(() => { 168 + const onSubmitQuery = useCallback(() => { 169 169 debouncedSearch(query) 170 170 }, [query, debouncedSearch]) 171 - const onPullToRefresh = React.useCallback(async () => { 171 + const onPullToRefresh = useCallback(async () => { 172 172 setIsPTR(true) 173 173 await Promise.all([ 174 174 refetchSavedFeeds().catch(_e => undefined), ··· 176 176 ]) 177 177 setIsPTR(false) 178 178 }, [setIsPTR, refetchSavedFeeds, refetchPopularFeeds]) 179 - const onEndReached = React.useCallback(() => { 179 + const onEndReached = useCallback(() => { 180 180 if ( 181 181 isPopularFeedsFetching || 182 182 isUserSearching || ··· 194 194 ]) 195 195 196 196 useFocusEffect( 197 - React.useCallback(() => { 197 + useCallback(() => { 198 198 setMinimalShellMode(false) 199 199 }, [setMinimalShellMode]), 200 200 ) 201 201 202 - const items = React.useMemo(() => { 202 + const items = useMemo(() => { 203 203 let slices: FlatlistSlice[] = [] 204 204 const hasActualSavedCount = 205 205 !isSavedFeedsPlaceholder || ··· 383 383 item => item.type === 'popularFeedsHeader', 384 384 ) 385 385 386 - const onChangeSearchFocus = React.useCallback( 386 + const onChangeSearchFocus = useCallback( 387 387 (focus: boolean) => { 388 388 if (focus && searchBarIndex > -1) { 389 389 if (IS_NATIVE) { ··· 408 408 [searchBarIndex, isMobile], 409 409 ) 410 410 411 - const renderItem = React.useCallback( 411 + const renderItem = useCallback( 412 412 ({item}: {item: FlatlistSlice}) => { 413 413 if (item.type === 'error') { 414 414 return <ErrorMessage message={item.error} />
+15 -15
src/view/screens/Home.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useEffect, useLayoutEffect, useMemo, useRef} from 'react' 2 2 import {ActivityIndicator, StyleSheet} from 'react-native' 3 3 import {useFocusEffect} from '@react-navigation/native' 4 4 ··· 47 47 const {data: pinnedFeedInfos, isLoading: isPinnedFeedsLoading} = 48 48 usePinnedFeedsInfos() 49 49 50 - React.useEffect(() => { 50 + useEffect(() => { 51 51 if (IS_WEB && !currentAccount) { 52 52 const getParams = new URLSearchParams(window.location.search) 53 53 const splash = getParams.get('splash') ··· 106 106 pinnedFeedInfos: SavedFeedSourceInfo[] 107 107 }) { 108 108 const ax = useAnalytics() 109 - const allFeeds = React.useMemo( 109 + const allFeeds = useMemo( 110 110 () => pinnedFeedInfos.map(f => f.feedDescriptor), 111 111 [pinnedFeedInfos], 112 112 ) ··· 121 121 useSetTitle(pinnedFeedInfos[selectedIndex]?.displayName) 122 122 useOTAUpdates() 123 123 124 - React.useEffect(() => { 124 + useEffect(() => { 125 125 requestNotificationsPermission('Home') 126 126 }, [requestNotificationsPermission]) 127 127 128 - const pagerRef = React.useRef<PagerRef>(null) 129 - const lastPagerReportedIndexRef = React.useRef(selectedIndex) 130 - React.useLayoutEffect(() => { 128 + const pagerRef = useRef<PagerRef>(null) 129 + const lastPagerReportedIndexRef = useRef(selectedIndex) 130 + useLayoutEffect(() => { 131 131 // Since the pager is not a controlled component, adjust it imperatively 132 132 // if the selected index gets out of sync with what it last reported. 133 133 // This is supposed to only happen on the web when you use the right nav. ··· 140 140 const {hasSession} = useSession() 141 141 const setMinimalShellMode = useSetMinimalShellMode() 142 142 useFocusEffect( 143 - React.useCallback(() => { 143 + useCallback(() => { 144 144 setMinimalShellMode(false) 145 145 }, [setMinimalShellMode]), 146 146 ) ··· 158 158 }), 159 159 ) 160 160 161 - const onPageSelected = React.useCallback( 161 + const onPageSelected = useCallback( 162 162 (index: number) => { 163 163 setMinimalShellMode(false) 164 164 const maybeFeed = allFeeds[index] ··· 179 179 [ax, setSelectedFeed, setMinimalShellMode, allFeeds], 180 180 ) 181 181 182 - const onPressSelected = React.useCallback(() => { 182 + const onPressSelected = useCallback(() => { 183 183 emitSoftReset() 184 184 }, []) 185 185 186 - const onPageScrollStateChanged = React.useCallback( 186 + const onPageScrollStateChanged = useCallback( 187 187 (state: 'idle' | 'dragging' | 'settling') => { 188 188 'worklet' 189 189 if (state === 'dragging') { ··· 195 195 196 196 const [demoMode] = useDemoMode() 197 197 198 - const renderTabBar = React.useCallback( 198 + const renderTabBar = useCallback( 199 199 (props: RenderTabBarFnProps) => { 200 200 if (demoMode) { 201 201 return ( ··· 222 222 [onPressSelected, pinnedFeedInfos, demoMode], 223 223 ) 224 224 225 - const renderFollowingEmptyState = React.useCallback(() => { 225 + const renderFollowingEmptyState = useCallback(() => { 226 226 return <FollowingEmptyState /> 227 227 }, []) 228 228 229 - const renderCustomFeedEmptyState = React.useCallback(() => { 229 + const renderCustomFeedEmptyState = useCallback(() => { 230 230 return <CustomFeedEmptyState /> 231 231 }, []) 232 232 233 - const homeFeedParams = React.useMemo<FeedParams>(() => { 233 + const homeFeedParams = useMemo<FeedParams>(() => { 234 234 return { 235 235 mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled), 236 236 mergeFeedSources: preferences.feedViewPrefs.lab_mergeFeedEnabled
+3 -3
src/view/screens/NotFound.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {StyleSheet, View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 25 25 const setMinimalShellMode = useSetMinimalShellMode() 26 26 27 27 useFocusEffect( 28 - React.useCallback(() => { 28 + useCallback(() => { 29 29 setMinimalShellMode(false) 30 30 }, [setMinimalShellMode]), 31 31 ) 32 32 33 33 const canGoBack = navigation.canGoBack() 34 - const onPressHome = React.useCallback(() => { 34 + const onPressHome = useCallback(() => { 35 35 if (canGoBack) { 36 36 navigation.goBack() 37 37 } else {
+2 -2
src/view/screens/PrivacyPolicy.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 25 25 const setMinimalShellMode = useSetMinimalShellMode() 26 26 27 27 useFocusEffect( 28 - React.useCallback(() => { 28 + useCallback(() => { 29 29 setMinimalShellMode(false) 30 30 }, [setMinimalShellMode]), 31 31 )
+4 -6
src/view/screens/Storybook/Dialogs.tsx
··· 1 - import React from 'react' 1 + import {useRef, useState} from 'react' 2 2 import {View} from 'react-native' 3 3 4 4 import {useDialogStateControlContext} from '#/state/dialogs' ··· 18 18 const testDialog = Dialog.useDialogControl() 19 19 const {closeAllDialogs} = useDialogStateControlContext() 20 20 const unmountTestDialog = Dialog.useDialogControl() 21 - const [reducedMotionEnabled, setReducedMotionEnabled] = 22 - React.useState<boolean>() 23 - const [shouldRenderUnmountTest, setShouldRenderUnmountTest] = 24 - React.useState(false) 25 - const unmountTestInterval = React.useRef<number>(undefined) 21 + const [reducedMotionEnabled, setReducedMotionEnabled] = useState<boolean>() 22 + const [shouldRenderUnmountTest, setShouldRenderUnmountTest] = useState(false) 23 + const unmountTestInterval = useRef<number>(undefined) 26 24 27 25 const onUnmountTestStartPressWithClose = () => { 28 26 setShouldRenderUnmountTest(true)
+12 -12
src/view/screens/Storybook/Forms.tsx
··· 1 - import React from 'react' 1 + import {useRef, useState} from 'react' 2 2 import {type TextInput, View} from 'react-native' 3 3 4 4 import {APP_LANGUAGES} from '#/lib/../locale/languages' ··· 16 16 import {H1, H3} from '#/components/Typography' 17 17 18 18 export function Forms() { 19 - const [toggleGroupAValues, setToggleGroupAValues] = React.useState(['a']) 20 - const [toggleGroupBValues, setToggleGroupBValues] = React.useState(['a', 'b']) 21 - const [toggleGroupCValues, setToggleGroupCValues] = React.useState(['a', 'b']) 22 - const [toggleGroupDValues, setToggleGroupDValues] = React.useState(['warn']) 23 - const [segmentedControlValue, setSegmentedControlValue] = React.useState< 19 + const [toggleGroupAValues, setToggleGroupAValues] = useState(['a']) 20 + const [toggleGroupBValues, setToggleGroupBValues] = useState(['a', 'b']) 21 + const [toggleGroupCValues, setToggleGroupCValues] = useState(['a', 'b']) 22 + const [toggleGroupDValues, setToggleGroupDValues] = useState(['warn']) 23 + const [segmentedControlValue, setSegmentedControlValue] = useState< 24 24 'hide' | 'warn' | 'show' 25 25 >('warn') 26 26 27 - const [value, setValue] = React.useState('') 28 - const [date, setDate] = React.useState('2001-01-01') 29 - const [countryCode, setCountryCode] = React.useState<CountryCode>('US') 30 - const [phoneNumber, setPhoneNumber] = React.useState('') 31 - const [lang, setLang] = React.useState('en') 27 + const [value, setValue] = useState('') 28 + const [date, setDate] = useState('2001-01-01') 29 + const [countryCode, setCountryCode] = useState<CountryCode>('US') 30 + const [phoneNumber, setPhoneNumber] = useState('') 31 + const [lang, setLang] = useState('en') 32 32 33 - const inputRef = React.useRef<TextInput>(null) 33 + const inputRef = useRef<TextInput>(null) 34 34 35 35 return ( 36 36 <View style={[a.gap_4xl, a.align_start]}>
+4 -4
src/view/screens/Storybook/ListContained.tsx
··· 1 - import React from 'react' 1 + import {useMemo, useRef, useState} from 'react' 2 2 import {View} from 'react-native' 3 3 4 4 import {ScrollProvider} from '#/lib/ScrollContext' ··· 8 8 import {Text} from '#/components/Typography' 9 9 10 10 export function ListContained() { 11 - const [animated, setAnimated] = React.useState(false) 12 - const ref = React.useRef<ListMethods>(null) 11 + const [animated, setAnimated] = useState(false) 12 + const ref = useRef<ListMethods>(null) 13 13 14 - const data = React.useMemo(() => { 14 + const data = useMemo(() => { 15 15 return Array.from({length: 100}, (_, i) => ({ 16 16 id: i, 17 17 text: `Message ${i}`,
+2 -2
src/view/screens/Storybook/Storybook.tsx
··· 1 - import React from 'react' 1 + import {useState} from 'react' 2 2 import {View} from 'react-native' 3 3 import {useNavigation} from '@react-navigation/native' 4 4 ··· 28 28 29 29 export default function Storybook() { 30 30 const {setColorMode, setDarkTheme} = useSetThemePrefs() 31 - const [showContainedList, setShowContainedList] = React.useState(false) 31 + const [showContainedList, setShowContainedList] = useState(false) 32 32 const navigation = useNavigation<NavigationProp>() 33 33 const requestDeviceGeolocation = useRequestDeviceGeolocation() 34 34 const {setDeviceGeolocation} = useDeviceGeolocationApi()
+2 -2
src/view/screens/Support.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {msg} from '@lingui/core/macro' 3 3 import {useLingui} from '@lingui/react' 4 4 import {Trans} from '@lingui/react/macro' ··· 25 25 const {_} = useLingui() 26 26 27 27 useFocusEffect( 28 - React.useCallback(() => { 28 + useCallback(() => { 29 29 setMinimalShellMode(false) 30 30 }, [setMinimalShellMode]), 31 31 )
+2 -2
src/view/screens/TermsOfService.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 25 25 const {_} = useLingui() 26 26 27 27 useFocusEffect( 28 - React.useCallback(() => { 28 + useCallback(() => { 29 29 setMinimalShellMode(false) 30 30 }, [setMinimalShellMode]), 31 31 )
+3 -3
src/view/shell/Composer.ios.tsx
··· 1 - import React from 'react' 1 + import {useEffect, useRef} from 'react' 2 2 import {Modal, View} from 'react-native' 3 3 4 4 import {useDialogStateControlContext} from '#/state/dialogs' ··· 14 14 const ref = useComposerCancelRef() 15 15 16 16 const open = !!state 17 - const prevOpen = React.useRef(open) 17 + const prevOpen = useRef(open) 18 18 19 - React.useEffect(() => { 19 + useEffect(() => { 20 20 if (open && !prevOpen.current) { 21 21 setFullyExpandedCount(c => c + 1) 22 22 } else if (!open && prevOpen.current) {
+10 -13
src/view/shell/Composer.web.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useState} from 'react' 2 2 import {StyleSheet, View} from 'react-native' 3 3 import {DismissableLayer, FocusGuards, FocusScope} from 'radix-ui/internal' 4 4 import {RemoveScrollBar} from 'react-remove-scroll-bar' ··· 41 41 const t = useTheme() 42 42 const {gtMobile} = useBreakpoints() 43 43 const {reduceMotionEnabled} = useA11y() 44 - const [pickerState, setPickerState] = React.useState<EmojiPickerState>({ 44 + const [pickerState, setPickerState] = useState<EmojiPickerState>({ 45 45 isOpen: false, 46 46 pos: {top: 0, left: 0, right: 0, bottom: 0, nextFocusRef: null}, 47 47 }) 48 48 49 - const onOpenPicker = React.useCallback( 50 - (pos: EmojiPickerPosition | undefined) => { 51 - if (!pos) return 52 - setPickerState({ 53 - isOpen: true, 54 - pos, 55 - }) 56 - }, 57 - [], 58 - ) 49 + const onOpenPicker = useCallback((pos: EmojiPickerPosition | undefined) => { 50 + if (!pos) return 51 + setPickerState({ 52 + isOpen: true, 53 + pos, 54 + }) 55 + }, []) 59 56 60 - const onClosePicker = React.useCallback(() => { 57 + const onClosePicker = useCallback(() => { 61 58 setPickerState(prev => ({ 62 59 ...prev, 63 60 isOpen: false,
+25 -28
src/view/shell/Drawer.tsx
··· 1 - import React, {type ComponentProps, type JSX} from 'react' 1 + import {type ComponentProps, type JSX, memo, useCallback} from 'react' 2 2 import {Linking, ScrollView, TouchableOpacity, View} from 'react-native' 3 3 import {useSafeAreaInsets} from 'react-native-safe-area-context' 4 4 import {msg, plural} from '@lingui/core/macro' ··· 130 130 </TouchableOpacity> 131 131 ) 132 132 } 133 - DrawerProfileCard = React.memo(DrawerProfileCard) 133 + DrawerProfileCard = memo(DrawerProfileCard) 134 134 export {DrawerProfileCard} 135 135 136 136 let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => { ··· 152 152 // events 153 153 // = 154 154 155 - const onPressTab = React.useCallback( 155 + const onPressTab = useCallback( 156 156 (tab: 'Home' | 'Search' | 'Messages' | 'Notifications' | 'MyProfile') => { 157 157 const state = navigation.getState() 158 158 setDrawerOpen(false) ··· 193 193 [navigation, setDrawerOpen, currentAccount], 194 194 ) 195 195 196 - const onPressHome = React.useCallback(() => onPressTab('Home'), [onPressTab]) 196 + const onPressHome = useCallback(() => onPressTab('Home'), [onPressTab]) 197 197 198 - const onPressSearch = React.useCallback( 199 - () => onPressTab('Search'), 200 - [onPressTab], 201 - ) 198 + const onPressSearch = useCallback(() => onPressTab('Search'), [onPressTab]) 202 199 203 - const onPressMessages = React.useCallback( 200 + const onPressMessages = useCallback( 204 201 () => onPressTab('Messages'), 205 202 [onPressTab], 206 203 ) 207 204 208 - const onPressNotifications = React.useCallback( 205 + const onPressNotifications = useCallback( 209 206 () => onPressTab('Notifications'), 210 207 [onPressTab], 211 208 ) 212 209 213 - const onPressProfile = React.useCallback(() => { 210 + const onPressProfile = useCallback(() => { 214 211 onPressTab('MyProfile') 215 212 }, [onPressTab]) 216 213 217 - const onPressMyFeeds = React.useCallback(() => { 214 + const onPressMyFeeds = useCallback(() => { 218 215 navigation.navigate('Feeds') 219 216 setDrawerOpen(false) 220 217 }, [navigation, setDrawerOpen]) 221 218 222 - const onPressLists = React.useCallback(() => { 219 + const onPressLists = useCallback(() => { 223 220 navigation.navigate('Lists') 224 221 setDrawerOpen(false) 225 222 }, [navigation, setDrawerOpen]) 226 223 227 - const onPressBookmarks = React.useCallback(() => { 224 + const onPressBookmarks = useCallback(() => { 228 225 navigation.navigate('Bookmarks') 229 226 setDrawerOpen(false) 230 227 }, [navigation, setDrawerOpen]) 231 228 232 - const onPressSettings = React.useCallback(() => { 229 + const onPressSettings = useCallback(() => { 233 230 navigation.navigate('Settings') 234 231 setDrawerOpen(false) 235 232 }, [navigation, setDrawerOpen]) 236 233 237 - const onPressFeedback = React.useCallback(() => { 234 + const onPressFeedback = useCallback(() => { 238 235 Linking.openURL( 239 236 FEEDBACK_FORM_URL({ 240 237 email: currentAccount?.email, ··· 243 240 ) 244 241 }, [currentAccount]) 245 242 246 - const onPressHelp = React.useCallback(() => { 243 + const onPressHelp = useCallback(() => { 247 244 Linking.openURL(HELP_DESK_URL) 248 245 }, []) 249 246 ··· 321 318 </View> 322 319 ) 323 320 } 324 - DrawerContent = React.memo(DrawerContent) 321 + DrawerContent = memo(DrawerContent) 325 322 export {DrawerContent} 326 323 327 324 let DrawerFooter = ({ ··· 375 372 </View> 376 373 ) 377 374 } 378 - DrawerFooter = React.memo(DrawerFooter) 375 + DrawerFooter = memo(DrawerFooter) 379 376 380 377 interface MenuItemProps extends ComponentProps<typeof PressableScale> { 381 378 icon: JSX.Element ··· 408 405 /> 409 406 ) 410 407 } 411 - SearchMenuItem = React.memo(SearchMenuItem) 408 + SearchMenuItem = memo(SearchMenuItem) 412 409 413 410 let HomeMenuItem = ({ 414 411 isActive, ··· 434 431 /> 435 432 ) 436 433 } 437 - HomeMenuItem = React.memo(HomeMenuItem) 434 + HomeMenuItem = memo(HomeMenuItem) 438 435 439 436 let ChatMenuItem = ({ 440 437 isActive, ··· 460 457 /> 461 458 ) 462 459 } 463 - ChatMenuItem = React.memo(ChatMenuItem) 460 + ChatMenuItem = memo(ChatMenuItem) 464 461 465 462 let NotificationsMenuItem = ({ 466 463 isActive, ··· 498 495 /> 499 496 ) 500 497 } 501 - NotificationsMenuItem = React.memo(NotificationsMenuItem) 498 + NotificationsMenuItem = memo(NotificationsMenuItem) 502 499 503 500 let FeedsMenuItem = ({ 504 501 isActive, ··· 524 521 /> 525 522 ) 526 523 } 527 - FeedsMenuItem = React.memo(FeedsMenuItem) 524 + FeedsMenuItem = memo(FeedsMenuItem) 528 525 529 526 let ListsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => { 530 527 const {_} = useLingui() ··· 538 535 /> 539 536 ) 540 537 } 541 - ListsMenuItem = React.memo(ListsMenuItem) 538 + ListsMenuItem = memo(ListsMenuItem) 542 539 543 540 let BookmarksMenuItem = ({ 544 541 isActive, ··· 564 561 /> 565 562 ) 566 563 } 567 - BookmarksMenuItem = React.memo(BookmarksMenuItem) 564 + BookmarksMenuItem = memo(BookmarksMenuItem) 568 565 569 566 let ProfileMenuItem = ({ 570 567 isActive, ··· 589 586 /> 590 587 ) 591 588 } 592 - ProfileMenuItem = React.memo(ProfileMenuItem) 589 + ProfileMenuItem = memo(ProfileMenuItem) 593 590 594 591 let SettingsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => { 595 592 const {_} = useLingui() ··· 602 599 /> 603 600 ) 604 601 } 605 - SettingsMenuItem = React.memo(SettingsMenuItem) 602 + SettingsMenuItem = memo(SettingsMenuItem) 606 603 607 604 function MenuItem({icon, label, count, bold, onPress}: MenuItemProps) { 608 605 const t = useTheme()
+4 -4
src/view/shell/NavSignupCard.tsx
··· 1 - import React from 'react' 1 + import {memo, useCallback} from 'react' 2 2 import {View} from 'react-native' 3 3 import {msg} from '@lingui/core/macro' 4 4 import {useLingui} from '@lingui/react' ··· 18 18 const {requestSwitchToAccount} = useLoggedOutViewControls() 19 19 const closeAllActiveElements = useCloseAllActiveElements() 20 20 21 - const showSignIn = React.useCallback(() => { 21 + const showSignIn = useCallback(() => { 22 22 closeAllActiveElements() 23 23 requestSwitchToAccount({requestedAccount: 'none'}) 24 24 }, [requestSwitchToAccount, closeAllActiveElements]) 25 25 26 - const showCreateAccount = React.useCallback(() => { 26 + const showCreateAccount = useCallback(() => { 27 27 closeAllActiveElements() 28 28 requestSwitchToAccount({requestedAccount: 'new'}) 29 29 // setShowLoggedOut(true) ··· 71 71 </View> 72 72 ) 73 73 } 74 - NavSignupCard = React.memo(NavSignupCard) 74 + NavSignupCard = memo(NavSignupCard) 75 75 export {NavSignupCard}
+4 -4
src/view/shell/bottom-bar/BottomBarWeb.tsx
··· 1 - import React from 'react' 1 + import {useCallback} from 'react' 2 2 import {View} from 'react-native' 3 3 import Animated from 'react-native-reanimated' 4 4 import {msg, plural} from '@lingui/core/macro' ··· 61 61 const unreadMessageCount = useUnreadMessageCount() 62 62 const notificationCountStr = useUnreadNotifications() 63 63 64 - const showSignIn = React.useCallback(() => { 64 + const showSignIn = useCallback(() => { 65 65 closeAllActiveElements() 66 66 requestSwitchToAccount({requestedAccount: 'none'}) 67 67 }, [requestSwitchToAccount, closeAllActiveElements]) 68 68 69 - const showCreateAccount = React.useCallback(() => { 69 + const showCreateAccount = useCallback(() => { 70 70 closeAllActiveElements() 71 71 requestSwitchToAccount({requestedAccount: 'new'}) 72 72 // setShowLoggedOut(true) 73 73 }, [requestSwitchToAccount, closeAllActiveElements]) 74 74 75 - const onLongPressProfile = React.useCallback(() => { 75 + const onLongPressProfile = useCallback(() => { 76 76 accountSwitchControl.open() 77 77 }, [accountSwitchControl]) 78 78