Create codemod for addressing ESLint warnings (#10032)

This commit is contained in:
DS Boyce
2026-03-11 15:51:31 -07:00
committed by GitHub
parent 80429ec902
commit 1db01a09a8
240 changed files with 1560 additions and 1347 deletions
+3 -3
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useEffect, useState} from 'react'
import {Pressable, View} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg} from '@lingui/core/macro'
@@ -33,9 +33,9 @@ export const SplashScreen = ({
const {_} = useLingui()
const t = useTheme()
const {isTabletOrMobile: IS_WEB_MOBILE} = useWebMediaQueries()
const [showClipOverlay, setShowClipOverlay] = React.useState(false)
const [showClipOverlay, setShowClipOverlay] = useState(false)
React.useEffect(() => {
useEffect(() => {
const getParams = new URLSearchParams(window.location.search)
const clip = getParams.get('clip')
if (clip === 'true') {
@@ -1,5 +1,4 @@
import {View} from 'react-native'
import type React from 'react'
import {MAX_ALT_TEXT} from '#/lib/constants'
import {CharProgress} from '#/view/com/composer/char-progress/CharProgress'
+17 -15
View File
@@ -1,4 +1,6 @@
import React, {
import {
Fragment,
memo,
useCallback,
useEffect,
useImperativeHandle,
@@ -299,7 +301,7 @@ export const ComposePost = ({
[activePost.id],
)
const selectVideo = React.useCallback(
const selectVideo = useCallback(
(postId: string, asset: ImagePickerAsset) => {
const abortController = new AbortController()
composerDispatch({
@@ -485,7 +487,7 @@ export const ComposePost = ({
[_, agent, currentDid, composerDispatch],
)
const handleSelectDraft = React.useCallback(
const handleSelectDraft = useCallback(
async (draftSummary: DraftSummary) => {
logger.debug('loading draft for editing', {
draftId: draftSummary.id,
@@ -553,7 +555,7 @@ export const ComposePost = ({
revokeAllMediaUrls()
}, [closeComposer, queryClient])
const getDraftSaveError = React.useCallback(
const getDraftSaveError = useCallback(
(e: unknown): string => {
if (e instanceof AppBskyDraftCreateDraft.DraftLimitReachedError) {
return _(msg`You've reached the maximum number of drafts`)
@@ -563,7 +565,7 @@ export const ComposePost = ({
[_],
)
const validateDraftTextOrError = React.useCallback((): boolean => {
const validateDraftTextOrError = useCallback((): boolean => {
const tooLong = composerState.thread.posts.some(
post => post.richtext.graphemeLength > MAX_DRAFT_GRAPHEME_LENGTH,
)
@@ -578,7 +580,7 @@ export const ComposePost = ({
return true
}, [composerState.thread.posts, _])
const handleSaveDraft = React.useCallback(async () => {
const handleSaveDraft = useCallback(async () => {
setError('')
if (!validateDraftTextOrError()) {
return
@@ -621,7 +623,7 @@ export const ComposePost = ({
])
// Save without closing - for use by DraftsButton
const saveCurrentDraft = React.useCallback(async (): Promise<{
const saveCurrentDraft = useCallback(async (): Promise<{
success: boolean
}> => {
setError('')
@@ -648,7 +650,7 @@ export const ComposePost = ({
])
// Handle discard action - fires metric and closes composer
const handleDiscard = React.useCallback(() => {
const handleDiscard = useCallback(() => {
const posts = thread.posts
const hasContent = posts.some(
post =>
@@ -665,7 +667,7 @@ export const ComposePost = ({
}, [thread.posts, ax, onClose])
// Check if composer is empty (no content to save)
const isComposerEmpty = React.useMemo(() => {
const isComposerEmpty = useMemo(() => {
// Has multiple posts means it's not empty
if (thread.posts.length > 1) return false
@@ -683,7 +685,7 @@ export const ComposePost = ({
}, [thread.posts])
// Clear the composer (discard current content)
const handleClearComposer = React.useCallback(() => {
const handleClearComposer = useCallback(() => {
composerDispatch({
type: 'clear',
initInteractionSettings: preferences?.postInteractionSettings,
@@ -794,7 +796,7 @@ export const ComposePost = ({
),
)
const onPressPublish = React.useCallback(async () => {
const onPressPublish = useCallback(async () => {
if (isPublishing) {
return
}
@@ -1015,7 +1017,7 @@ export const ComposePost = ({
onPressPublish()
})
React.useEffect(() => {
useEffect(() => {
if (publishOnUpload) {
let erroredVideos = 0
let uploadingVideos = 0
@@ -1181,7 +1183,7 @@ export const ComposePost = ({
onLayout={onScrollViewLayout}>
{replyTo ? <ComposerReplyTo replyTo={replyTo} /> : undefined}
{thread.posts.map((post, index) => (
<React.Fragment key={post.id + (composerState.draftId ?? '')}>
<Fragment key={post.id + (composerState.draftId ?? '')}>
<ComposerPost
post={post}
dispatch={composerDispatch}
@@ -1201,7 +1203,7 @@ export const ComposePost = ({
{IS_WEBFooterSticky && post.id === activePost.id && (
<View style={styles.stickyFooterWeb}>{footer}</View>
)}
</React.Fragment>
</Fragment>
))}
</Animated.ScrollView>
{!IS_WEBFooterSticky && footer}
@@ -1273,7 +1275,7 @@ export const ComposePost = ({
)
}
let ComposerPost = React.memo(function ComposerPost({
let ComposerPost = memo(function ComposerPost({
post,
dispatch,
textInput,
@@ -1,7 +1,6 @@
import {View} from 'react-native'
import {KeyboardStickyView} from 'react-native-keyboard-controller'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import type React from 'react'
import {atoms as a, useTheme} from '#/alf'
import {IS_WEB} from '#/env'
+32 -33
View File
@@ -1,4 +1,4 @@
import React, {useState} from 'react'
import {memo, useMemo, useState} from 'react'
import {
findNodeHandle,
type ImageStyle,
@@ -37,7 +37,7 @@ interface GalleryProps {
}
export let Gallery = (props: GalleryProps): React.ReactNode => {
const [containerInfo, setContainerInfo] = React.useState<Dimensions>()
const [containerInfo, setContainerInfo] = useState<Dimensions>()
const onLayout = (evt: LayoutChangeEvent) => {
const {width, height} = evt.nativeEvent.layout
@@ -55,7 +55,7 @@ export let Gallery = (props: GalleryProps): React.ReactNode => {
</View>
)
}
Gallery = React.memo(Gallery)
Gallery = memo(Gallery)
interface GalleryInnerProps extends GalleryProps {
containerInfo: Dimensions
@@ -64,39 +64,38 @@ interface GalleryInnerProps extends GalleryProps {
const GalleryInner = ({images, containerInfo, dispatch}: GalleryInnerProps) => {
const {isMobile} = useWebMediaQueries()
const {altTextControlStyle, imageControlsStyle, imageStyle} =
React.useMemo(() => {
const side =
images.length === 1
? 250
: (containerInfo.width - IMAGE_GAP * (images.length - 1)) /
images.length
const {altTextControlStyle, imageControlsStyle, imageStyle} = useMemo(() => {
const side =
images.length === 1
? 250
: (containerInfo.width - IMAGE_GAP * (images.length - 1)) /
images.length
const isOverflow = isMobile && images.length > 2
const isOverflow = isMobile && images.length > 2
return {
altTextControlStyle: isOverflow
? {left: 4, bottom: 4}
return {
altTextControlStyle: isOverflow
? {left: 4, bottom: 4}
: !isMobile && images.length < 3
? {left: 8, top: 8}
: {left: 4, top: 4},
imageControlsStyle: {
display: 'flex' as const,
flexDirection: 'row' as const,
position: 'absolute' as const,
...(isOverflow
? {top: 4, right: 4, gap: 4}
: !isMobile && images.length < 3
? {left: 8, top: 8}
: {left: 4, top: 4},
imageControlsStyle: {
display: 'flex' as const,
flexDirection: 'row' as const,
position: 'absolute' as const,
...(isOverflow
? {top: 4, right: 4, gap: 4}
: !isMobile && images.length < 3
? {top: 8, right: 8, gap: 8}
: {top: 4, right: 4, gap: 4}),
zIndex: 1,
},
imageStyle: {
height: side,
width: side,
},
}
}, [images.length, containerInfo, isMobile])
? {top: 8, right: 8, gap: 8}
: {top: 4, right: 4, gap: 4}),
zIndex: 1,
},
imageStyle: {
height: side,
width: side,
},
}
}, [images.length, containerInfo, isMobile])
return images.length !== 0 ? (
<>
@@ -1,4 +1,4 @@
import React from 'react'
import {useEffect, useMemo, useRef} from 'react'
import {Pressable, useWindowDimensions, View} from 'react-native'
import Picker from '@emoji-mart/react'
import {msg} from '@lingui/core/macro'
@@ -52,9 +52,9 @@ export function EmojiPicker({state, close, pinToTop}: IProps) {
const {_} = useLingui()
const {height, width} = useWindowDimensions()
const isShiftDown = React.useRef(false)
const isShiftDown = useRef(false)
const position = React.useMemo(() => {
const position = useMemo(() => {
if (pinToTop) {
return {
top: state.pos.top - PICKER_HEIGHT + HEIGHT_OFFSET - 10,
@@ -86,7 +86,7 @@ export function EmojiPicker({state, close, pinToTop}: IProps) {
}
}, [state.pos, height, width, pinToTop])
React.useEffect(() => {
useEffect(() => {
if (!state.isOpen) return
const onKeyDown = (e: KeyboardEvent) => {
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback} from 'react'
import {init} from 'emoji-mart'
/**
@@ -11,7 +11,7 @@ let loadRequested = false
* {@link https://github.com/missive/emoji-mart/blob/16978d04a766eec6455e2e8bb21cd8dc0b3c7436/README.md?plain=1#L194}
*/
export function useWebPreloadEmoji({immediate}: {immediate?: boolean} = {}) {
const preload = React.useCallback(async () => {
const preload = useCallback(async () => {
if (loadRequested) return
loadRequested = true
try {
@@ -3,7 +3,6 @@ import {View} from 'react-native'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
import type React from 'react'
import {logger} from '#/logger'
import * as Toast from '#/view/com/util/Toast'
+5 -5
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useMemo} from 'react'
import {useNavigation} from '@react-navigation/native'
import {type NavigationProp} from '#/lib/routes/types'
@@ -19,7 +19,7 @@ export function HomeHeader(
const {hasSession} = useSession()
const navigation = useNavigation<NavigationProp>()
const hasPinnedCustom = React.useMemo<boolean>(() => {
const hasPinnedCustom = useMemo<boolean>(() => {
if (!hasSession) return false
return feeds.some(tab => {
const isFollowing = tab.uri === 'following'
@@ -27,7 +27,7 @@ export function HomeHeader(
})
}, [feeds, hasSession])
const items = React.useMemo(() => {
const items = useMemo(() => {
const pinnedNames = feeds.map(f => f.displayName)
if (!hasPinnedCustom) {
return pinnedNames.concat('Feeds ✨')
@@ -35,11 +35,11 @@ export function HomeHeader(
return pinnedNames
}, [hasPinnedCustom, feeds])
const onPressFeedsLink = React.useCallback(() => {
const onPressFeedsLink = useCallback(() => {
navigation.navigate('Feeds')
}, [navigation])
const onSelect = React.useCallback(
const onSelect = useCallback(
(index: number) => {
if (!hasPinnedCustom && index === items.length - 1) {
onPressFeedsLink()
@@ -2,7 +2,6 @@ import {type JSX} from 'react'
import {View} from 'react-native'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import type React from 'react'
import {HITSLOP_10} from '#/lib/constants'
import {useKawaiiMode} from '#/state/preferences/kawaii'
@@ -1,4 +1,4 @@
import React, {useState} from 'react'
import {memo, useState} from 'react'
import {ActivityIndicator, StyleSheet} from 'react-native'
import {
Gesture,
@@ -472,4 +472,4 @@ function withClampedSpring(value: any) {
return withSpring(value, {overshootClamping: true})
}
export default React.memo(ImageItem)
export default memo(ImageItem)
@@ -6,7 +6,7 @@
*
*/
import React, {useState} from 'react'
import {memo, useState} from 'react'
import {ActivityIndicator, StyleSheet} from 'react-native'
import {
Gesture,
@@ -363,4 +363,4 @@ const getZoomRectAfterDoubleTap = (
}
}
export default React.memo(ImageItem)
export default memo(ImageItem)
@@ -1,6 +1,6 @@
// default implementation fallback for web
import React from 'react'
import {memo} from 'react'
import {View} from 'react-native'
import {type PanGesture} from 'react-native-gesture-handler'
import {type SharedValue} from 'react-native-reanimated'
@@ -44,4 +44,4 @@ const ImageItem = (_props: Props) => {
return <View />
}
export default React.memo(ImageItem)
export default memo(ImageItem)
+7 -7
View File
@@ -7,7 +7,7 @@
*/
// Original code copied and simplified from the link below as the codebase is currently not maintained:
// https://github.com/jobtoday/react-native-image-viewing
import React, {useCallback, useEffect, useMemo, useState} from 'react'
import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
import {
LayoutAnimation,
PixelRatio,
@@ -104,7 +104,7 @@ export default function ImageViewRoot({
setActiveLightbox(nextLightbox)
}
React.useEffect(() => {
useEffect(() => {
if (!nextLightbox) {
return
}
@@ -150,7 +150,7 @@ export default function ImageViewRoot({
},
)
const onFlyAway = React.useCallback(() => {
const onFlyAway = useCallback(() => {
'worklet'
openProgress.set(0)
runOnJS(onRequestClose)()
@@ -216,7 +216,7 @@ function ImageView({
const [isDragging, setIsDragging] = useState(false)
const [imageIndex, setImageIndex] = useState(initialImageIndex)
const [showControls, setShowControls] = useState(true)
const [isAltExpanded, setAltExpanded] = React.useState(false)
const [isAltExpanded, setAltExpanded] = useState(false)
const dismissSwipeTranslateY = useSharedValue(0)
const isFlyingAway = useSharedValue(false)
@@ -418,7 +418,7 @@ function LightboxImage({
openProgress: SharedValue<number>
dismissSwipeTranslateY: SharedValue<number>
}) {
const [fetchedDims, setFetchedDims] = React.useState<Dimensions | null>(null)
const [fetchedDims, setFetchedDims] = useState<Dimensions | null>(null)
const dims = fetchedDims ?? imageSrc.dimensions ?? imageSrc.thumbDimensions
let imageAspect: number | undefined
if (dims) {
@@ -432,7 +432,7 @@ function LightboxImage({
width: widthDelayedForJSThreadOnly,
height: heightDelayedForJSThreadOnly,
} = useWindowDimensions()
const measureSafeArea = React.useCallback(() => {
const measureSafeArea = useCallback(() => {
'worklet'
let safeArea: Rect | null = measure(safeAreaRef)
if (!safeArea) {
@@ -563,7 +563,7 @@ function LightboxFooter({
onPressShare: (uri: string) => void
}) {
const {alt: altText, uri} = images[index]
const isMomentumScrolling = React.useRef(false)
const isMomentumScrolling = useRef(false)
return (
<ScrollView
style={styles.footerScrollView}
+8 -8
View File
@@ -1,4 +1,4 @@
import React, {type JSX, useCallback} from 'react'
import {type JSX, useCallback, useMemo, useState} from 'react'
import {
Dimensions,
type GestureResponderEvent,
@@ -57,7 +57,7 @@ export function ListMembers({
}) {
const t = useTheme()
const {_} = useLingui()
const [isRefreshing, setIsRefreshing] = React.useState(false)
const [isRefreshing, setIsRefreshing] = useState(false)
const {openModal} = useModalControls()
const {currentAccount} = useSession()
const moderationOpts = useModerationOpts()
@@ -77,7 +77,7 @@ export function ListMembers({
const isOwner =
currentAccount && data?.pages[0].list.creator.did === currentAccount.did
const items = React.useMemo(() => {
const items = useMemo(() => {
let items: any[] = []
if (isFetched) {
if (isEmpty && isError) {
@@ -102,7 +102,7 @@ export function ListMembers({
// events
// =
const onRefresh = React.useCallback(async () => {
const onRefresh = useCallback(async () => {
setIsRefreshing(true)
try {
await refetch()
@@ -112,7 +112,7 @@ export function ListMembers({
setIsRefreshing(false)
}, [refetch, setIsRefreshing])
const onEndReached = React.useCallback(async () => {
const onEndReached = useCallback(async () => {
if (isFetching || !hasNextPage || isError) return
try {
await fetchNextPage()
@@ -121,11 +121,11 @@ export function ListMembers({
}
}, [isFetching, hasNextPage, isError, fetchNextPage])
const onPressRetryLoadMore = React.useCallback(() => {
const onPressRetryLoadMore = useCallback(() => {
fetchNextPage()
}, [fetchNextPage])
const onPressEditMembership = React.useCallback(
const onPressEditMembership = useCallback(
(e: GestureResponderEvent, profile: bsky.profile.AnyProfileView) => {
e.preventDefault()
openModal({
@@ -141,7 +141,7 @@ export function ListMembers({
// rendering
// =
const renderItem = React.useCallback(
const renderItem = useCallback(
({item}: {item: any}) => {
if (item === EMPTY_ITEM) {
return renderEmptyState()
+5 -5
View File
@@ -1,4 +1,4 @@
import React, {type JSX} from 'react'
import {type JSX, useCallback, useMemo, useState} from 'react'
import {
ActivityIndicator,
FlatList as RNFlatList,
@@ -45,12 +45,12 @@ export function MyLists({
const t = useTheme()
const {_} = useLingui()
const moderationOpts = useModerationOpts()
const [isPTRing, setIsPTRing] = React.useState(false)
const [isPTRing, setIsPTRing] = useState(false)
const {data, isFetching, isFetched, isError, error, refetch} =
useMyListsQuery(filter)
const isEmpty = !isFetching && !data?.length
const items = React.useMemo(() => {
const items = useMemo(() => {
let items: any[] = []
if (isError && isEmpty) {
items = items.concat([ERROR_ITEM])
@@ -85,7 +85,7 @@ export function MyLists({
// events
// =
const onRefresh = React.useCallback(async () => {
const onRefresh = useCallback(async () => {
setIsPTRing(true)
try {
await refetch()
@@ -98,7 +98,7 @@ export function MyLists({
// rendering
// =
const renderItemInner = React.useCallback(
const renderItemInner = useCallback(
({item, index}: {item: any; index: number}) => {
if (item === EMPTY) {
return (
+4 -4
View File
@@ -1,4 +1,4 @@
import React, {useCallback} from 'react'
import {useCallback, useMemo, useState} from 'react'
import {
ActivityIndicator,
StyleSheet,
@@ -56,7 +56,7 @@ export function Component({
closeModal()
}, [closeModal])
const listStyle = React.useMemo(() => {
const listStyle = useMemo(() => {
if (IS_WEB_MOBILE) {
return [pal.border, {height: screenHeight / 2}]
} else if (IS_WEB) {
@@ -141,8 +141,8 @@ function ListItem({
const pal = usePalette('default')
const {_} = useLingui()
const {currentAccount} = useSession()
const [isProcessing, setIsProcessing] = React.useState(false)
const membership = React.useMemo(
const [isProcessing, setIsProcessing] = useState(false)
const membership = useMemo(
() => getMembership(memberships, list.uri, subject),
[memberships, list.uri, subject],
)
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useEffect, useMemo, useState} from 'react'
import {
ActivityIndicator,
type ListRenderItemInfo,
@@ -45,7 +45,7 @@ export function NotificationFeed({
refreshNotifications: () => Promise<void>
}) {
const initialNumToRender = useInitialNumToRender()
const [isPTRing, setIsPTRing] = React.useState(false)
const [isPTRing, setIsPTRing] = useState(false)
const {_} = useLingui()
const moderationOpts = useModerationOpts()
const trackPostView = usePostViewTracking('Notifications')
@@ -70,7 +70,7 @@ export function NotificationFeed({
const isEmpty =
!isFetching && !data?.pages.find(page => page.items.length > 0)
const items = React.useMemo(() => {
const items = useMemo(() => {
let arr: any[] = []
if (isFetched) {
if (isEmpty) {
@@ -89,7 +89,7 @@ export function NotificationFeed({
return arr
}, [isFetched, isError, isEmpty, data])
const onRefresh = React.useCallback(async () => {
const onRefresh = useCallback(async () => {
try {
setIsPTRing(true)
await refreshNotifications()
@@ -102,7 +102,7 @@ export function NotificationFeed({
}
}, [refreshNotifications, setIsPTRing])
const onEndReached = React.useCallback(async () => {
const onEndReached = useCallback(async () => {
if (isFetching || !hasNextPage || isError) return
try {
@@ -112,11 +112,11 @@ export function NotificationFeed({
}
}, [isFetching, hasNextPage, isError, fetchNextPage])
const onPressRetryLoadMore = React.useCallback(() => {
const onPressRetryLoadMore = useCallback(() => {
fetchNextPage()
}, [fetchNextPage])
const renderItem = React.useCallback(
const renderItem = useCallback(
({item, index}: ListRenderItemInfo<any>) => {
if (item === EMPTY_FEED_ITEM) {
return (
@@ -150,7 +150,7 @@ export function NotificationFeed({
[moderationOpts, _, onPressRetryLoadMore, filter],
)
const FeedFooter = React.useCallback(
const FeedFooter = useCallback(
() =>
isFetchingNextPage ? (
<View style={styles.feedFooter}>
@@ -162,7 +162,7 @@ export function NotificationFeed({
[isFetchingNextPage],
)
React.useEffect(() => {
useEffect(() => {
if (!enabled) {
setIsPTRing(false)
}
+3 -3
View File
@@ -1,9 +1,9 @@
import React, {useContext} from 'react'
import {createContext, useContext, useMemo} from 'react'
import {type SharedValue} from 'react-native-reanimated'
import {IS_NATIVE} from '#/env'
export const PagerHeaderContext = React.createContext<{
export const PagerHeaderContext = createContext<{
scrollY: SharedValue<number>
headerHeight: number
} | null>(null)
@@ -24,7 +24,7 @@ export function PagerHeaderProvider({
headerHeight: number
children: React.ReactNode
}) {
const value = React.useMemo(
const value = useMemo(
() => ({scrollY, headerHeight}),
[scrollY, headerHeight],
)
+3 -3
View File
@@ -1,4 +1,4 @@
import React, {useEffect} from 'react'
import {useCallback, useEffect, useRef} from 'react'
import {StyleSheet, View} from 'react-native'
import {
FontAwesomeIcon,
@@ -23,7 +23,7 @@ export function CustomFeedEmptyState() {
const ax = useAnalytics()
const feedFeedback = useFeedFeedbackContext()
const {currentAccount} = useSession()
const hasLoggedDiscoverEmptyErrorRef = React.useRef(false)
const hasLoggedDiscoverEmptyErrorRef = useRef(false)
useEffect(() => {
// Log the empty feed error event
@@ -44,7 +44,7 @@ export function CustomFeedEmptyState() {
const palInverted = usePalette('inverted')
const navigation = useNavigation<NavigationProp>()
const onPressFindAccounts = React.useCallback(() => {
const onPressFindAccounts = useCallback(() => {
if (IS_WEB) {
navigation.navigate('Search', {})
} else {
+3 -3
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback} from 'react'
import {View} from 'react-native'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
@@ -38,7 +38,7 @@ export function FeedShutdownMsg({feedUri}: {feedUri: string}) {
const hasFeedPinned = Boolean(feedConfig)
const hasDiscoverPinned = Boolean(discoverFeedConfig?.pinned)
const onRemoveFeed = React.useCallback(async () => {
const onRemoveFeed = useCallback(async () => {
try {
if (feedConfig) {
await removeFeed(feedConfig)
@@ -58,7 +58,7 @@ export function FeedShutdownMsg({feedUri}: {feedUri: string}) {
}
}, [removeFeed, feedConfig, _, hasDiscoverPinned, setSelectedFeed])
const onReplaceFeed = React.useCallback(async () => {
const onReplaceFeed = useCallback(async () => {
try {
await replaceFeedWithDiscover({
forYouFeedConfig: feedConfig,
+3 -3
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback} from 'react'
import {StyleSheet, View} from 'react-native'
import {
FontAwesomeIcon,
@@ -20,7 +20,7 @@ export function FollowingEmptyState() {
const palInverted = usePalette('inverted')
const navigation = useNavigation<NavigationProp>()
const onPressFindAccounts = React.useCallback(() => {
const onPressFindAccounts = useCallback(() => {
if (IS_WEB) {
navigation.navigate('Search', {})
} else {
@@ -29,7 +29,7 @@ export function FollowingEmptyState() {
}
}, [navigation])
const onPressDiscoverFeeds = React.useCallback(() => {
const onPressDiscoverFeeds = useCallback(() => {
navigation.navigate('Feeds')
}, [navigation])
+3 -3
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback} from 'react'
import {Dimensions, StyleSheet, View} from 'react-native'
import {
FontAwesomeIcon,
@@ -19,7 +19,7 @@ export function FollowingEndOfFeed() {
const palInverted = usePalette('inverted')
const navigation = useNavigation<NavigationProp>()
const onPressFindAccounts = React.useCallback(() => {
const onPressFindAccounts = useCallback(() => {
if (IS_WEB) {
navigation.navigate('Search', {})
} else {
@@ -28,7 +28,7 @@ export function FollowingEndOfFeed() {
}
}, [navigation])
const onPressDiscoverFeeds = React.useCallback(() => {
const onPressDiscoverFeeds = useCallback(() => {
navigation.navigate('Feeds')
}, [navigation])
+7 -7
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useMemo} from 'react'
import {View} from 'react-native'
import {
type AppBskyActorDefs,
@@ -48,7 +48,7 @@ export function PostFeedErrorMessage({
savedFeedConfig?: AppBskyActorDefs.SavedFeed
}) {
const {_: _l} = useLingui()
const knownError = React.useMemo(
const knownError = useMemo(
() => detectKnownError(feedDesc, error),
[feedDesc, error],
)
@@ -101,7 +101,7 @@ function FeedgenErrorMessage({
const pal = usePalette('default')
const {_: _l} = useLingui()
const navigation = useNavigation<NavigationProp>()
const msg = React.useMemo(
const msg = useMemo(
() =>
({
[KnownError.Unknown]: '',
@@ -135,15 +135,15 @@ function FeedgenErrorMessage({
const removePromptControl = Prompt.usePromptControl()
const {mutateAsync: removeFeed} = useRemoveFeedMutation()
const onViewProfile = React.useCallback(() => {
const onViewProfile = useCallback(() => {
navigation.navigate('Profile', {name: ownerDid})
}, [navigation, ownerDid])
const onPressRemoveFeed = React.useCallback(() => {
const onPressRemoveFeed = useCallback(() => {
removePromptControl.open()
}, [removePromptControl])
const onRemoveFeed = React.useCallback(async () => {
const onRemoveFeed = useCallback(async () => {
try {
if (!savedFeedConfig) return
await removeFeed(savedFeedConfig)
@@ -158,7 +158,7 @@ function FeedgenErrorMessage({
}
}, [removeFeed, _l, savedFeedConfig])
const cta = React.useMemo(() => {
const cta = useMemo(() => {
switch (knownError) {
case KnownError.FeedSignedInOnly: {
return null
+2 -2
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useMemo} from 'react'
import {StyleSheet, View} from 'react-native'
import Svg, {Circle, Line} from 'react-native-svg'
import {AtUri} from '@atproto/api'
@@ -19,7 +19,7 @@ export function ViewFullThread({uri}: {uri: string}) {
onOut: onHoverOut,
} = useInteractionState()
const pal = usePalette('default')
const itemHref = React.useMemo(() => {
const itemHref = useMemo(() => {
const urip = new AtUri(uri)
return makeProfileLink({did: urip.hostname, handle: ''}, 'post', urip.rkey)
}, [uri])
+12 -12
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
import {type AppBskyActorDefs as ActorDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
@@ -47,7 +47,7 @@ export function ProfileFollowers({name}: {name: string}) {
const initialNumToRender = useInitialNumToRender()
const {currentAccount} = useSession()
const [isPTRing, setIsPTRing] = React.useState(false)
const [isPTRing, setIsPTRing] = useState(false)
const {
data: resolvedDid,
isLoading: isDidLoading,
@@ -66,7 +66,7 @@ export function ProfileFollowers({name}: {name: string}) {
const isError = !!resolveError || !!error
const isMe = resolvedDid === currentAccount?.did
const followers = React.useMemo(() => {
const followers = useMemo(() => {
if (data?.pages) {
return data.pages.flatMap(page => page.followers)
}
@@ -74,11 +74,11 @@ export function ProfileFollowers({name}: {name: string}) {
}, [data])
// Track pagination events - fire for page 3+ (pages 1-2 may auto-load)
const paginationTrackingRef = React.useRef<{
const paginationTrackingRef = useRef<{
did: string | undefined
page: number
}>({did: undefined, page: 0})
React.useEffect(() => {
useEffect(() => {
const currentPageCount = data?.pages?.length || 0
// Reset tracking when profile changes
if (paginationTrackingRef.current.did !== resolvedDid) {
@@ -99,7 +99,7 @@ export function ProfileFollowers({name}: {name: string}) {
paginationTrackingRef.current.page = currentPageCount
}, [ax, data?.pages?.length, resolvedDid, followers.length])
const onRefresh = React.useCallback(async () => {
const onRefresh = useCallback(async () => {
setIsPTRing(true)
try {
await refetch()
@@ -109,7 +109,7 @@ export function ProfileFollowers({name}: {name: string}) {
setIsPTRing(false)
}, [refetch, setIsPTRing])
const onEndReached = React.useCallback(async () => {
const onEndReached = useCallback(async () => {
if (isFetchingNextPage || !hasNextPage || !!error) return
try {
await fetchNextPage()
@@ -118,14 +118,14 @@ export function ProfileFollowers({name}: {name: string}) {
}
}, [isFetchingNextPage, hasNextPage, error, fetchNextPage])
const renderItemWithContext = React.useCallback(
const renderItemWithContext = useCallback(
({item, index}: {item: ActorDefs.ProfileView; index: number}) =>
renderItem({item, index, contextProfileDid: resolvedDid}),
[resolvedDid],
)
// track pageview
React.useEffect(() => {
useEffect(() => {
if (resolvedDid) {
ax.metric('profile:followers:view', {
contextProfileDid: resolvedDid,
@@ -135,11 +135,11 @@ export function ProfileFollowers({name}: {name: string}) {
}, [ax, resolvedDid, isMe])
// track seen items
const seenItemsRef = React.useRef<Set<string>>(new Set())
React.useEffect(() => {
const seenItemsRef = useRef<Set<string>>(new Set())
useEffect(() => {
seenItemsRef.current.clear()
}, [resolvedDid])
const onItemSeen = React.useCallback(
const onItemSeen = useCallback(
(item: ActorDefs.ProfileView) => {
if (seenItemsRef.current.has(item.did)) {
return
+13 -13
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
import {type AppBskyActorDefs as ActorDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
@@ -50,7 +50,7 @@ export function ProfileFollows({name}: {name: string}) {
const {currentAccount} = useSession()
const navigation = useNavigation<NavigationProp>()
const onPressFindAccounts = React.useCallback(() => {
const onPressFindAccounts = useCallback(() => {
if (IS_WEB) {
navigation.navigate('Search', {})
} else {
@@ -59,7 +59,7 @@ export function ProfileFollows({name}: {name: string}) {
}
}, [navigation])
const [isPTRing, setIsPTRing] = React.useState(false)
const [isPTRing, setIsPTRing] = useState(false)
const {
data: resolvedDid,
isLoading: isDidLoading,
@@ -78,7 +78,7 @@ export function ProfileFollows({name}: {name: string}) {
const isError = !!resolveError || !!error
const isMe = resolvedDid === currentAccount?.did
const follows = React.useMemo(() => {
const follows = useMemo(() => {
if (data?.pages) {
return data.pages.flatMap(page => page.follows)
}
@@ -86,11 +86,11 @@ export function ProfileFollows({name}: {name: string}) {
}, [data])
// Track pagination events - fire for page 3+ (pages 1-2 may auto-load)
const paginationTrackingRef = React.useRef<{
const paginationTrackingRef = useRef<{
did: string | undefined
page: number
}>({did: undefined, page: 0})
React.useEffect(() => {
useEffect(() => {
const currentPageCount = data?.pages?.length || 0
// Reset tracking when profile changes
if (paginationTrackingRef.current.did !== resolvedDid) {
@@ -111,7 +111,7 @@ export function ProfileFollows({name}: {name: string}) {
paginationTrackingRef.current.page = currentPageCount
}, [ax, data?.pages?.length, resolvedDid, follows.length])
const onRefresh = React.useCallback(async () => {
const onRefresh = useCallback(async () => {
setIsPTRing(true)
try {
await refetch()
@@ -121,7 +121,7 @@ export function ProfileFollows({name}: {name: string}) {
setIsPTRing(false)
}, [refetch, setIsPTRing])
const onEndReached = React.useCallback(async () => {
const onEndReached = useCallback(async () => {
if (isFetchingNextPage || !hasNextPage || !!error) return
try {
await fetchNextPage()
@@ -130,14 +130,14 @@ export function ProfileFollows({name}: {name: string}) {
}
}, [isFetchingNextPage, hasNextPage, error, fetchNextPage])
const renderItemWithContext = React.useCallback(
const renderItemWithContext = useCallback(
({item, index}: {item: ActorDefs.ProfileView; index: number}) =>
renderItem({item, index, contextProfileDid: resolvedDid}),
[resolvedDid],
)
// track pageview
React.useEffect(() => {
useEffect(() => {
if (resolvedDid) {
ax.metric('profile:following:view', {
contextProfileDid: resolvedDid,
@@ -147,11 +147,11 @@ export function ProfileFollows({name}: {name: string}) {
}, [ax, resolvedDid, isMe])
// track seen items
const seenItemsRef = React.useRef<Set<string>>(new Set())
React.useEffect(() => {
const seenItemsRef = useRef<Set<string>>(new Set())
useEffect(() => {
seenItemsRef.current.clear()
}, [resolvedDid])
const onItemSeen = React.useCallback(
const onItemSeen = useCallback(
(item: ActorDefs.ProfileView) => {
if (seenItemsRef.current.has(item.did)) {
return
+14 -14
View File
@@ -1,4 +1,4 @@
import React, {memo} from 'react'
import {memo, useCallback, useMemo} from 'react'
import {type AppBskyActorDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
@@ -108,29 +108,29 @@ let ProfileMenu = ({
const goLiveDisabledDialogControl = useDialogControl()
const addToStarterPacksDialogControl = useDialogControl()
const showLoggedOutWarning = React.useMemo(() => {
const showLoggedOutWarning = useMemo(() => {
return (
profile.did !== currentAccount?.did &&
!!profile.labels?.find(label => label.val === '!no-unauthenticated')
)
}, [currentAccount, profile])
const invalidateProfileQuery = React.useCallback(() => {
const invalidateProfileQuery = useCallback(() => {
queryClient.invalidateQueries({
queryKey: profileQueryKey(profile.did),
})
}, [queryClient, profile.did])
const onPressAddToStarterPacks = React.useCallback(() => {
const onPressAddToStarterPacks = useCallback(() => {
ax.metric('profile:addToStarterPack', {})
addToStarterPacksDialogControl.open()
}, [addToStarterPacksDialogControl])
const onPressShare = React.useCallback(() => {
const onPressShare = useCallback(() => {
shareUrl(toShareUrl(makeProfileLink(profile)))
}, [profile])
const onPressAddRemoveLists = React.useCallback(() => {
const onPressAddRemoveLists = useCallback(() => {
openModal({
name: 'user-add-remove-lists',
subject: profile.did,
@@ -141,7 +141,7 @@ let ProfileMenu = ({
})
}, [profile, openModal, invalidateProfileQuery])
const onPressMuteAccount = React.useCallback(async () => {
const onPressMuteAccount = useCallback(async () => {
if (profile.viewer?.muted) {
try {
await queueUnmute()
@@ -165,7 +165,7 @@ let ProfileMenu = ({
}
}, [ax, profile.viewer?.muted, queueUnmute, _, queueMute])
const blockAccount = React.useCallback(async () => {
const blockAccount = useCallback(async () => {
if (profile.viewer?.blocking) {
try {
await queueUnblock()
@@ -189,7 +189,7 @@ let ProfileMenu = ({
}
}, [ax, profile.viewer?.blocking, _, queueUnblock, queueBlock])
const onPressFollowAccount = React.useCallback(async () => {
const onPressFollowAccount = useCallback(async () => {
try {
await queueFollow()
Toast.show(_(msg({message: 'Account followed', context: 'toast'})))
@@ -201,7 +201,7 @@ let ProfileMenu = ({
}
}, [_, ax, queueFollow])
const onPressUnfollowAccount = React.useCallback(async () => {
const onPressUnfollowAccount = useCallback(async () => {
try {
await queueUnfollow()
Toast.show(_(msg({message: 'Account unfollowed', context: 'toast'})))
@@ -213,19 +213,19 @@ let ProfileMenu = ({
}
}, [_, ax, queueUnfollow])
const onPressReportAccount = React.useCallback(() => {
const onPressReportAccount = useCallback(() => {
reportDialogControl.open()
}, [reportDialogControl])
const onPressShareATUri = React.useCallback(() => {
const onPressShareATUri = useCallback(() => {
shareText(`at://${profile.did}`)
}, [profile.did])
const onPressShareDID = React.useCallback(() => {
const onPressShareDID = useCallback(() => {
shareText(profile.did)
}, [profile.did])
const onPressSearch = React.useCallback(() => {
const onPressSearch = useCallback(() => {
navigation.navigate('ProfileSearch', {name: profile.handle})
}, [navigation, profile.handle])
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback} from 'react'
import {Pressable, View} from 'react-native'
import Animated, {
measure,
@@ -60,7 +60,7 @@ export function ProfileSubpageHeader({
const canGoBack = navigation.canGoBack()
const aviRef = useAnimatedRef()
const _openLightbox = React.useCallback(
const _openLightbox = useCallback(
(uri: string, thumbRect: MeasuredDimensions | null) => {
openLightbox({
images: [
@@ -83,7 +83,7 @@ export function ProfileSubpageHeader({
[openLightbox],
)
const onPressAvi = React.useCallback(() => {
const onPressAvi = useCallback(() => {
if (
avatar // TODO && !(view.moderation.avatar.blur && view.moderation.avatar.noOverride)
) {
@@ -8,7 +8,6 @@ import Animated, {
import {type BottomSheetBackdropProps} from '@discord/bottom-sheet/src'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import type React from 'react'
export function createCustomBackdrop(
onClose?: () => void,
+3 -3
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {isValidElement} from 'react'
import {type StyleProp, type TextStyle, type ViewStyle} from 'react-native'
import {View} from 'react-native'
@@ -45,7 +45,7 @@ export function EmptyState({
return placeholderIcon
}
if (React.isValidElement(icon)) {
if (isValidElement(icon)) {
return icon
}
@@ -77,7 +77,7 @@ export function EmptyState({
a.rounded_full,
a.mt_5xl,
{height: 64, width: 64},
React.isValidElement(icon)
isValidElement(icon)
? a.bg_transparent
: [isTabletOrDesktop && {marginTop: 50}],
]}>
+2 -2
View File
@@ -1,4 +1,4 @@
import React, {useCallback, useEffect} from 'react'
import {useCallback, useEffect} from 'react'
import {type NativeScrollEvent} from 'react-native'
import {
clamp,
@@ -25,7 +25,7 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) {
const startMode = useSharedValue<number | null>(null)
const didJustRestoreScroll = useSharedValue<boolean>(false)
const setMode = React.useCallback(
const setMode = useCallback(
(v: boolean) => {
'worklet'
headerMode.set(() =>
+17 -11
View File
@@ -1,4 +1,13 @@
import React, {type JSX, useEffect, useState} from 'react'
import {
forwardRef,
type JSX,
useCallback,
useEffect,
useImperativeHandle,
useMemo,
useRef,
useState,
} from 'react'
import {
type NativeScrollEvent,
type NativeSyntheticEvent,
@@ -25,7 +34,7 @@ export type ViewSelectorHandle = {
scrollToTop: () => void
}
export const ViewSelector = React.forwardRef<
export const ViewSelector = forwardRef<
ViewSelectorHandle,
{
sections: string[]
@@ -61,14 +70,14 @@ export const ViewSelector = React.forwardRef<
) {
const pal = usePalette('default')
const [selectedIndex, setSelectedIndex] = useState<number>(0)
const flatListRef = React.useRef<FlatList_INTERNAL>(null)
const flatListRef = useRef<FlatList_INTERNAL>(null)
// events
// =
const keyExtractor = React.useCallback((item: any) => item._reactKey, [])
const keyExtractor = useCallback((item: any) => item._reactKey, [])
const onPressSelection = React.useCallback(
const onPressSelection = useCallback(
(index: number) => setSelectedIndex(clamp(index, 0, sections.length)),
[setSelectedIndex, sections],
)
@@ -76,7 +85,7 @@ export const ViewSelector = React.forwardRef<
onSelectView?.(selectedIndex)
}, [selectedIndex, onSelectView])
React.useImperativeHandle(ref, () => ({
useImperativeHandle(ref, () => ({
scrollToTop: () => {
flatListRef.current?.scrollToOffset({offset: 0})
},
@@ -85,7 +94,7 @@ export const ViewSelector = React.forwardRef<
// rendering
// =
const renderItemInternal = React.useCallback(
const renderItemInternal = useCallback(
({item}: {item: any}) => {
if (item === HEADER_ITEM) {
if (renderHeader) {
@@ -107,10 +116,7 @@ export const ViewSelector = React.forwardRef<
[sections, selectedIndex, onPressSelection, renderHeader, renderItem],
)
const data = React.useMemo(
() => [HEADER_ITEM, SELECTOR_ITEM, ...items],
[items],
)
const data = useMemo(() => [HEADER_ITEM, SELECTOR_ITEM, ...items], [items])
return (
<FlatList_INTERNAL
// @ts-expect-error FlatList_INTERNAL ref type is wrong -sfn
+4 -4
View File
@@ -12,7 +12,7 @@
* need to match layout but which aren't scrolled.
*/
import React from 'react'
import {forwardRef} from 'react'
import {
type FlatList,
type FlatListProps,
@@ -37,7 +37,7 @@ interface AddedProps {
/**
* @deprecated use `Layout` components
*/
export const CenteredView = React.forwardRef(function CenteredView(
export const CenteredView = forwardRef(function CenteredView(
{
style,
topBorder,
@@ -66,7 +66,7 @@ export const CenteredView = React.forwardRef(function CenteredView(
return <View ref={ref} style={style} {...props} />
})
export const FlatList_INTERNAL = React.forwardRef(function FlatListImpl<ItemT>(
export const FlatList_INTERNAL = forwardRef(function FlatListImpl<ItemT>(
{
contentContainerStyle,
style,
@@ -141,7 +141,7 @@ export const FlatList_INTERNAL = React.forwardRef(function FlatListImpl<ItemT>(
/**
* @deprecated use `Layout` components
*/
export const ScrollView = React.forwardRef(function ScrollViewImpl(
export const ScrollView = forwardRef(function ScrollViewImpl(
{contentContainerStyle, ...props}: React.PropsWithChildren<ScrollViewProps>,
ref: React.Ref<Animated.ScrollView>,
) {
-1
View File
@@ -1,5 +1,4 @@
import {Platform} from 'react-native'
import type React from 'react'
const onMouseUp = (e: React.MouseEvent & {target: HTMLElement}) => {
// Only handle whenever it is the middle button
+5 -5
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useState} from 'react'
import {
ActivityIndicator,
type GestureResponderEvent,
@@ -148,8 +148,8 @@ export function Button({
},
)
const [isLoading, setIsLoading] = React.useState(false)
const onPressWrapped = React.useCallback(
const [isLoading, setIsLoading] = useState(false)
const onPressWrapped = useCallback(
async (event: GestureResponderEvent) => {
event.stopPropagation()
event.preventDefault()
@@ -160,7 +160,7 @@ export function Button({
[onPress, withLoading],
)
const getStyle = React.useCallback(
const getStyle = useCallback(
(state: PressableStateCallbackType) => {
const arr = [typeOuterStyle, styles.outer, style]
if (state.pressed) {
@@ -173,7 +173,7 @@ export function Button({
[typeOuterStyle, style],
)
const renderChildern = React.useCallback(() => {
const renderChildern = useCallback(() => {
if (!label) {
return children
}
@@ -1,5 +1,4 @@
import {ScrollView, StyleSheet, View} from 'react-native'
import type React from 'react'
import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle'
import {useIsKeyboardVisible} from '#/lib/hooks/useIsKeyboardVisible'
+2 -2
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useMemo} from 'react'
import {StyleSheet, type TextProps} from 'react-native'
import {UITextView} from 'react-native-uitextview'
@@ -57,7 +57,7 @@ function Text_DEPRECATED({
}
}
const textProps = React.useMemo(() => {
const textProps = useMemo(() => {
const typography = theme.typography[type]
const lineHeightStyle = lineHeight ? lh(theme, type, lineHeight) : undefined