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
+7 -7
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useState} from 'react'
import {View} from 'react-native'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {msg} from '@lingui/core/macro'
@@ -38,11 +38,11 @@ export function Deactivated() {
const hasOtherAccounts = accounts.length > 1
const {logoutCurrentAccount} = useSessionApi()
const agent = useAgent()
const [pending, setPending] = React.useState(false)
const [error, setError] = React.useState<string | undefined>()
const [pending, setPending] = useState(false)
const [error, setError] = useState<string | undefined>()
const queryClient = useQueryClient()
const onSelectAccount = React.useCallback(
const onSelectAccount = useCallback(
(account: SessionAccount) => {
if (account.did !== currentAccount?.did) {
onPressSwitchAccount(account, 'SwitchAccount')
@@ -51,11 +51,11 @@ export function Deactivated() {
[currentAccount, onPressSwitchAccount],
)
const onPressAddAccount = React.useCallback(() => {
const onPressAddAccount = useCallback(() => {
setShowLoggedOut(true)
}, [setShowLoggedOut])
const onPressLogout = React.useCallback(() => {
const onPressLogout = useCallback(() => {
if (IS_WEB) {
// We're switching accounts, which remounts the entire app.
// On mobile, this gets us Home, but on the web we also need reset the URL.
@@ -67,7 +67,7 @@ export function Deactivated() {
logoutCurrentAccount('Deactivated')
}, [logoutCurrentAccount])
const handleActivate = React.useCallback(async () => {
const handleActivate = useCallback(async () => {
try {
setPending(true)
await agent.com.atproto.server.activateAccount()
@@ -1,4 +1,4 @@
import React from 'react'
import {useState} from 'react'
import {View} from 'react-native'
import {ScrollView} from '#/view/com/util/Views'
@@ -9,7 +9,7 @@ import {Text} from '#/components/Typography'
import {SharedPrefs} from '../../../modules/expo-bluesky-swiss-army'
export function SharedPreferencesTesterScreen() {
const [currentTestOutput, setCurrentTestOutput] = React.useState<string>('')
const [currentTestOutput, setCurrentTestOutput] = useState<string>('')
return (
<Layout.Screen>
+15 -15
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useMemo, useState} from 'react'
import {type ListRenderItemInfo, View} from 'react-native'
import {type AppBskyFeedDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro'
@@ -47,29 +47,29 @@ export default function HashtagScreen({
const {tag, author} = route.params
const {_} = useLingui()
const decodedTag = React.useMemo(() => {
const decodedTag = useMemo(() => {
return decodeURIComponent(tag)
}, [tag])
const isCashtag = decodedTag.startsWith('$')
const fullTag = React.useMemo(() => {
const fullTag = useMemo(() => {
// Cashtags already include the $ prefix, hashtags need # added
return isCashtag ? decodedTag : `#${decodedTag}`
}, [decodedTag, isCashtag])
const headerTitle = React.useMemo(() => {
const headerTitle = useMemo(() => {
// Keep cashtags uppercase, lowercase hashtags
const displayTag = isCashtag ? fullTag.toUpperCase() : fullTag.toLowerCase()
return enforceLen(displayTag, 24, true, 'middle')
}, [fullTag, isCashtag])
const sanitizedAuthor = React.useMemo(() => {
const sanitizedAuthor = useMemo(() => {
if (!author) return ''
return sanitizeHandle(author)
}, [author])
const onShare = React.useCallback(() => {
const onShare = useCallback(() => {
const url = new URL('https://bsky.app')
url.pathname = `/hashtag/${decodeURIComponent(tag)}`
if (author) {
@@ -78,16 +78,16 @@ export default function HashtagScreen({
shareUrl(url.toString())
}, [tag, author])
const [activeTab, setActiveTab] = React.useState(0)
const [activeTab, setActiveTab] = useState(0)
const setMinimalShellMode = useSetMinimalShellMode()
useFocusEffect(
React.useCallback(() => {
useCallback(() => {
setMinimalShellMode(false)
}, [setMinimalShellMode]),
)
const onPageSelected = React.useCallback(
const onPageSelected = useCallback(
(index: number) => {
setMinimalShellMode(false)
setActiveTab(index)
@@ -95,7 +95,7 @@ export default function HashtagScreen({
[setMinimalShellMode],
)
const sections = React.useMemo(() => {
const sections = useMemo(() => {
return [
{
title: _(msg`Top`),
@@ -177,14 +177,14 @@ function HashtagScreenTab({
}) {
const {_} = useLingui()
const initialNumToRender = useInitialNumToRender()
const [isPTR, setIsPTR] = React.useState(false)
const [isPTR, setIsPTR] = useState(false)
const t = useTheme()
const {hasSession} = useSession()
const trackPostView = usePostViewTracking('Hashtag')
const isCashtag = fullTag.startsWith('$')
const queryParam = React.useMemo(() => {
const queryParam = useMemo(() => {
// Cashtags need # prefix for search: "#$BTC" or "#$BTC from:author"
const searchTag = isCashtag ? `#${fullTag}` : fullTag
if (!author) return searchTag
@@ -203,17 +203,17 @@ function HashtagScreenTab({
hasNextPage,
} = useSearchPostsQuery({query: queryParam, sort, enabled: active})
const posts = React.useMemo(() => {
const posts = useMemo(() => {
return data?.pages.flatMap(page => page.posts) || []
}, [data])
const onRefresh = React.useCallback(async () => {
const onRefresh = useCallback(async () => {
setIsPTR(true)
await refetch()
setIsPTR(false)
}, [refetch])
const onEndReached = React.useCallback(() => {
const onEndReached = useCallback(() => {
if (isFetchingNextPage || !hasNextPage || error) return
fetchNextPage()
}, [isFetchingNextPage, hasNextPage, error, fetchNextPage])
+2 -2
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback} from 'react'
import {View} from 'react-native'
import {TID} from '@atproto/common-web'
import {msg} from '@lingui/core/macro'
@@ -27,7 +27,7 @@ export function NoFeedsPinned({
const {isPending, mutateAsync: overwriteSavedFeeds} =
useOverwriteSavedFeedsMutation()
const addRecommendedFeeds = React.useCallback(async () => {
const addRecommendedFeeds = useCallback(async () => {
let skippedTimeline = false
let skippedDiscover = false
let remainingSavedFeeds = []
+2 -2
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useState} from 'react'
import {View} from 'react-native'
import {AppBskyGraphDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro'
@@ -45,7 +45,7 @@ export function ListHiddenScreen({
const isModList = list.purpose === AppBskyGraphDefs.MODLIST
const [isProcessing, setIsProcessing] = React.useState(false)
const [isProcessing, setIsProcessing] = useState(false)
const listBlockMutation = useListBlockMutation()
const listMuteMutation = useListMuteMutation()
const {mutateAsync: removeSavedFeed} = useRemoveFeedMutation()
+2 -2
View File
@@ -1,4 +1,4 @@
import React, {useState} from 'react'
import {useCallback, useState} from 'react'
import {Keyboard, View} from 'react-native'
import {type ComAtprotoServerDescribeServer} from '@atproto/api'
import {msg} from '@lingui/core/macro'
@@ -44,7 +44,7 @@ export const ForgotPasswordForm = ({
const [email, setEmail] = useState<string>('')
const {_} = useLingui()
const onPressSelectService = React.useCallback(() => {
const onPressSelectService = useCallback(() => {
Keyboard.dismiss()
}, [])
-1
View File
@@ -1,5 +1,4 @@
import {type StyleProp, View, type ViewStyle} from 'react-native'
import type React from 'react'
import {atoms as a, useBreakpoints, useGutters} from '#/alf'
import {Text} from '#/components/Typography'
+4 -4
View File
@@ -1,4 +1,4 @@
import React, {useCallback, useEffect} from 'react'
import {useCallback, useEffect, useMemo, useState} from 'react'
import {View} from 'react-native'
import {
type AppBskyActorDefs,
@@ -105,7 +105,7 @@ function Inner() {
})
const recipient = useMaybeProfileShadow(recipientUnshadowed)
const moderation = React.useMemo(() => {
const moderation = useMemo(() => {
if (!recipient || !moderationOpts) return null
return moderateProfile(recipient, moderationOpts)
}, [recipient, moderationOpts])
@@ -113,7 +113,7 @@ function Inner() {
// Because we want to give the list a chance to asynchronously scroll to the end before it is visible to the user,
// we use `hasScrolled` to determine when to render. With that said however, there is a chance that the chat will be
// empty. So, we also check for that possible state as well and render once we can.
const [hasScrolled, setHasScrolled] = React.useState(false)
const [hasScrolled, setHasScrolled] = useState(false)
const readyToShow =
hasScrolled ||
(isConvoActive(convoState) &&
@@ -122,7 +122,7 @@ function Inner() {
// Any time that we re-render the `Initializing` state, we have to reset `hasScrolled` to false. After entering this
// state, we know that we're resetting the list of messages and need to re-scroll to the bottom when they get added.
React.useEffect(() => {
useEffect(() => {
if (convoState.status === ConvoStatus.Initializing) {
setHasScrolled(false)
}
@@ -1,4 +1,4 @@
import React, {useCallback, useMemo, useState} from 'react'
import {memo, useCallback, useMemo, useState} from 'react'
import {type GestureResponderEvent, View} from 'react-native'
import {
AppBskyEmbedRecord,
@@ -80,7 +80,7 @@ export let ChatListItem = ({
)
}
ChatListItem = React.memo(ChatListItem)
ChatListItem = memo(ChatListItem)
function ChatListItemReady({
convo,
@@ -104,7 +104,7 @@ function ChatListItemReady({
const {gtMobile} = useBreakpoints()
const profile = useProfileShadow(profileUnshadowed)
const {mutate: markAsRead} = useMarkAsReadMutation()
const moderation = React.useMemo(
const moderation = useMemo(
() => moderateProfile(profile, moderationOpts),
[profile, moderationOpts],
)
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useEffect, useRef, useState} from 'react'
import {Pressable, View} from 'react-native'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
@@ -43,16 +43,16 @@ export function MessageInput({
const {_} = useLingui()
const t = useTheme()
const {getDraft, clearDraft} = useMessageDraft()
const [message, setMessage] = React.useState(getDraft)
const [message, setMessage] = useState(getDraft)
const inputStyles = useSharedInputStyles()
const isComposing = React.useRef(false)
const [isFocused, setIsFocused] = React.useState(false)
const [isHovered, setIsHovered] = React.useState(false)
const [textAreaHeight, setTextAreaHeight] = React.useState(38)
const textAreaRef = React.useRef<HTMLTextAreaElement>(null)
const isComposing = useRef(false)
const [isFocused, setIsFocused] = useState(false)
const [isHovered, setIsHovered] = useState(false)
const [textAreaHeight, setTextAreaHeight] = useState(38)
const textAreaRef = useRef<HTMLTextAreaElement>(null)
const onSubmit = React.useCallback(() => {
const onSubmit = useCallback(() => {
if (!hasEmbed && message.trim() === '') {
return
}
@@ -66,7 +66,7 @@ export function MessageInput({
setEmbed(undefined)
}, [message, onSendMessage, _, clearDraft, hasEmbed, setEmbed])
const onKeyDown = React.useCallback(
const onKeyDown = useCallback(
(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
// Don't submit the form when the Japanese or any other IME is composing
if (isComposing.current) return
@@ -98,14 +98,11 @@ export function MessageInput({
[onSubmit],
)
const onChange = React.useCallback(
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
setMessage(e.target.value)
},
[],
)
const onChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {
setMessage(e.target.value)
}, [])
const onEmojiInserted = React.useCallback(
const onEmojiInserted = useCallback(
(emoji: Emoji) => {
if (!textAreaRef.current) {
return
@@ -123,7 +120,7 @@ export function MessageInput({
},
[setMessage],
)
React.useEffect(() => {
useEffect(() => {
textInputWebEmitter.addListener('emoji-inserted', onEmojiInserted)
return () => {
textInputWebEmitter.removeListener('emoji-inserted', onEmojiInserted)
@@ -1,4 +1,4 @@
import React from 'react'
import {useMemo} from 'react'
import {View} from 'react-native'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
@@ -12,7 +12,7 @@ import {Text} from '#/components/Typography'
export function MessageListError({item}: {item: ConvoItem & {type: 'error'}}) {
const t = useTheme()
const {_} = useLingui()
const {description, help, cta} = React.useMemo(() => {
const {description, help, cta} = useMemo(() => {
return {
[ConvoItemError.FirehoseFailed]: {
description: _(msg`This chat was disconnected`),
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useMemo, useState} from 'react'
import {View} from 'react-native'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
@@ -63,9 +63,9 @@ function Inner({preferences}: {preferences: UsePreferencesQueryResponse}) {
const {_} = useLingui()
const {mutateAsync: setPostInteractionSettings, isPending} =
usePostInteractionSettingsMutation()
const [error, setError] = React.useState<string | undefined>(undefined)
const [error, setError] = useState<string | undefined>(undefined)
const allowUI = React.useMemo(() => {
const allowUI = useMemo(() => {
return threadgateRecordToAllowUISetting({
$type: 'app.bsky.feed.threadgate',
post: '',
@@ -73,7 +73,7 @@ function Inner({preferences}: {preferences: UsePreferencesQueryResponse}) {
allow: preferences.postInteractionSettings.threadgateAllowRules,
})
}, [preferences.postInteractionSettings.threadgateAllowRules])
const postgate = React.useMemo(() => {
const postgate = useMemo(() => {
return createPostgateRecord({
post: '',
embeddingRules:
@@ -81,17 +81,17 @@ function Inner({preferences}: {preferences: UsePreferencesQueryResponse}) {
})
}, [preferences.postInteractionSettings.postgateEmbeddingRules])
const [maybeEditedAllowUI, setAllowUI] = React.useState(allowUI)
const [maybeEditedPostgate, setEditedPostgate] = React.useState(postgate)
const [maybeEditedAllowUI, setAllowUI] = useState(allowUI)
const [maybeEditedPostgate, setEditedPostgate] = useState(postgate)
const wasEdited = React.useMemo(() => {
const wasEdited = useMemo(() => {
return (
!deepEqual(allowUI, maybeEditedAllowUI) ||
!deepEqual(postgate.embeddingRules, maybeEditedPostgate.embeddingRules)
)
}, [postgate, allowUI, maybeEditedAllowUI, maybeEditedPostgate])
const onSave = React.useCallback(async () => {
const onSave = useCallback(async () => {
setError('')
try {
@@ -1,4 +1,4 @@
import React from 'react'
import {useMemo} from 'react'
import {type TextStyle, View, type ViewStyle} from 'react-native'
import {type Interest, useInterestsDisplayNames} from '#/lib/interests'
@@ -12,7 +12,7 @@ export function InterestButton({interest}: {interest: Interest}) {
const interestsDisplayNames = useInterestsDisplayNames()
const ctx = Toggle.useItemContext()
const styles = React.useMemo(() => {
const styles = useMemo(() => {
const hovered: ViewStyle[] = [
{
backgroundColor:
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useState} from 'react'
import {View} from 'react-native'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
@@ -27,12 +27,12 @@ export function StepInterests() {
const interestsDisplayNames = useInterestsDisplayNames()
const {state, dispatch} = useOnboardingInternalState()
const [saving, setSaving] = React.useState(false)
const [selectedInterests, setSelectedInterests] = React.useState<string[]>(
const [saving, setSaving] = useState(false)
const [selectedInterests, setSelectedInterests] = useState<string[]>(
state.interestsStepResults.selectedInterests.map(i => i),
)
const saveInterests = React.useCallback(async () => {
const saveInterests = useCallback(async () => {
setSaving(true)
try {
@@ -1,4 +1,4 @@
import React from 'react'
import {useMemo} from 'react'
import {View} from 'react-native'
import {Image as ExpoImage} from 'expo-image'
import {msg} from '@lingui/core/macro'
@@ -22,7 +22,7 @@ export function AvatarCircle({
const t = useTheme()
const {avatar} = useAvatar()
const styles = React.useMemo(
const styles = useMemo(
() => ({
imageContainer: [
a.rounded_full,
@@ -1,4 +1,4 @@
import React from 'react'
import {useMemo} from 'react'
import {View} from 'react-native'
import {type Avatar} from '#/screens/Onboarding/StepProfile/index'
@@ -14,7 +14,7 @@ export function AvatarCreatorCircle({
const t = useTheme()
const Icon = avatar.placeholder.component
const styles = React.useMemo(
const styles = useMemo(
() => ({
imageContainer: [
a.rounded_full,
@@ -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'
@@ -39,7 +39,7 @@ export function AvatarCreatorItems({
const t = useTheme()
const isEmojis = type === 'emojis'
const onSelectEmoji = React.useCallback(
const onSelectEmoji = useCallback(
(emoji: EmojiName) => {
setAvatar(prev => ({
...prev,
@@ -49,7 +49,7 @@ export function AvatarCreatorItems({
[setAvatar],
)
const onSelectColor = React.useCallback(
const onSelectColor = useCallback(
(color: AvatarColor) => {
setAvatar(prev => ({
...prev,
@@ -1,11 +1,18 @@
import React from 'react'
import {
forwardRef,
lazy,
Suspense,
useImperativeHandle,
useMemo,
useRef,
} from 'react'
import {View} from 'react-native'
import type ViewShot from 'react-native-view-shot'
import {useAvatar} from '#/screens/Onboarding/StepProfile/index'
import {atoms as a} from '#/alf'
const LazyViewShot = React.lazy(
const LazyViewShot = lazy(
// @ts-expect-error dynamic import
() => import('react-native-view-shot/src/index'),
)
@@ -18,13 +25,13 @@ export interface PlaceholderCanvasRef {
// This component is supposed to be invisible to the user. We only need this for ViewShot to have something to
// "screenshot".
export const PlaceholderCanvas = React.forwardRef<PlaceholderCanvasRef, {}>(
export const PlaceholderCanvas = forwardRef<PlaceholderCanvasRef, {}>(
function PlaceholderCanvas({}, ref) {
const {avatar} = useAvatar()
const viewshotRef = React.useRef<ViewShot>(null)
const viewshotRef = useRef<ViewShot>(null)
const Icon = avatar.placeholder.component
const styles = React.useMemo(
const styles = useMemo(
() => ({
container: [a.absolute, {top: -2000}],
imageContainer: [
@@ -36,7 +43,7 @@ export const PlaceholderCanvas = React.forwardRef<PlaceholderCanvasRef, {}>(
[],
)
React.useImperativeHandle(ref, () => ({
useImperativeHandle(ref, () => ({
capture: async () => {
if (viewshotRef.current?.capture) {
return await viewshotRef.current.capture()
@@ -46,7 +53,7 @@ export const PlaceholderCanvas = React.forwardRef<PlaceholderCanvasRef, {}>(
return (
<View style={styles.container}>
<React.Suspense fallback={null}>
<Suspense fallback={null}>
<LazyViewShot
// @ts-ignore this library doesn't have types
ref={viewshotRef}
@@ -70,7 +77,7 @@ export const PlaceholderCanvas = React.forwardRef<PlaceholderCanvasRef, {}>(
/>
</View>
</LazyViewShot>
</React.Suspense>
</Suspense>
</View>
)
},
+21 -13
View File
@@ -1,4 +1,12 @@
import React from 'react'
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useRef,
useState,
} from 'react'
import {View} from 'react-native'
import {Image as ExpoImage} from 'expo-image'
import {ImageManipulator, SaveFormat} from 'expo-image-manipulator'
@@ -60,9 +68,9 @@ interface IAvatarContext {
setAvatar: React.Dispatch<React.SetStateAction<Avatar>>
}
const AvatarContext = React.createContext<IAvatarContext>({} as IAvatarContext)
const AvatarContext = createContext<IAvatarContext>({} as IAvatarContext)
AvatarContext.displayName = 'AvatarContext'
export const useAvatar = () => React.useContext(AvatarContext)
export const useAvatar = () => useContext(AvatarContext)
const randomColor =
avatarColors[Math.floor(Math.random() * avatarColors.length)]
@@ -76,10 +84,10 @@ export function StepProfile() {
const requestNotificationsPermission = useRequestNotificationsPermission()
const creatorControl = Dialog.useDialogControl()
const [error, setError] = React.useState('')
const [error, setError] = useState('')
const {state, dispatch} = useOnboardingInternalState()
const [avatar, setAvatar] = React.useState<Avatar>({
const [avatar, setAvatar] = useState<Avatar>({
image: state.profileStepResults?.image,
placeholder: state.profileStepResults.creatorState?.emoji || emojiItems.at,
backgroundColor:
@@ -87,14 +95,14 @@ export function StepProfile() {
useCreatedAvatar: state.profileStepResults.isCreatedAvatar,
})
const canvasRef = React.useRef<PlaceholderCanvasRef>(null)
const canvasRef = useRef<PlaceholderCanvasRef>(null)
React.useEffect(() => {
useEffect(() => {
requestNotificationsPermission('StartOnboarding')
}, [requestNotificationsPermission])
const sheetWrapper = useSheetWrapper()
const openPicker = React.useCallback(
const openPicker = useCallback(
async (opts?: ImagePickerOptions) => {
const response = await sheetWrapper(
launchImageLibraryAsync({
@@ -139,7 +147,7 @@ export function StepProfile() {
[_, setError, sheetWrapper],
)
const onContinue = React.useCallback(async () => {
const onContinue = useCallback(async () => {
let imageUri = avatar?.image?.path
// In the event that view-shot didn't load in time and the user pressed continue, this will just be undefined
@@ -167,7 +175,7 @@ export function StepProfile() {
ax.metric('onboarding:profile:nextPressed', {})
}, [ax, avatar, dispatch])
const onDoneCreating = React.useCallback(() => {
const onDoneCreating = useCallback(() => {
setAvatar(prev => ({
...prev,
image: undefined,
@@ -176,7 +184,7 @@ export function StepProfile() {
creatorControl.close()
}, [creatorControl])
const openLibrary = React.useCallback(async () => {
const openLibrary = useCallback(async () => {
if (!(await requestPhotoAccessIfNeeded())) {
return
}
@@ -225,7 +233,7 @@ export function StepProfile() {
sheetWrapper,
])
const onSecondaryPress = React.useCallback(() => {
const onSecondaryPress = useCallback(() => {
if (avatar.useCreatedAvatar) {
openLibrary()
} else {
@@ -233,7 +241,7 @@ export function StepProfile() {
}
}, [avatar.useCreatedAvatar, creatorControl, openLibrary])
const value = React.useMemo(
const value = useMemo(
() => ({
avatar,
setAvatar,
+2 -2
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback} from 'react'
import {Plural, Trans} from '@lingui/react/macro'
import {useFocusEffect} from '@react-navigation/native'
@@ -25,7 +25,7 @@ export const PostLikedByScreen = ({route}: Props) => {
}
useFocusEffect(
React.useCallback(() => {
useCallback(() => {
setMinimalShellMode(false)
}, [setMinimalShellMode]),
)
+2 -2
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback} from 'react'
import {Plural, Trans} from '@lingui/react/macro'
import {useFocusEffect} from '@react-navigation/native'
@@ -25,7 +25,7 @@ export const PostQuotesScreen = ({route}: Props) => {
}
useFocusEffect(
React.useCallback(() => {
useCallback(() => {
setMinimalShellMode(false)
}, [setMinimalShellMode]),
)
+2 -2
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback} from 'react'
import {Plural, Trans} from '@lingui/react/macro'
import {useFocusEffect} from '@react-navigation/native'
@@ -25,7 +25,7 @@ export const PostRepostedByScreen = ({route}: Props) => {
}
useFocusEffect(
React.useCallback(() => {
useCallback(() => {
setMinimalShellMode(false)
}, [setMinimalShellMode]),
)
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useEffect, useMemo, useState} from 'react'
import {type AppBskyActorDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
@@ -71,10 +71,10 @@ function PostThreadFollowBtnLoaded({
const isFollowing = !!profile.viewer?.following
const isFollowedBy = !!profile.viewer?.followedBy
const [wasFollowing, setWasFollowing] = React.useState<boolean>(isFollowing)
const [wasFollowing, setWasFollowing] = useState<boolean>(isFollowing)
// This prevents the button from disappearing as soon as we follow.
const showFollowBtn = React.useMemo(
const showFollowBtn = useMemo(
() => !isFollowing || !wasFollowing,
[isFollowing, wasFollowing],
)
@@ -90,7 +90,7 @@ function PostThreadFollowBtnLoaded({
* sudden rendering of the button. However, on web if we do this, there's an obvious flicker once the
* button renders. So, we update the state in both cases.
*/
React.useEffect(() => {
useEffect(() => {
const updateWasFollowing = () => {
if (wasFollowing !== isFollowing) {
setWasFollowing(isFollowing)
@@ -106,7 +106,7 @@ function PostThreadFollowBtnLoaded({
}
}, [isFollowing, wasFollowing, navigation])
const onPress = React.useCallback(() => {
const onPress = useCallback(() => {
if (!isFollowing) {
requireAuth(async () => {
try {
+2 -2
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'
@@ -16,7 +16,7 @@ export function ErrorState({error}: {error: string}) {
const {_} = useLingui()
const navigation = useNavigation<NavigationProp>()
const onPressBack = React.useCallback(() => {
const onPressBack = useCallback(() => {
if (navigation.canGoBack()) {
navigation.goBack()
} else {
@@ -5,7 +5,6 @@ import Animated, {
type SharedValue,
useAnimatedStyle,
} from 'react-native-reanimated'
import type React from 'react'
import {usePagerHeaderContext} from '#/view/com/pager/PagerHeaderContext'
import {IS_IOS} from '#/env'
@@ -13,7 +13,6 @@ import Animated, {
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {BlurView} from 'expo-blur'
import {useIsFetching} from '@tanstack/react-query'
import type React from 'react'
import {RQKEY_ROOT as STARTERPACK_RQKEY_ROOT} from '#/state/queries/actor-starter-packs'
import {RQKEY_ROOT as FEED_RQKEY_ROOT} from '#/state/queries/post-feed'
+6 -6
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useMemo, useState} from 'react'
import {type AppBskyActorDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
@@ -51,7 +51,7 @@ export const ProfileKnownFollowersScreen = ({route}: Props) => {
const {name} = route.params
const [isPTRing, setIsPTRing] = React.useState(false)
const [isPTRing, setIsPTRing] = useState(false)
const {
data: resolvedDid,
isLoading: isDidLoading,
@@ -67,7 +67,7 @@ export const ProfileKnownFollowersScreen = ({route}: Props) => {
refetch,
} = useProfileKnownFollowersQuery(resolvedDid)
const onRefresh = React.useCallback(async () => {
const onRefresh = useCallback(async () => {
setIsPTRing(true)
try {
await refetch()
@@ -77,7 +77,7 @@ export const ProfileKnownFollowersScreen = ({route}: Props) => {
setIsPTRing(false)
}, [refetch, setIsPTRing])
const onEndReached = React.useCallback(async () => {
const onEndReached = useCallback(async () => {
if (isFetchingNextPage || !hasNextPage || !!error) return
try {
await fetchNextPage()
@@ -86,7 +86,7 @@ export const ProfileKnownFollowersScreen = ({route}: Props) => {
}
}, [isFetchingNextPage, hasNextPage, error, fetchNextPage])
const followers = React.useMemo(() => {
const followers = useMemo(() => {
if (data?.pages) {
return data.pages.flatMap(page => page.followers)
}
@@ -96,7 +96,7 @@ export const ProfileKnownFollowersScreen = ({route}: Props) => {
const isError = Boolean(resolveError || error)
useFocusEffect(
React.useCallback(() => {
useCallback(() => {
setMinimalShellMode(false)
}, [setMinimalShellMode]),
)
+2 -2
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback} from 'react'
import {Plural} from '@lingui/react/macro'
import {useFocusEffect} from '@react-navigation/native'
@@ -24,7 +24,7 @@ export const ProfileFollowersScreen = ({route}: Props) => {
})
useFocusEffect(
React.useCallback(() => {
useCallback(() => {
setMinimalShellMode(false)
}, [setMinimalShellMode]),
)
+2 -2
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback} from 'react'
import {Plural} from '@lingui/react/macro'
import {useFocusEffect} from '@react-navigation/native'
@@ -24,7 +24,7 @@ export const ProfileFollowsScreen = ({route}: Props) => {
})
useFocusEffect(
React.useCallback(() => {
useCallback(() => {
setMinimalShellMode(false)
}, [setMinimalShellMode]),
)
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback} from 'react'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {useFocusEffect} from '@react-navigation/native'
@@ -22,7 +22,7 @@ export function ProfileLabelerLikedByScreen({
const {_} = useLingui()
useFocusEffect(
React.useCallback(() => {
useCallback(() => {
setMinimalShellMode(false)
}, [setMinimalShellMode]),
)
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useMemo, useState} from 'react'
import {View} from 'react-native'
import {AtUri} from '@atproto/api'
import {msg} from '@lingui/core/macro'
@@ -95,7 +95,7 @@ export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
const {data: preferences} = usePreferencesQuery()
const [likeUri, setLikeUri] = React.useState(info.likeUri || '')
const [likeUri, setLikeUri] = useState(info.likeUri || '')
const likeCount =
(info.likeCount || 0) +
(likeUri && !info.likeUri ? 1 : !likeUri && info.likeUri ? -1 : 0)
@@ -401,7 +401,7 @@ function DialogInner({
useUnlikeMutation()
const isLiked = !!likeUri
const feedRkey = React.useMemo(() => new AtUri(info.uri).rkey, [info.uri])
const feedRkey = useMemo(() => new AtUri(info.uri).rkey, [info.uri])
const onToggleLiked = async () => {
try {
@@ -427,14 +427,14 @@ function DialogInner({
}
}
const onPressShare = React.useCallback(() => {
const onPressShare = useCallback(() => {
playHaptic()
const url = toShareUrl(info.route.href)
shareUrl(url)
ax.metric('feed:share', {feedUrl: info.uri})
}, [info, playHaptic])
const onPressReport = React.useCallback(() => {
const onPressReport = useCallback(() => {
reportDialogControl.open()
}, [reportDialogControl])
@@ -1,4 +1,4 @@
import React from 'react'
import {useState} from 'react'
import {View} from 'react-native'
import {
type AppBskyGraphDefs,
@@ -143,7 +143,7 @@ export function AvatarStack({
const computedTotal = (total ?? numPending) - numPending
const circlesCount = numPending + 1 // add total at end
const widthPerc = 100 / circlesCount
const [size, setSize] = React.useState<number | null>(null)
const [size, setSize] = useState<number | null>(null)
const isPending = (numPending && profiles.length === 0) || !moderationOpts
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useState} from 'react'
import {View} from 'react-native'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
@@ -36,10 +36,10 @@ function DeactivateAccountDialogInner({
const {_} = useLingui()
const agent = useAgent()
const {logoutCurrentAccount} = useSessionApi()
const [pending, setPending] = React.useState(false)
const [error, setError] = React.useState<string | undefined>()
const [pending, setPending] = useState(false)
const [error, setError] = useState<string | undefined>()
const handleDeactivate = React.useCallback(async () => {
const handleDeactivate = useCallback(async () => {
try {
setPending(true)
await agent.com.atproto.server.deactivateAccount({})
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback} from 'react'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
@@ -17,7 +17,7 @@ export function Email2FAToggle() {
const disableDialogControl = useDialogControl()
const emailDialogControl = useEmailDialogControl()
const onToggle = React.useCallback(() => {
const onToggle = useCallback(() => {
emailDialogControl.open({
id: EmailDialogScreenID.Manage2FA,
})
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback} from 'react'
import {View} from 'react-native'
import {type $Typed, ComAtprotoLabelDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro'
@@ -26,7 +26,7 @@ export function PwiOptOut() {
profile?.labels?.some(l => l.val === '!no-unauthenticated') || false
const canToggle = profile && !updateProfile.isPending
const onToggleOptOut = React.useCallback(() => {
const onToggleOptOut = useCallback(() => {
if (!profile) {
return
}
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useEffect} from 'react'
import {StyleSheet} from 'react-native'
// @ts-ignore web only, we will always redirect to the app on web (CORS)
@@ -15,7 +15,7 @@ export function CaptchaWebView({
onSuccess: (code: string) => void
onError: (error: unknown) => void
}) {
React.useEffect(() => {
useEffect(() => {
const timeout = setTimeout(() => {
onError({
errorMessage: 'User did not complete the captcha within 30 seconds',
@@ -27,7 +27,7 @@ export function CaptchaWebView({
}
}, [onError])
const onLoad = React.useCallback(() => {
const onLoad = useCallback(() => {
// @ts-ignore web
const frame: HTMLIFrameElement = document.getElementById(
'captcha-iframe',
+7 -7
View File
@@ -1,4 +1,4 @@
import React, {useEffect, useState} from 'react'
import {useCallback, useEffect, useMemo, useState} from 'react'
import {ActivityIndicator, Platform, View} from 'react-native'
import ReactNativeDeviceAttest from 'react-native-device-attest'
import {msg} from '@lingui/core/macro'
@@ -75,10 +75,10 @@ function StepCaptchaInner({
const theme = useTheme()
const {state, dispatch} = useSignupContext()
const [completed, setCompleted] = React.useState(false)
const [completed, setCompleted] = useState(false)
const stateParam = React.useMemo(() => nanoid(15), [])
const url = React.useMemo(() => {
const stateParam = useMemo(() => nanoid(15), [])
const url = useMemo(() => {
const newUrl = new URL(state.serviceUrl)
newUrl.pathname = CAPTCHA_PATH
newUrl.searchParams.set(
@@ -107,7 +107,7 @@ function StepCaptchaInner({
payload,
])
const onSuccess = React.useCallback(
const onSuccess = useCallback(
(code: string) => {
setCompleted(true)
ax.metric('signup:captchaSuccess', {})
@@ -119,7 +119,7 @@ function StepCaptchaInner({
[ax, dispatch],
)
const onError = React.useCallback(
const onError = useCallback(
(error?: unknown) => {
dispatch({
type: 'setError',
@@ -134,7 +134,7 @@ function StepCaptchaInner({
[_, ax, dispatch, state.handle],
)
const onBackPress = React.useCallback(() => {
const onBackPress = useCallback(() => {
logger.error('Signup Flow Error', {
errorMessage:
'User went back from captcha step. Possibly encountered an error.',
+4 -4
View File
@@ -1,4 +1,4 @@
import React, {useRef} from 'react'
import {useEffect, useRef, useState} from 'react'
import {type TextInput, View} from 'react-native'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
@@ -89,10 +89,10 @@ export function StepInfo({
: true
const isDeviceGeolocationGranted = useIsDeviceGeolocationGranted()
const [hasWarnedEmail, setHasWarnedEmail] = React.useState<boolean>(false)
const [hasWarnedEmail, setHasWarnedEmail] = useState<boolean>(false)
const tldtsRef = React.useRef<typeof tldts>(undefined)
React.useEffect(() => {
const tldtsRef = useRef<typeof tldts>(undefined)
useEffect(() => {
// @ts-expect-error - valid path
import('tldts/dist/index.cjs.min.js').then(tldts => {
tldtsRef.current = tldts
+3 -3
View File
@@ -1,4 +1,4 @@
import React, {useCallback} from 'react'
import {createContext, useCallback, useContext} from 'react'
import {LayoutAnimation} from 'react-native'
import {
ComAtprotoServerCreateAccount,
@@ -250,9 +250,9 @@ interface IContext {
state: SignupState
dispatch: React.Dispatch<SignupAction>
}
export const SignupContext = React.createContext<IContext>({} as IContext)
export const SignupContext = createContext<IContext>({} as IContext)
SignupContext.displayName = 'SignupContext'
export const useSignupContext = () => React.useContext(SignupContext)
export const useSignupContext = () => useContext(SignupContext)
export function useSubmitSignup() {
const ax = useAnalytics()
+6 -6
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useEffect, useState} from 'react'
import {Modal, ScrollView, View} from 'react-native'
import {SystemBars} from 'react-native-edge-to-edge'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
@@ -27,15 +27,15 @@ export function SignupQueued() {
const {logoutCurrentAccount} = useSessionApi()
const agent = useAgent()
const [isProcessing, setProcessing] = React.useState(false)
const [estimatedTime, setEstimatedTime] = React.useState<string | undefined>(
const [isProcessing, setProcessing] = useState(false)
const [estimatedTime, setEstimatedTime] = useState<string | undefined>(
undefined,
)
const [placeInQueue, setPlaceInQueue] = React.useState<number | undefined>(
const [placeInQueue, setPlaceInQueue] = useState<number | undefined>(
undefined,
)
const checkStatus = React.useCallback(async () => {
const checkStatus = useCallback(async () => {
setProcessing(true)
try {
const res = await agent.com.atproto.temp.checkSignupQueue()
@@ -65,7 +65,7 @@ export function SignupQueued() {
agent,
])
React.useEffect(() => {
useEffect(() => {
checkStatus()
const interval = setInterval(checkStatus, 60e3)
return () => clearInterval(interval)
@@ -1,4 +1,4 @@
import React from 'react'
import {useEffect, useState} from 'react'
import {Pressable, View} from 'react-native'
import Animated, {FadeIn, FadeOut} from 'react-native-reanimated'
import {
@@ -75,7 +75,7 @@ export function LandingScreen({
AppBskyGraphDefs.validateStarterPackView(starterPack) &&
AppBskyGraphStarterpack.validateRecord(starterPack.record)
React.useEffect(() => {
useEffect(() => {
if (isErrorStarterPack || (starterPack && !isValid)) {
setScreenState(LoggedOutScreenState.S_LoginOrCreateAccount)
}
@@ -128,8 +128,7 @@ function LandingScreenLoaded({
const androidDialogControl = useDialogControl()
const [descriptionRt] = useRichText(record.description || '')
const [appClipOverlayVisible, setAppClipOverlayVisible] =
React.useState(false)
const [appClipOverlayVisible, setAppClipOverlayVisible] = useState(false)
const listItemsCount = starterPack.list?.listItemCount ?? 0
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useEffect, useState} from 'react'
import {View} from 'react-native'
import {Image} from 'expo-image'
import {
@@ -201,16 +201,16 @@ function StarterPackScreenLoaded({
const shareDialogControl = useDialogControl()
const shortenLink = useShortenLink()
const [link, setLink] = React.useState<string>()
const [imageLoaded, setImageLoaded] = React.useState(false)
const [link, setLink] = useState<string>()
const [imageLoaded, setImageLoaded] = useState(false)
React.useEffect(() => {
useEffect(() => {
ax.metric('starterPack:opened', {
starterPack: starterPack.uri,
})
}, [ax, starterPack.uri])
const onOpenShareDialog = React.useCallback(() => {
const onOpenShareDialog = useCallback(() => {
const rkey = new AtUri(starterPack.uri).rkey
shortenLink(makeStarterPackLink(starterPack.creator.did, rkey)).then(
res => {
@@ -227,7 +227,7 @@ function StarterPackScreenLoaded({
shareDialogControl.open()
}, [shareDialogControl, shortenLink, starterPack])
React.useEffect(() => {
useEffect(() => {
if (routeParams.new) {
onOpenShareDialog()
}
@@ -316,7 +316,7 @@ function Header({
const {requestSwitchToAccount} = useLoggedOutViewControls()
const {captureAction} = useProgressGuideControls()
const [isProcessing, setIsProcessing] = React.useState(false)
const [isProcessing, setIsProcessing] = useState(false)
const {record, creator} = starterPack
const isOwn = creator?.did === currentAccount?.did
@@ -325,7 +325,7 @@ function Header({
const navigation = useNavigation<NavigationProp>()
React.useEffect(() => {
useEffect(() => {
const onFocus = () => {
if (hasSession) return
setActiveStarterPack({
@@ -731,7 +731,7 @@ function InvalidStarterPack({rkey}: {rkey: string}) {
const t = useTheme()
const navigation = useNavigation<NavigationProp>()
const {gtMobile} = useBreakpoints()
const [isProcessing, setIsProcessing] = React.useState(false)
const [isProcessing, setIsProcessing] = useState(false)
const goBack = () => {
if (navigation.canGoBack()) {
+4 -4
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {createContext, useContext, useReducer} from 'react'
import {
type AppBskyFeedDefs,
type AppBskyGraphDefs,
@@ -41,12 +41,12 @@ interface State {
type TStateContext = [State, (action: Action) => void]
const StateContext = React.createContext<TStateContext>([
const StateContext = createContext<TStateContext>([
{} as State,
(_: Action) => {},
])
StateContext.displayName = 'StarterPackWizardStateContext'
export const useWizardState = () => React.useContext(StateContext)
export const useWizardState = () => useContext(StateContext)
function reducer(state: State, action: Action): State {
let updatedState = state
@@ -157,7 +157,7 @@ export function Provider({
}
}
const [state, dispatch] = React.useReducer(reducer, null, createInitialState)
const [state, dispatch] = useReducer(reducer, null, createInitialState)
return (
<StateContext.Provider value={[state, dispatch]}>
+3 -3
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useEffect} from 'react'
import {Keyboard, View} from 'react-native'
import {KeyboardAwareScrollView} from 'react-native-keyboard-controller'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
@@ -179,14 +179,14 @@ function WizardInner({
})
const parsed = parseStarterPackUri(currentStarterPack?.uri)
React.useEffect(() => {
useEffect(() => {
navigation.setOptions({
gestureEnabled: false,
})
}, [navigation])
useFocusEffect(
React.useCallback(() => {
useCallback(() => {
setMinimalShellMode(true)
return () => {
+11 -11
View File
@@ -1,4 +1,4 @@
import React from 'react'
import {useCallback, useMemo, useState} from 'react'
import {type ListRenderItemInfo, View} from 'react-native'
import {type AppBskyFeedDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro'
@@ -39,26 +39,26 @@ export default function TopicScreen({
const {topic} = route.params
const {_} = useLingui()
const headerTitle = React.useMemo(() => {
const headerTitle = useMemo(() => {
return enforceLen(decodeURIComponent(topic), 24, true, 'middle')
}, [topic])
const onShare = React.useCallback(() => {
const onShare = useCallback(() => {
const url = new URL('https://bsky.app')
url.pathname = `/topic/${topic}`
shareUrl(url.toString())
}, [topic])
const [activeTab, setActiveTab] = React.useState(0)
const [activeTab, setActiveTab] = useState(0)
const setMinimalShellMode = useSetMinimalShellMode()
useFocusEffect(
React.useCallback(() => {
useCallback(() => {
setMinimalShellMode(false)
}, [setMinimalShellMode]),
)
const onPageSelected = React.useCallback(
const onPageSelected = useCallback(
(index: number) => {
setMinimalShellMode(false)
setActiveTab(index)
@@ -66,7 +66,7 @@ export default function TopicScreen({
[setMinimalShellMode],
)
const sections = React.useMemo(() => {
const sections = useMemo(() => {
return [
{
title: _(msg`Top`),
@@ -135,7 +135,7 @@ function TopicScreenTab({
}) {
const {_} = useLingui()
const initialNumToRender = useInitialNumToRender()
const [isPTR, setIsPTR] = React.useState(false)
const [isPTR, setIsPTR] = useState(false)
const trackPostView = usePostViewTracking('Topic')
const {
@@ -154,17 +154,17 @@ function TopicScreenTab({
enabled: active,
})
const posts = React.useMemo(() => {
const posts = useMemo(() => {
return data?.pages.flatMap(page => page.posts) || []
}, [data])
const onRefresh = React.useCallback(async () => {
const onRefresh = useCallback(async () => {
setIsPTR(true)
await refetch()
setIsPTR(false)
}, [refetch])
const onEndReached = React.useCallback(() => {
const onEndReached = useCallback(() => {
if (isFetchingNextPage || !hasNextPage || error) return
fetchNextPage()
}, [isFetchingNextPage, hasNextPage, error, fetchNextPage])