Compare commits
1 Commits
1.131.0
...
react-codemod
| Author | SHA1 | Date | |
|---|---|---|---|
| b410ba867f |
@@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
import {type SVGAttributes} from 'react'
|
||||
|
||||
export function Butterfly(props: React.SVGAttributes<SVGSVGElement>) {
|
||||
export function Butterfly(props: SVGAttributes<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {type ImgHTMLAttributes} from 'react'
|
||||
|
||||
// @NOTE satori does not currently support webp, see vercel/satori#273
|
||||
function detectMime(buf: Buffer): string {
|
||||
@@ -10,7 +10,7 @@ function detectMime(buf: Buffer): string {
|
||||
}
|
||||
|
||||
export function Img(
|
||||
props: Omit<React.ImgHTMLAttributes<HTMLImageElement>, 'src'> & {src: Buffer},
|
||||
props: Omit<ImgHTMLAttributes<HTMLImageElement>, 'src'> & {src: Buffer},
|
||||
) {
|
||||
const {src, ...others} = props
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable bsky-internal/avoid-unwrapped-text */
|
||||
import React from 'react'
|
||||
import {AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
|
||||
import {type AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
|
||||
|
||||
import {Butterfly} from './Butterfly.js'
|
||||
import {Img} from './Img.js'
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import assert from 'node:assert'
|
||||
|
||||
import React from 'react'
|
||||
import {type AppBskyGraphDefs, AtUri} from '@atproto/api'
|
||||
import resvg from '@resvg/resvg-js'
|
||||
import {type Express} from 'express'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as React from 'react'
|
||||
import {Component, createRef} from 'react'
|
||||
import {type ComponentType, type ContextType, type RefObject} from 'react'
|
||||
import {
|
||||
Dimensions,
|
||||
type LayoutChangeEvent,
|
||||
@@ -21,9 +22,9 @@ import {
|
||||
Context as PortalContext,
|
||||
} from './BottomSheetPortal'
|
||||
|
||||
const NativeView: React.ComponentType<
|
||||
const NativeView: ComponentType<
|
||||
BottomSheetViewProps & {
|
||||
ref: React.RefObject<any>
|
||||
ref: RefObject<any>
|
||||
style: StyleProp<ViewStyle>
|
||||
}
|
||||
> = requireNativeViewManager('BottomSheet')
|
||||
@@ -39,14 +40,14 @@ const IS_IOS15 =
|
||||
const IS_NON_E2E_ANDROID =
|
||||
Platform.OS === 'android' && Number(Platform.Version) < 35
|
||||
|
||||
export class BottomSheetNativeComponent extends React.Component<
|
||||
export class BottomSheetNativeComponent extends Component<
|
||||
BottomSheetViewProps,
|
||||
{
|
||||
open: boolean
|
||||
viewHeight?: number
|
||||
}
|
||||
> {
|
||||
ref = React.createRef<any>()
|
||||
ref = createRef<any>()
|
||||
|
||||
static contextType = PortalContext
|
||||
|
||||
@@ -79,7 +80,7 @@ export class BottomSheetNativeComponent extends React.Component<
|
||||
}
|
||||
|
||||
render() {
|
||||
const Portal = this.context as React.ContextType<typeof PortalContext>
|
||||
const Portal = this.context as ContextType<typeof PortalContext>
|
||||
if (!Portal) {
|
||||
throw new Error(
|
||||
'BottomSheet: You need to wrap your component tree with a <BottomSheetPortalProvider> to use the bottom sheet.',
|
||||
@@ -139,7 +140,7 @@ function BottomSheetNativeComponentInner({
|
||||
onStateChange: (
|
||||
event: NativeSyntheticEvent<{state: BottomSheetState}>,
|
||||
) => void
|
||||
nativeViewRef: React.RefObject<View>
|
||||
nativeViewRef: RefObject<View>
|
||||
onLayout?: (event: LayoutChangeEvent) => void
|
||||
}) {
|
||||
const insets = useSafeAreaInsets()
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
import React from 'react'
|
||||
import {createContext, useContext, useMemo} from 'react'
|
||||
import {type ElementType, type ReactNode} from 'react'
|
||||
|
||||
import {createPortalGroup_INTERNAL} from './lib/Portal'
|
||||
|
||||
type PortalContext = React.ElementType<{children: React.ReactNode}>
|
||||
type PortalContext = ElementType<{children: ReactNode}>
|
||||
|
||||
export const Context = React.createContext({} as PortalContext)
|
||||
export const Context = createContext({} as PortalContext)
|
||||
Context.displayName = 'BottomSheetPortalContext'
|
||||
|
||||
export const useBottomSheetPortal_INTERNAL = () => React.useContext(Context)
|
||||
export const useBottomSheetPortal_INTERNAL = () => useContext(Context)
|
||||
|
||||
export function BottomSheetPortalProvider({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const portal = React.useMemo(() => {
|
||||
export function BottomSheetPortalProvider({children}: {children: ReactNode}) {
|
||||
const portal = useMemo(() => {
|
||||
return createPortalGroup_INTERNAL()
|
||||
}, [])
|
||||
|
||||
@@ -32,7 +29,7 @@ const defaultPortal = createPortalGroup_INTERNAL()
|
||||
|
||||
export const BottomSheetOutlet = defaultPortal.Outlet
|
||||
|
||||
export function BottomSheetProvider({children}: {children: React.ReactNode}) {
|
||||
export function BottomSheetProvider({children}: {children: ReactNode}) {
|
||||
return (
|
||||
<Context.Provider value={defaultPortal.Portal}>
|
||||
<defaultPortal.Provider>{children}</defaultPortal.Provider>
|
||||
|
||||
+9
-9
@@ -1,6 +1,7 @@
|
||||
import React from 'react'
|
||||
import {createContext, useContext, useEffect, useMemo, useState} from 'react'
|
||||
import {type ReactNode} from 'react'
|
||||
|
||||
import {BackgroundNotificationHandlerPreferences} from './ExpoBackgroundNotificationHandler.types'
|
||||
import {type BackgroundNotificationHandlerPreferences} from './ExpoBackgroundNotificationHandler.types'
|
||||
import {BackgroundNotificationHandler} from './ExpoBackgroundNotificationHandlerModule'
|
||||
|
||||
interface BackgroundNotificationPreferencesContext {
|
||||
@@ -11,30 +12,29 @@ interface BackgroundNotificationPreferencesContext {
|
||||
) => void
|
||||
}
|
||||
|
||||
const Context = React.createContext<BackgroundNotificationPreferencesContext>(
|
||||
const Context = createContext<BackgroundNotificationPreferencesContext>(
|
||||
{} as BackgroundNotificationPreferencesContext,
|
||||
)
|
||||
export const useBackgroundNotificationPreferences = () =>
|
||||
React.useContext(Context)
|
||||
export const useBackgroundNotificationPreferences = () => useContext(Context)
|
||||
|
||||
export function BackgroundNotificationPreferencesProvider({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
children: ReactNode
|
||||
}) {
|
||||
const [preferences, setPreferences] =
|
||||
React.useState<BackgroundNotificationHandlerPreferences>({
|
||||
useState<BackgroundNotificationHandlerPreferences>({
|
||||
playSoundChat: true,
|
||||
})
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
;(async () => {
|
||||
const prefs = await BackgroundNotificationHandler.getAllPrefsAsync()
|
||||
setPreferences(prefs)
|
||||
})()
|
||||
}, [])
|
||||
|
||||
const value = React.useMemo(
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
preferences,
|
||||
setPref: async <
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import React from 'react'
|
||||
import {createRef, PureComponent} from 'react'
|
||||
import {type ComponentType, type RefObject} from 'react'
|
||||
import {requireNativeModule} from 'expo'
|
||||
import {requireNativeViewManager} from 'expo-modules-core'
|
||||
|
||||
import {GifViewProps} from './GifView.types'
|
||||
import {type GifViewProps} from './GifView.types'
|
||||
|
||||
const NativeModule = requireNativeModule('ExpoBlueskyGifView')
|
||||
const NativeView: React.ComponentType<
|
||||
GifViewProps & {ref: React.RefObject<any>}
|
||||
> = requireNativeViewManager('ExpoBlueskyGifView')
|
||||
const NativeView: ComponentType<GifViewProps & {ref: RefObject<any>}> =
|
||||
requireNativeViewManager('ExpoBlueskyGifView')
|
||||
|
||||
export class GifView extends React.PureComponent<GifViewProps> {
|
||||
export class GifView extends PureComponent<GifViewProps> {
|
||||
// TODO native types, should all be the same as those in this class
|
||||
private nativeRef: React.RefObject<any> = React.createRef()
|
||||
private nativeRef: RefObject<any> = createRef()
|
||||
|
||||
constructor(props: GifViewProps | Readonly<GifViewProps>) {
|
||||
super(props)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import * as React from 'react'
|
||||
import {createRef, PureComponent} from 'react'
|
||||
import {type RefObject} from 'react'
|
||||
import {StyleSheet} from 'react-native'
|
||||
|
||||
import {GifViewProps} from './GifView.types'
|
||||
import {type GifViewProps} from './GifView.types'
|
||||
|
||||
export class GifView extends React.PureComponent<GifViewProps> {
|
||||
private readonly videoPlayerRef: React.RefObject<HTMLMediaElement> =
|
||||
React.createRef()
|
||||
export class GifView extends PureComponent<GifViewProps> {
|
||||
private readonly videoPlayerRef: RefObject<HTMLMediaElement> = createRef()
|
||||
private isLoaded = false
|
||||
|
||||
constructor(props: GifViewProps | Readonly<GifViewProps>) {
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
import '#/logger/sentry/setup'
|
||||
import '#/view/icons'
|
||||
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import {useEffect, useState} from 'react'
|
||||
import * as React from 'react'
|
||||
import {GestureHandlerRootView} from 'react-native-gesture-handler'
|
||||
import {KeyboardProvider as KeyboardControllerProvider} from 'react-native-keyboard-controller'
|
||||
import {
|
||||
|
||||
+8
-9
@@ -1,4 +1,5 @@
|
||||
import React, {useCallback, useEffect} from 'react'
|
||||
import {forwardRef, useCallback, useEffect, useState} from 'react'
|
||||
import {type PropsWithChildren} from 'react'
|
||||
import {
|
||||
AccessibilityInfo,
|
||||
Image as RNImage,
|
||||
@@ -29,7 +30,7 @@ const darkSplashImageUri = RNImage.resolveAssetSource(
|
||||
darkSplashImagePointer,
|
||||
).uri
|
||||
|
||||
export const Logo = React.forwardRef(function LogoImpl(props: SvgProps, ref) {
|
||||
export const Logo = forwardRef(function LogoImpl(props: SvgProps, ref) {
|
||||
const width = 1000
|
||||
const height = width * (67 / 64)
|
||||
return (
|
||||
@@ -51,19 +52,17 @@ type Props = {
|
||||
isReady: boolean
|
||||
}
|
||||
|
||||
export function Splash(props: React.PropsWithChildren<Props>) {
|
||||
export function Splash(props: PropsWithChildren<Props>) {
|
||||
'use no memo'
|
||||
const insets = useSafeAreaInsets()
|
||||
const intro = useSharedValue(0)
|
||||
const outroLogo = useSharedValue(0)
|
||||
const outroApp = useSharedValue(0)
|
||||
const outroAppOpacity = useSharedValue(0)
|
||||
const [isAnimationComplete, setIsAnimationComplete] = React.useState(false)
|
||||
const [isImageLoaded, setIsImageLoaded] = React.useState(false)
|
||||
const [isLayoutReady, setIsLayoutReady] = React.useState(false)
|
||||
const [reduceMotion, setReduceMotion] = React.useState<boolean | undefined>(
|
||||
false,
|
||||
)
|
||||
const [isAnimationComplete, setIsAnimationComplete] = useState(false)
|
||||
const [isImageLoaded, setIsImageLoaded] = useState(false)
|
||||
const [isLayoutReady, setIsLayoutReady] = useState(false)
|
||||
const [reduceMotion, setReduceMotion] = useState<boolean | undefined>(false)
|
||||
const isReady =
|
||||
props.isReady &&
|
||||
isImageLoaded &&
|
||||
|
||||
+13
-16
@@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import {createContext, useCallback, useContext, useMemo, useState} from 'react'
|
||||
import {type PropsWithChildren} from 'react'
|
||||
import {type Theme, type ThemeName} from '@bsky.app/alf'
|
||||
|
||||
import {
|
||||
@@ -46,7 +47,7 @@ export type Alf = {
|
||||
/*
|
||||
* Context
|
||||
*/
|
||||
export const Context = React.createContext<Alf>({
|
||||
export const Context = createContext<Alf>({
|
||||
themeName: 'light',
|
||||
theme: themes.light,
|
||||
themes,
|
||||
@@ -64,16 +65,14 @@ Context.displayName = 'AlfContext'
|
||||
export function ThemeProvider({
|
||||
children,
|
||||
theme: themeName,
|
||||
}: React.PropsWithChildren<{theme: ThemeName}>) {
|
||||
const [fontScale, setFontScale] = React.useState<Alf['fonts']['scale']>(() =>
|
||||
}: PropsWithChildren<{theme: ThemeName}>) {
|
||||
const [fontScale, setFontScale] = useState<Alf['fonts']['scale']>(() =>
|
||||
getFontScale(),
|
||||
)
|
||||
const [fontScaleMultiplier, setFontScaleMultiplier] = React.useState(() =>
|
||||
const [fontScaleMultiplier, setFontScaleMultiplier] = useState(() =>
|
||||
computeFontScaleMultiplier(fontScale),
|
||||
)
|
||||
const setFontScaleAndPersist = React.useCallback<
|
||||
Alf['fonts']['setFontScale']
|
||||
>(
|
||||
const setFontScaleAndPersist = useCallback<Alf['fonts']['setFontScale']>(
|
||||
fs => {
|
||||
setFontScale(fs)
|
||||
persistFontScale(fs)
|
||||
@@ -81,12 +80,10 @@ export function ThemeProvider({
|
||||
},
|
||||
[setFontScale],
|
||||
)
|
||||
const [fontFamily, setFontFamily] = React.useState<Alf['fonts']['family']>(
|
||||
() => getFontFamily(),
|
||||
const [fontFamily, setFontFamily] = useState<Alf['fonts']['family']>(() =>
|
||||
getFontFamily(),
|
||||
)
|
||||
const setFontFamilyAndPersist = React.useCallback<
|
||||
Alf['fonts']['setFontFamily']
|
||||
>(
|
||||
const setFontFamilyAndPersist = useCallback<Alf['fonts']['setFontFamily']>(
|
||||
ff => {
|
||||
setFontFamily(ff)
|
||||
persistFontFamily(ff)
|
||||
@@ -94,7 +91,7 @@ export function ThemeProvider({
|
||||
[setFontFamily],
|
||||
)
|
||||
|
||||
const value = React.useMemo<Alf>(
|
||||
const value = useMemo<Alf>(
|
||||
() => ({
|
||||
themes,
|
||||
themeName: themeName,
|
||||
@@ -122,12 +119,12 @@ export function ThemeProvider({
|
||||
}
|
||||
|
||||
export function useAlf() {
|
||||
return React.useContext(Context)
|
||||
return useContext(Context)
|
||||
}
|
||||
|
||||
export function useTheme(theme?: ThemeName) {
|
||||
const alf = useAlf()
|
||||
return React.useMemo(() => {
|
||||
return useMemo(() => {
|
||||
return theme ? alf.themes[theme] : alf.theme
|
||||
}, [theme, alf])
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useLayoutEffect} from 'react'
|
||||
import {type ColorSchemeName, useColorScheme} from 'react-native'
|
||||
import {type ThemeName} from '@bsky.app/alf'
|
||||
|
||||
@@ -9,7 +9,7 @@ import {IS_WEB} from '#/env'
|
||||
export function useColorModeTheme(): ThemeName {
|
||||
const theme = useThemeName()
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
useLayoutEffect(() => {
|
||||
updateDocument(theme)
|
||||
}, [theme])
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useMemo} from 'react'
|
||||
|
||||
import {type Breakpoint, useBreakpoints} from '#/alf/breakpoints'
|
||||
import * as tokens from '#/alf/tokens'
|
||||
@@ -52,7 +52,7 @@ export function useGutters([top, right, bottom, left]: Gutter[]) {
|
||||
bottom = top
|
||||
left = right
|
||||
}
|
||||
return React.useMemo(() => {
|
||||
return useMemo(() => {
|
||||
return {
|
||||
paddingTop: top === 0 ? 0 : gutters[top][activeBreakpoint || 'default'],
|
||||
paddingRight:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, {useCallback} from 'react'
|
||||
import {Fragment, useCallback} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type AppBskyActorDefs} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
@@ -52,7 +52,7 @@ export function AccountList({
|
||||
t.atoms.border_contrast_low,
|
||||
]}>
|
||||
{accounts.map(account => (
|
||||
<React.Fragment key={account.did}>
|
||||
<Fragment key={account.did}>
|
||||
<AccountItem
|
||||
profile={profiles?.profiles.find(p => p.did === account.did)}
|
||||
account={account}
|
||||
@@ -61,7 +61,7 @@ export function AccountList({
|
||||
isPendingAccount={account.did === pendingDid}
|
||||
/>
|
||||
<View style={[a.border_b, t.atoms.border_contrast_low]} />
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
))}
|
||||
<Button
|
||||
testID="chooseAddAccountBtn"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useCallback} from 'react'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
@@ -20,7 +20,7 @@ export function AppLanguageDropdown() {
|
||||
const setLangPrefs = useLanguagePrefsApi()
|
||||
const sanitizedLang = sanitizeAppLanguageSetting(langPrefs.appLanguage)
|
||||
|
||||
const onChangeAppLanguage = React.useCallback(
|
||||
const onChangeAppLanguage = useCallback(
|
||||
(value: string) => {
|
||||
if (!value) return
|
||||
if (sanitizedLang !== value) {
|
||||
|
||||
+84
-77
@@ -1,4 +1,12 @@
|
||||
import React from 'react'
|
||||
import {
|
||||
createContext,
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useContext,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import {type ComponentType, type ReactElement, type ReactNode} from 'react'
|
||||
import {
|
||||
type AccessibilityProps,
|
||||
type GestureResponderEvent,
|
||||
@@ -75,8 +83,8 @@ export type ButtonState = {
|
||||
export type ButtonContext = VariantProps & ButtonState
|
||||
|
||||
type NonTextElements =
|
||||
| React.ReactElement<any>
|
||||
| Iterable<React.ReactElement<any> | null | undefined | boolean>
|
||||
| ReactElement<any>
|
||||
| Iterable<ReactElement<any> | null | undefined | boolean>
|
||||
|
||||
export type ButtonProps = Pick<
|
||||
PressableProps,
|
||||
@@ -102,13 +110,13 @@ export type ButtonProps = Pick<
|
||||
style?: StyleProp<ViewStyle>
|
||||
hoverStyle?: StyleProp<ViewStyle>
|
||||
children: NonTextElements | ((context: ButtonContext) => NonTextElements)
|
||||
PressableComponent?: React.ComponentType<PressableProps>
|
||||
PressableComponent?: ComponentType<PressableProps>
|
||||
}
|
||||
|
||||
export type ButtonTextProps = TextProps &
|
||||
VariantProps & {disabled?: boolean; emoji?: boolean}
|
||||
|
||||
const Context = React.createContext<VariantProps & ButtonState>({
|
||||
const Context = createContext<VariantProps & ButtonState>({
|
||||
hovered: false,
|
||||
focused: false,
|
||||
pressed: false,
|
||||
@@ -117,10 +125,10 @@ const Context = React.createContext<VariantProps & ButtonState>({
|
||||
Context.displayName = 'ButtonContext'
|
||||
|
||||
export function useButtonContext() {
|
||||
return React.useContext(Context)
|
||||
return useContext(Context)
|
||||
}
|
||||
|
||||
export const Button = React.forwardRef<View, ButtonProps>(
|
||||
export const Button = forwardRef<View, ButtonProps>(
|
||||
(
|
||||
{
|
||||
children,
|
||||
@@ -153,13 +161,13 @@ export const Button = React.forwardRef<View, ButtonProps>(
|
||||
}
|
||||
|
||||
const t = useTheme()
|
||||
const [state, setState] = React.useState({
|
||||
const [state, setState] = useState({
|
||||
pressed: false,
|
||||
hovered: false,
|
||||
focused: false,
|
||||
})
|
||||
|
||||
const onPressIn = React.useCallback(
|
||||
const onPressIn = useCallback(
|
||||
(e: GestureResponderEvent) => {
|
||||
setState(s => ({
|
||||
...s,
|
||||
@@ -169,7 +177,7 @@ export const Button = React.forwardRef<View, ButtonProps>(
|
||||
},
|
||||
[setState, onPressInOuter],
|
||||
)
|
||||
const onPressOut = React.useCallback(
|
||||
const onPressOut = useCallback(
|
||||
(e: GestureResponderEvent) => {
|
||||
setState(s => ({
|
||||
...s,
|
||||
@@ -179,7 +187,7 @@ export const Button = React.forwardRef<View, ButtonProps>(
|
||||
},
|
||||
[setState, onPressOutOuter],
|
||||
)
|
||||
const onHoverIn = React.useCallback(
|
||||
const onHoverIn = useCallback(
|
||||
(e: MouseEvent) => {
|
||||
setState(s => ({
|
||||
...s,
|
||||
@@ -189,7 +197,7 @@ export const Button = React.forwardRef<View, ButtonProps>(
|
||||
},
|
||||
[setState, onHoverInOuter],
|
||||
)
|
||||
const onHoverOut = React.useCallback(
|
||||
const onHoverOut = useCallback(
|
||||
(e: MouseEvent) => {
|
||||
setState(s => ({
|
||||
...s,
|
||||
@@ -199,7 +207,7 @@ export const Button = React.forwardRef<View, ButtonProps>(
|
||||
},
|
||||
[setState, onHoverOutOuter],
|
||||
)
|
||||
const onFocus = React.useCallback(
|
||||
const onFocus = useCallback(
|
||||
(e: NativeSyntheticEvent<TargetedEvent>) => {
|
||||
setState(s => ({
|
||||
...s,
|
||||
@@ -209,7 +217,7 @@ export const Button = React.forwardRef<View, ButtonProps>(
|
||||
},
|
||||
[setState, onFocusOuter],
|
||||
)
|
||||
const onBlur = React.useCallback(
|
||||
const onBlur = useCallback(
|
||||
(e: NativeSyntheticEvent<TargetedEvent>) => {
|
||||
setState(s => ({
|
||||
...s,
|
||||
@@ -220,7 +228,7 @@ export const Button = React.forwardRef<View, ButtonProps>(
|
||||
[setState, onBlurOuter],
|
||||
)
|
||||
|
||||
const {baseStyles, hoverStyles} = React.useMemo(() => {
|
||||
const {baseStyles, hoverStyles} = useMemo(() => {
|
||||
const baseStyles: ViewStyle[] = []
|
||||
const hoverStyles: ViewStyle[] = []
|
||||
|
||||
@@ -526,7 +534,7 @@ export const Button = React.forwardRef<View, ButtonProps>(
|
||||
}
|
||||
}, [t, variant, color, size, shape, disabled])
|
||||
|
||||
const context = React.useMemo<ButtonContext>(
|
||||
const context = useMemo<ButtonContext>(
|
||||
() => ({
|
||||
...state,
|
||||
variant,
|
||||
@@ -581,7 +589,7 @@ Button.displayName = 'Button'
|
||||
export function useSharedButtonTextStyles() {
|
||||
const t = useTheme()
|
||||
const {color, variant, disabled, size} = useButtonContext()
|
||||
return React.useMemo(() => {
|
||||
return useMemo(() => {
|
||||
const baseStyles: TextStyle[] = []
|
||||
|
||||
/*
|
||||
@@ -769,7 +777,7 @@ export function ButtonIcon({
|
||||
icon: Comp,
|
||||
size,
|
||||
}: {
|
||||
icon: React.ComponentType<SVGIconProps>
|
||||
icon: ComponentType<SVGIconProps>
|
||||
/**
|
||||
* @deprecated no longer needed
|
||||
*/
|
||||
@@ -778,67 +786,66 @@ export function ButtonIcon({
|
||||
}) {
|
||||
const {size: buttonSize, shape: buttonShape} = useButtonContext()
|
||||
const textStyles = useSharedButtonTextStyles()
|
||||
const {iconSize, iconContainerSize, iconNegativeMargin} =
|
||||
React.useMemo(() => {
|
||||
/**
|
||||
* Pre-set icon sizes for different button sizes
|
||||
*/
|
||||
const iconSizeShorthand =
|
||||
size ??
|
||||
(({
|
||||
large: 'md',
|
||||
small: 'sm',
|
||||
tiny: 'xs',
|
||||
}[buttonSize || 'small'] || 'sm') as Exclude<
|
||||
SVGIconProps['size'],
|
||||
undefined
|
||||
>)
|
||||
const {iconSize, iconContainerSize, iconNegativeMargin} = useMemo(() => {
|
||||
/**
|
||||
* Pre-set icon sizes for different button sizes
|
||||
*/
|
||||
const iconSizeShorthand =
|
||||
size ??
|
||||
(({
|
||||
large: 'md',
|
||||
small: 'sm',
|
||||
tiny: 'xs',
|
||||
}[buttonSize || 'small'] || 'sm') as Exclude<
|
||||
SVGIconProps['size'],
|
||||
undefined
|
||||
>)
|
||||
|
||||
/*
|
||||
* Copied here from icons/common.tsx so we can tweak if we need to, but
|
||||
* also so that we can calculate transforms.
|
||||
*/
|
||||
const iconSize = {
|
||||
xs: 12,
|
||||
sm: 16,
|
||||
md: 18,
|
||||
lg: 24,
|
||||
xl: 28,
|
||||
'2xs': 8,
|
||||
'2xl': 32,
|
||||
'3xl': 40,
|
||||
}[iconSizeShorthand]
|
||||
/*
|
||||
* Copied here from icons/common.tsx so we can tweak if we need to, but
|
||||
* also so that we can calculate transforms.
|
||||
*/
|
||||
const iconSize = {
|
||||
xs: 12,
|
||||
sm: 16,
|
||||
md: 18,
|
||||
lg: 24,
|
||||
xl: 28,
|
||||
'2xs': 8,
|
||||
'2xl': 32,
|
||||
'3xl': 40,
|
||||
}[iconSizeShorthand]
|
||||
|
||||
/*
|
||||
* Goal here is to match rendered text size so that different size icons
|
||||
* don't increase button size
|
||||
*/
|
||||
const iconContainerSize = {
|
||||
large: 20,
|
||||
small: 17,
|
||||
tiny: 15,
|
||||
/*
|
||||
* Goal here is to match rendered text size so that different size icons
|
||||
* don't increase button size
|
||||
*/
|
||||
const iconContainerSize = {
|
||||
large: 20,
|
||||
small: 17,
|
||||
tiny: 15,
|
||||
}[buttonSize || 'small']
|
||||
|
||||
/*
|
||||
* The icon needs to be closer to the edge of the button than the text. Therefore
|
||||
* we make the gap slightly too large, and then pull in the sides using negative margins.
|
||||
*/
|
||||
let iconNegativeMargin = 0
|
||||
|
||||
if (buttonShape === 'default') {
|
||||
iconNegativeMargin = {
|
||||
large: -2,
|
||||
small: -2,
|
||||
tiny: -1,
|
||||
}[buttonSize || 'small']
|
||||
}
|
||||
|
||||
/*
|
||||
* The icon needs to be closer to the edge of the button than the text. Therefore
|
||||
* we make the gap slightly too large, and then pull in the sides using negative margins.
|
||||
*/
|
||||
let iconNegativeMargin = 0
|
||||
|
||||
if (buttonShape === 'default') {
|
||||
iconNegativeMargin = {
|
||||
large: -2,
|
||||
small: -2,
|
||||
tiny: -1,
|
||||
}[buttonSize || 'small']
|
||||
}
|
||||
|
||||
return {
|
||||
iconSize,
|
||||
iconContainerSize,
|
||||
iconNegativeMargin,
|
||||
}
|
||||
}, [buttonSize, buttonShape, size])
|
||||
return {
|
||||
iconSize,
|
||||
iconContainerSize,
|
||||
iconNegativeMargin,
|
||||
}
|
||||
}, [buttonSize, buttonShape, size])
|
||||
|
||||
return (
|
||||
<View
|
||||
@@ -888,8 +895,8 @@ export type StackedButtonProps = Omit<
|
||||
keyof VariantProps | 'children'
|
||||
> &
|
||||
Pick<VariantProps, 'color'> & {
|
||||
children: React.ReactNode
|
||||
icon: React.ComponentType<SVGIconProps>
|
||||
children: ReactNode
|
||||
icon: ComponentType<SVGIconProps>
|
||||
}
|
||||
|
||||
export function StackedButton({children, ...props}: StackedButtonProps) {
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import React, {
|
||||
import {
|
||||
cloneElement,
|
||||
Fragment,
|
||||
isValidElement,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useId,
|
||||
@@ -6,6 +9,7 @@ import React, {
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import {type ReactNode} from 'react'
|
||||
import {
|
||||
BackHandler,
|
||||
Keyboard,
|
||||
@@ -99,7 +103,7 @@ const SPRING_OUT: WithSpringConfig = {
|
||||
/**
|
||||
* Needs placing near the top of the provider stack, but BELOW the theme provider.
|
||||
*/
|
||||
export function Provider({children}: {children: React.ReactNode}) {
|
||||
export function Provider({children}: {children: ReactNode}) {
|
||||
return (
|
||||
<PortalProvider>
|
||||
{children}
|
||||
@@ -108,7 +112,7 @@ export function Provider({children}: {children: React.ReactNode}) {
|
||||
)
|
||||
}
|
||||
|
||||
export function Root({children}: {children: React.ReactNode}) {
|
||||
export function Root({children}: {children: ReactNode}) {
|
||||
const playHaptic = useHaptics()
|
||||
const [mode, setMode] = useState<'full' | 'auxiliary-only'>('full')
|
||||
const [measurement, setMeasurement] = useState<Measurement | null>(null)
|
||||
@@ -569,7 +573,7 @@ export function Outer({
|
||||
style,
|
||||
align = 'left',
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
children: ReactNode
|
||||
style?: StyleProp<ViewStyle>
|
||||
align?: 'left' | 'right'
|
||||
}) {
|
||||
@@ -689,22 +693,22 @@ export function Outer({
|
||||
t.atoms.border_contrast_low,
|
||||
]}>
|
||||
{flattenReactChildren(children).map((child, i) => {
|
||||
return React.isValidElement(child) &&
|
||||
return isValidElement(child) &&
|
||||
(child.type === Item || child.type === Divider) ? (
|
||||
<React.Fragment key={i}>
|
||||
<Fragment key={i}>
|
||||
{i > 0 ? (
|
||||
<View
|
||||
style={[a.border_b, t.atoms.border_contrast_low]}
|
||||
/>
|
||||
) : null}
|
||||
{React.cloneElement(child, {
|
||||
{cloneElement(child, {
|
||||
// @ts-expect-error not typed
|
||||
style: {
|
||||
borderRadius: 0,
|
||||
borderWidth: 0,
|
||||
},
|
||||
})}
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
) : null
|
||||
})}
|
||||
</View>
|
||||
@@ -892,7 +896,7 @@ export function ItemRadio({selected}: {selected: boolean}) {
|
||||
)
|
||||
}
|
||||
|
||||
export function LabelText({children}: {children: React.ReactNode}) {
|
||||
export function LabelText({children}: {children: ReactNode}) {
|
||||
const t = useTheme()
|
||||
return (
|
||||
<Text
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import React, {useImperativeHandle} from 'react'
|
||||
import {
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useContext,
|
||||
useImperativeHandle,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import {type PropsWithChildren, type ReactNode} from 'react'
|
||||
import {
|
||||
FlatList,
|
||||
type FlatListProps,
|
||||
@@ -45,18 +53,18 @@ export function Outer({
|
||||
control,
|
||||
onClose,
|
||||
webOptions,
|
||||
}: React.PropsWithChildren<DialogOuterProps>) {
|
||||
}: PropsWithChildren<DialogOuterProps>) {
|
||||
const {_} = useLingui()
|
||||
const {gtMobile} = useBreakpoints()
|
||||
const [isOpen, setIsOpen] = React.useState(false)
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const {setDialogIsOpen} = useDialogStateControlContext()
|
||||
|
||||
const open = React.useCallback(() => {
|
||||
const open = useCallback(() => {
|
||||
setDialogIsOpen(control.id, true)
|
||||
setIsOpen(true)
|
||||
}, [setIsOpen, setDialogIsOpen, control.id])
|
||||
|
||||
const close = React.useCallback<DialogControlProps['close']>(
|
||||
const close = useCallback<DialogControlProps['close']>(
|
||||
cb => {
|
||||
setDialogIsOpen(control.id, false)
|
||||
setIsOpen(false)
|
||||
@@ -80,7 +88,7 @@ export function Outer({
|
||||
[control.id, onClose, setDialogIsOpen],
|
||||
)
|
||||
|
||||
const handleBackgroundPress = React.useCallback(
|
||||
const handleBackgroundPress = useCallback(
|
||||
async (e: GestureResponderEvent) => {
|
||||
webOptions?.onBackgroundPress ? webOptions.onBackgroundPress(e) : close()
|
||||
},
|
||||
@@ -96,7 +104,7 @@ export function Outer({
|
||||
[close, open],
|
||||
)
|
||||
|
||||
const context = React.useMemo(
|
||||
const context = useMemo(
|
||||
() => ({
|
||||
close,
|
||||
isNativeDialog: false,
|
||||
@@ -165,7 +173,7 @@ export function Inner({
|
||||
contentContainerStyle,
|
||||
}: DialogInnerProps) {
|
||||
const t = useTheme()
|
||||
const {close} = React.useContext(Context)
|
||||
const {close} = useContext(Context)
|
||||
const {gtMobile} = useBreakpoints()
|
||||
const {reduceMotionEnabled} = useA11y()
|
||||
FocusGuards.useFocusGuards()
|
||||
@@ -215,7 +223,7 @@ export function Inner({
|
||||
|
||||
export const ScrollableInner = Inner
|
||||
|
||||
export const InnerFlatList = React.forwardRef<
|
||||
export const InnerFlatList = forwardRef<
|
||||
FlatList,
|
||||
FlatListProps<any> & {label: string} & {
|
||||
webInnerStyle?: StyleProp<ViewStyle>
|
||||
@@ -258,7 +266,7 @@ export function FlatListFooter({
|
||||
children,
|
||||
onLayout,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
children: ReactNode
|
||||
onLayout?: (event: LayoutChangeEvent) => void
|
||||
}) {
|
||||
const t = useTheme()
|
||||
@@ -284,7 +292,7 @@ export function FlatListFooter({
|
||||
|
||||
export function Close() {
|
||||
const {_} = useLingui()
|
||||
const {close} = React.useContext(Context)
|
||||
const {close} = useContext(Context)
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react'
|
||||
import {useEffect} from 'react'
|
||||
|
||||
import {type DialogControlProps} from '#/components/Dialog/types'
|
||||
|
||||
export function useAutoOpen(control: DialogControlProps, showTimeout?: number) {
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (showTimeout) {
|
||||
const timeout = setTimeout(() => {
|
||||
control.open()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useRef} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {
|
||||
type AppBskyActorDefs,
|
||||
@@ -46,9 +46,7 @@ export function KnownFollowers({
|
||||
minimal?: boolean
|
||||
showIfEmpty?: boolean
|
||||
}) {
|
||||
const cache = React.useRef<Map<string, AppBskyActorDefs.KnownFollowers>>(
|
||||
new Map(),
|
||||
)
|
||||
const cache = useRef<Map<string, AppBskyActorDefs.KnownFollowers>>(new Map())
|
||||
|
||||
/*
|
||||
* Results for `knownFollowers` are not sorted consistently, so when
|
||||
@@ -190,7 +188,7 @@ function KnownFollowersInner({
|
||||
numberOfLines={2}>
|
||||
{slice.length >= 2 ? (
|
||||
// 2-n followers, including blocks
|
||||
serverCount > 2 ? (
|
||||
serverCount > 2 ? ( // only 2
|
||||
<Trans>
|
||||
Followed by{' '}
|
||||
<Text emoji key={slice[0].profile.did} style={textStyle}>
|
||||
@@ -206,7 +204,7 @@ function KnownFollowersInner({
|
||||
one="# other"
|
||||
other="# others"
|
||||
/>
|
||||
</Trans> // only 2
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans>
|
||||
Followed by{' '}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useCallback} from 'react'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
@@ -22,7 +22,7 @@ export function LanguageSelect({
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
|
||||
const handleOnChange = React.useCallback(
|
||||
const handleOnChange = useCallback(
|
||||
(value: string) => {
|
||||
if (!value) return
|
||||
onChange(sanitizeAppLanguageSetting(value))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
import {createContext} from 'react'
|
||||
|
||||
export const ScrollbarOffsetContext = React.createContext({
|
||||
export const ScrollbarOffsetContext = createContext({
|
||||
isWithinOffsetView: false,
|
||||
})
|
||||
ScrollbarOffsetContext.displayName = 'ScrollbarOffsetContext'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useCallback, useMemo, useState} from 'react'
|
||||
import {type AppBskyFeedGetLikes as GetLikes} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
@@ -29,7 +29,7 @@ function keyExtractor(item: GetLikes.Like) {
|
||||
export function LikedByList({uri}: {uri: string}) {
|
||||
const {_} = useLingui()
|
||||
const initialNumToRender = useInitialNumToRender()
|
||||
const [isPTRing, setIsPTRing] = React.useState(false)
|
||||
const [isPTRing, setIsPTRing] = useState(false)
|
||||
|
||||
const {
|
||||
data: resolvedUri,
|
||||
@@ -49,14 +49,14 @@ export function LikedByList({uri}: {uri: string}) {
|
||||
const error = resolveError || likedByError
|
||||
const isError = !!resolveError || !!likedByError
|
||||
|
||||
const likes = React.useMemo(() => {
|
||||
const likes = useMemo(() => {
|
||||
if (data?.pages) {
|
||||
return data.pages.flatMap(page => page.likes)
|
||||
}
|
||||
return []
|
||||
}, [data])
|
||||
|
||||
const onRefresh = React.useCallback(async () => {
|
||||
const onRefresh = useCallback(async () => {
|
||||
setIsPTRing(true)
|
||||
try {
|
||||
await refetch()
|
||||
@@ -66,7 +66,7 @@ export function LikedByList({uri}: {uri: string}) {
|
||||
setIsPTRing(false)
|
||||
}, [refetch, setIsPTRing])
|
||||
|
||||
const onEndReached = React.useCallback(async () => {
|
||||
const onEndReached = useCallback(async () => {
|
||||
if (isFetchingNextPage || !hasNextPage || isError) return
|
||||
try {
|
||||
await fetchNextPage()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, {useMemo} from 'react'
|
||||
import {useCallback, useMemo} from 'react'
|
||||
import {type PropsWithChildren} from 'react'
|
||||
import {type GestureResponderEvent, Linking} from 'react-native'
|
||||
import {sanitizeUrl} from '@braintree/sanitize-url'
|
||||
import {
|
||||
@@ -117,7 +118,7 @@ export function useLink({
|
||||
const {linkWarningDialogControl} = useGlobalDialogsControlContext()
|
||||
const openLink = useOpenLink()
|
||||
|
||||
const onPress = React.useCallback(
|
||||
const onPress = useCallback(
|
||||
(e: GestureResponderEvent) => {
|
||||
const exitEarlyIfFalse = outerOnPress?.(e)
|
||||
|
||||
@@ -217,7 +218,7 @@ export function useLink({
|
||||
],
|
||||
)
|
||||
|
||||
const handleLongPress = React.useCallback(() => {
|
||||
const handleLongPress = useCallback(() => {
|
||||
const requiresWarning = Boolean(
|
||||
!disableMismatchWarning &&
|
||||
displayText &&
|
||||
@@ -242,7 +243,7 @@ export function useLink({
|
||||
linkWarningDialogControl,
|
||||
])
|
||||
|
||||
const onLongPress = React.useCallback(
|
||||
const onLongPress = useCallback(
|
||||
(e: GestureResponderEvent) => {
|
||||
const exitEarlyIfFalse = outerOnLongPress?.(e)
|
||||
if (exitEarlyIfFalse === false) return
|
||||
@@ -318,7 +319,7 @@ export function Link({
|
||||
)
|
||||
}
|
||||
|
||||
export type InlineLinkProps = React.PropsWithChildren<
|
||||
export type InlineLinkProps = PropsWithChildren<
|
||||
BaseLinkProps &
|
||||
TextStyleProp &
|
||||
Pick<TextProps, 'selectable' | 'numberOfLines' | 'emoji'> &
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useEffect, useMemo} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {
|
||||
type AppBskyGraphDefs,
|
||||
@@ -88,11 +88,11 @@ export function Link({
|
||||
}: Props & Omit<LinkProps, 'to' | 'label'>) {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const href = React.useMemo(() => {
|
||||
const href = useMemo(() => {
|
||||
return createProfileListHref({list: view})
|
||||
}, [view])
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
precacheList(queryClient, view)
|
||||
}, [view, queryClient])
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useEffect} from 'react'
|
||||
import Animated, {
|
||||
Easing,
|
||||
useAnimatedStyle,
|
||||
@@ -20,7 +20,7 @@ export function Loader(props: Props) {
|
||||
transform: [{rotate: rotation.get() + 'deg'}],
|
||||
}))
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
rotation.set(() =>
|
||||
withRepeat(withTiming(360, {duration: 500, easing: Easing.linear}), -1),
|
||||
)
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import React from 'react'
|
||||
import {createContext, useContext} from 'react'
|
||||
|
||||
import {type ContextType, type ItemContextType} from '#/components/Menu/types'
|
||||
|
||||
export const Context = React.createContext<ContextType | null>(null)
|
||||
export const Context = createContext<ContextType | null>(null)
|
||||
Context.displayName = 'MenuContext'
|
||||
|
||||
export const ItemContext = React.createContext<ItemContextType | null>(null)
|
||||
export const ItemContext = createContext<ItemContextType | null>(null)
|
||||
ItemContext.displayName = 'MenuItemContext'
|
||||
|
||||
export function useMenuContext() {
|
||||
const context = React.useContext(Context)
|
||||
const context = useContext(Context)
|
||||
|
||||
if (!context) {
|
||||
throw new Error('useMenuContext must be used within a Context.Provider')
|
||||
@@ -19,7 +19,7 @@ export function useMenuContext() {
|
||||
}
|
||||
|
||||
export function useMenuItemContext() {
|
||||
const context = React.useContext(ItemContext)
|
||||
const context = useContext(ItemContext)
|
||||
|
||||
if (!context) {
|
||||
throw new Error('useMenuItemContext must be used within a Context.Provider')
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import {useMemo} from 'react'
|
||||
import {type ReactNode} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {BSKY_LABELER_DID, type ModerationCause} from '@atproto/api'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
@@ -30,9 +31,8 @@ export function Row({
|
||||
children,
|
||||
style,
|
||||
size = 'sm',
|
||||
}: {children: React.ReactNode | React.ReactNode[]} & CommonProps &
|
||||
ViewStyleProp) {
|
||||
const styles = React.useMemo(() => {
|
||||
}: {children: ReactNode | ReactNode[]} & CommonProps & ViewStyleProp) {
|
||||
const styles = useMemo(() => {
|
||||
switch (size) {
|
||||
case 'lg':
|
||||
return [{gap: 5}]
|
||||
@@ -67,7 +67,7 @@ export function Label({
|
||||
const isBlueskyLabel =
|
||||
desc.sourceType === 'labeler' && desc.sourceDid === BSKY_LABELER_DID
|
||||
|
||||
const {outer, avi, text} = React.useMemo(() => {
|
||||
const {outer, avi, text} = useMemo(() => {
|
||||
switch (size) {
|
||||
case 'lg': {
|
||||
return {
|
||||
@@ -154,7 +154,7 @@ export function Label({
|
||||
export function FollowsYou({size = 'sm'}: CommonProps) {
|
||||
const t = useTheme()
|
||||
|
||||
const variantStyles = React.useMemo(() => {
|
||||
const variantStyles = useMemo(() => {
|
||||
switch (size) {
|
||||
case 'sm':
|
||||
case 'lg':
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, {useCallback} from 'react'
|
||||
import {useCallback} from 'react'
|
||||
import * as React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {
|
||||
type AppBskyActorDefs,
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import React, {useImperativeHandle} from 'react'
|
||||
import {
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useImperativeHandle,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import {Pressable, useWindowDimensions, View} from 'react-native'
|
||||
import Animated, {
|
||||
Easing,
|
||||
@@ -28,25 +35,25 @@ export interface ProgressGuideToastProps {
|
||||
visibleDuration?: number // default 5s
|
||||
}
|
||||
|
||||
export const ProgressGuideToast = React.forwardRef<
|
||||
export const ProgressGuideToast = forwardRef<
|
||||
ProgressGuideToastRef,
|
||||
ProgressGuideToastProps
|
||||
>(function ProgressGuideToast({title, subtitle, visibleDuration}, ref) {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const insets = useSafeAreaInsets()
|
||||
const [isOpen, setIsOpen] = React.useState(false)
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const translateY = useSharedValue(0)
|
||||
const opacity = useSharedValue(0)
|
||||
const animatedCheckRef = React.useRef<AnimatedCheckRef | null>(null)
|
||||
const timeoutRef = React.useRef<NodeJS.Timeout | undefined>(undefined)
|
||||
const animatedCheckRef = useRef<AnimatedCheckRef | null>(null)
|
||||
const timeoutRef = useRef<NodeJS.Timeout | undefined>(undefined)
|
||||
const winDim = useWindowDimensions()
|
||||
|
||||
/**
|
||||
* Methods
|
||||
*/
|
||||
|
||||
const close = React.useCallback(() => {
|
||||
const close = useCallback(() => {
|
||||
// clear the timeout, in case this was called imperatively
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current)
|
||||
@@ -67,7 +74,7 @@ export const ProgressGuideToast = React.forwardRef<
|
||||
)
|
||||
}, [setIsOpen, opacity])
|
||||
|
||||
const open = React.useCallback(() => {
|
||||
const open = useCallback(() => {
|
||||
// set isOpen=true to render
|
||||
setIsOpen(true)
|
||||
|
||||
@@ -105,7 +112,7 @@ export const ProgressGuideToast = React.forwardRef<
|
||||
[open, close],
|
||||
)
|
||||
|
||||
const containerStyle = React.useMemo(() => {
|
||||
const containerStyle = useMemo(() => {
|
||||
let left = 10
|
||||
let right = 10
|
||||
if (IS_WEB && winDim.width > 400) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useMemo} from 'react'
|
||||
import {type StyleProp, Text as RNText, type TextStyle} from 'react-native'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
@@ -68,7 +68,7 @@ export function RichTextTag({
|
||||
/*
|
||||
* Mute word records that exactly match the tag in question.
|
||||
*/
|
||||
const removeableMuteWords = React.useMemo(() => {
|
||||
const removeableMuteWords = useMemo(() => {
|
||||
return (
|
||||
preferences?.moderationPrefs.mutedWords?.filter(word => {
|
||||
return word.value === tag
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useMemo} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {Image} from 'expo-image'
|
||||
import {AppBskyGraphStarterpack, AtUri} from '@atproto/api'
|
||||
@@ -115,7 +115,7 @@ export function useStarterPackLink({
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
const qc = useQueryClient()
|
||||
const {rkey, handleOrDid} = React.useMemo(() => {
|
||||
const {rkey, handleOrDid} = useMemo(() => {
|
||||
const rkey = new AtUri(view.uri).rkey
|
||||
const {creator} = view
|
||||
return {rkey, handleOrDid: creator.handle || creator.did}
|
||||
@@ -148,7 +148,7 @@ export function Link({
|
||||
const {_} = useLingui()
|
||||
const queryClient = useQueryClient()
|
||||
const {record} = starterPack
|
||||
const {rkey, handleOrDid} = React.useMemo(() => {
|
||||
const {rkey, handleOrDid} = useMemo(() => {
|
||||
const rkey = new AtUri(starterPack.uri).rkey
|
||||
const {creator} = starterPack
|
||||
return {rkey, handleOrDid: creator.handle || creator.did}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import {isValidElement} from 'react'
|
||||
import {type ReactNode} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {nanoid} from 'nanoid/non-secure'
|
||||
import {toast as sonner, Toaster} from 'sonner-native'
|
||||
@@ -25,7 +26,7 @@ export function ToastOutlet() {
|
||||
return <Toaster pauseWhenPageIsHidden gap={a.gap_sm.gap} />
|
||||
}
|
||||
|
||||
export function Outer({children}: {children: React.ReactNode}) {
|
||||
export function Outer({children}: {children: ReactNode}) {
|
||||
return (
|
||||
<View style={[a.px_xl, a.w_full]}>
|
||||
<BaseOuter>{children}</BaseOuter>
|
||||
@@ -42,7 +43,7 @@ export const api = sonner
|
||||
* Our base toast API, using the `Toast` export of this file.
|
||||
*/
|
||||
export function show(
|
||||
content: React.ReactNode,
|
||||
content: ReactNode,
|
||||
{type = 'default', ...options}: BaseToastOptions = {},
|
||||
) {
|
||||
const id = nanoid()
|
||||
@@ -61,7 +62,7 @@ export function show(
|
||||
duration: options?.duration ?? DURATION,
|
||||
},
|
||||
)
|
||||
} else if (React.isValidElement(content)) {
|
||||
} else if (isValidElement(content)) {
|
||||
sonner.custom(
|
||||
<ToastConfigProvider id={id} type={type}>
|
||||
{content}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import {isValidElement} from 'react'
|
||||
import {type ReactNode} from 'react'
|
||||
import {nanoid} from 'nanoid/non-secure'
|
||||
import {toast as sonner, Toaster} from 'sonner'
|
||||
|
||||
@@ -40,7 +41,7 @@ export const api = sonner
|
||||
* Our base toast API, using the `Toast` export of this file.
|
||||
*/
|
||||
export function show(
|
||||
content: React.ReactNode,
|
||||
content: ReactNode,
|
||||
{type = 'default', ...options}: BaseToastOptions = {},
|
||||
) {
|
||||
const id = nanoid()
|
||||
@@ -60,7 +61,7 @@ export function show(
|
||||
duration: options?.duration ?? DURATION,
|
||||
},
|
||||
)
|
||||
} else if (React.isValidElement(content)) {
|
||||
} else if (isValidElement(content)) {
|
||||
sonner(
|
||||
<ToastConfigProvider id={id} type={type}>
|
||||
{content}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useMemo} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type AtUri} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
@@ -170,7 +170,7 @@ type ParsedTrendingTopic =
|
||||
|
||||
export function useTopic(raw: TrendingTopic): ParsedTrendingTopic {
|
||||
const {_} = useLingui()
|
||||
return React.useMemo(() => {
|
||||
return useMemo(() => {
|
||||
const {topic: displayName, link} = raw
|
||||
|
||||
if (link.startsWith('/search')) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {ToolsOzoneReportDefs} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
@@ -44,7 +44,7 @@ function Inner({control}: {control: Dialog.DialogControlProps}) {
|
||||
const {gtPhone} = useBreakpoints()
|
||||
const agent = useAgent()
|
||||
|
||||
const [details, setDetails] = React.useState('')
|
||||
const [details, setDetails] = useState('')
|
||||
const isInvalid = details.length > 1000
|
||||
|
||||
const {mutate, isPending} = useMutation({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {forwardRef, useCallback, useEffect, useImperativeHandle} from 'react'
|
||||
import Animated, {
|
||||
Easing,
|
||||
useAnimatedProps,
|
||||
@@ -23,74 +23,73 @@ export interface AnimatedCheckProps extends Props {
|
||||
playOnMount?: boolean
|
||||
}
|
||||
|
||||
export const AnimatedCheck = React.forwardRef<
|
||||
AnimatedCheckRef,
|
||||
AnimatedCheckProps
|
||||
>(function AnimatedCheck({playOnMount, ...props}, ref) {
|
||||
const {fill, size, style, ...rest} = useCommonSVGProps(props)
|
||||
const circleAnim = useSharedValue(0)
|
||||
const checkAnim = useSharedValue(0)
|
||||
export const AnimatedCheck = forwardRef<AnimatedCheckRef, AnimatedCheckProps>(
|
||||
function AnimatedCheck({playOnMount, ...props}, ref) {
|
||||
const {fill, size, style, ...rest} = useCommonSVGProps(props)
|
||||
const circleAnim = useSharedValue(0)
|
||||
const checkAnim = useSharedValue(0)
|
||||
|
||||
const circleAnimatedProps = useAnimatedProps(() => ({
|
||||
strokeDashoffset: 166 - circleAnim.get() * 166,
|
||||
}))
|
||||
const checkAnimatedProps = useAnimatedProps(() => ({
|
||||
strokeDashoffset: 48 - 48 * checkAnim.get(),
|
||||
}))
|
||||
const circleAnimatedProps = useAnimatedProps(() => ({
|
||||
strokeDashoffset: 166 - circleAnim.get() * 166,
|
||||
}))
|
||||
const checkAnimatedProps = useAnimatedProps(() => ({
|
||||
strokeDashoffset: 48 - 48 * checkAnim.get(),
|
||||
}))
|
||||
|
||||
const play = React.useCallback(
|
||||
(cb?: () => void) => {
|
||||
circleAnim.set(0)
|
||||
checkAnim.set(0)
|
||||
const play = useCallback(
|
||||
(cb?: () => void) => {
|
||||
circleAnim.set(0)
|
||||
checkAnim.set(0)
|
||||
|
||||
circleAnim.set(() =>
|
||||
withTiming(1, {duration: 500, easing: Easing.linear}),
|
||||
)
|
||||
checkAnim.set(() =>
|
||||
withDelay(
|
||||
500,
|
||||
withTiming(1, {duration: 300, easing: Easing.linear}, cb),
|
||||
),
|
||||
)
|
||||
},
|
||||
[circleAnim, checkAnim],
|
||||
)
|
||||
circleAnim.set(() =>
|
||||
withTiming(1, {duration: 500, easing: Easing.linear}),
|
||||
)
|
||||
checkAnim.set(() =>
|
||||
withDelay(
|
||||
500,
|
||||
withTiming(1, {duration: 300, easing: Easing.linear}, cb),
|
||||
),
|
||||
)
|
||||
},
|
||||
[circleAnim, checkAnim],
|
||||
)
|
||||
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
play,
|
||||
}))
|
||||
useImperativeHandle(ref, () => ({
|
||||
play,
|
||||
}))
|
||||
|
||||
React.useEffect(() => {
|
||||
if (playOnMount) {
|
||||
play()
|
||||
}
|
||||
}, [play, playOnMount])
|
||||
useEffect(() => {
|
||||
if (playOnMount) {
|
||||
play()
|
||||
}
|
||||
}, [play, playOnMount])
|
||||
|
||||
return (
|
||||
<Svg
|
||||
fill="none"
|
||||
{...rest}
|
||||
viewBox="0 0 52 52"
|
||||
width={size}
|
||||
height={size}
|
||||
style={style}>
|
||||
<AnimatedCircle
|
||||
animatedProps={circleAnimatedProps}
|
||||
cx="26"
|
||||
cy="26"
|
||||
r="24"
|
||||
return (
|
||||
<Svg
|
||||
fill="none"
|
||||
stroke={fill}
|
||||
strokeWidth={4}
|
||||
strokeDasharray={166}
|
||||
/>
|
||||
<AnimatedPath
|
||||
animatedProps={checkAnimatedProps}
|
||||
stroke={fill}
|
||||
d={PATH}
|
||||
strokeWidth={4}
|
||||
strokeDasharray={48}
|
||||
/>
|
||||
</Svg>
|
||||
)
|
||||
})
|
||||
{...rest}
|
||||
viewBox="0 0 52 52"
|
||||
width={size}
|
||||
height={size}
|
||||
style={style}>
|
||||
<AnimatedCircle
|
||||
animatedProps={circleAnimatedProps}
|
||||
cx="26"
|
||||
cy="26"
|
||||
r="24"
|
||||
fill="none"
|
||||
stroke={fill}
|
||||
strokeWidth={4}
|
||||
strokeDasharray={166}
|
||||
/>
|
||||
<AnimatedPath
|
||||
animatedProps={checkAnimatedProps}
|
||||
stroke={fill}
|
||||
d={PATH}
|
||||
strokeWidth={4}
|
||||
strokeDasharray={48}
|
||||
/>
|
||||
</Svg>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
@@ -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'
|
||||
@@ -125,12 +125,10 @@ function BirthdayInner({
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
const cleanError = useCleanError()
|
||||
const [date, setDate] = React.useState(
|
||||
preferences.birthDate || getDateAgo(18),
|
||||
)
|
||||
const [date, setDate] = useState(preferences.birthDate || getDateAgo(18))
|
||||
const {isPending, error, mutateAsync: setBirthDate} = useBirthdateMutation()
|
||||
const hasChanged = date !== preferences.birthDate
|
||||
const errorMessage = React.useMemo(() => {
|
||||
const errorMessage = useMemo(() => {
|
||||
if (error) {
|
||||
const {raw, clean} = cleanError(error)
|
||||
return clean || raw || error.toString()
|
||||
@@ -141,7 +139,7 @@ function BirthdayInner({
|
||||
const isUnder13 = age < 13
|
||||
const isUnder18 = age >= 13 && age < 18
|
||||
|
||||
const onSave = React.useCallback(async () => {
|
||||
const onSave = useCallback(async () => {
|
||||
try {
|
||||
// skip if date is the same
|
||||
if (hasChanged) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import {useCallback, useState} from 'react'
|
||||
import {type PropsWithChildren} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type AppBskyActorDefs, sanitizeMutedWordValue} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
@@ -58,13 +59,13 @@ function MutedWordsInner() {
|
||||
error: preferencesError,
|
||||
} = usePreferencesQuery()
|
||||
const {isPending, mutateAsync: addMutedWord} = useUpsertMutedWordsMutation()
|
||||
const [field, setField] = React.useState('')
|
||||
const [targets, setTargets] = React.useState(['content'])
|
||||
const [error, setError] = React.useState('')
|
||||
const [durations, setDurations] = React.useState(['forever'])
|
||||
const [excludeFollowing, setExcludeFollowing] = React.useState(false)
|
||||
const [field, setField] = useState('')
|
||||
const [targets, setTargets] = useState(['content'])
|
||||
const [error, setError] = useState('')
|
||||
const [durations, setDurations] = useState(['forever'])
|
||||
const [excludeFollowing, setExcludeFollowing] = useState(false)
|
||||
|
||||
const submit = React.useCallback(async () => {
|
||||
const submit = useCallback(async () => {
|
||||
const sanitizedValue = sanitizeMutedWordValue(field)
|
||||
const surfaces = ['tag', targets.includes('content') && 'content'].filter(
|
||||
Boolean,
|
||||
@@ -431,7 +432,7 @@ function MutedWordRow({
|
||||
const isExpired = expiryDate && expiryDate < new Date()
|
||||
const formatDistance = useFormatDistance()
|
||||
|
||||
const remove = React.useCallback(async () => {
|
||||
const remove = useCallback(async () => {
|
||||
control.close()
|
||||
removeMutedWord(word)
|
||||
}, [removeMutedWord, word, control])
|
||||
@@ -624,7 +625,7 @@ function MutedWordRow({
|
||||
)
|
||||
}
|
||||
|
||||
function TargetToggle({children}: React.PropsWithChildren<{}>) {
|
||||
function TargetToggle({children}: PropsWithChildren<{}>) {
|
||||
const t = useTheme()
|
||||
const ctx = Toggle.useItemContext()
|
||||
const {gtMobile} = useBreakpoints()
|
||||
|
||||
@@ -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'
|
||||
@@ -32,12 +32,12 @@ function SigninDialogInner({}: {control: Dialog.DialogOuterProps['control']}) {
|
||||
const {requestSwitchToAccount} = useLoggedOutViewControls()
|
||||
const closeAllActiveElements = useCloseAllActiveElements()
|
||||
|
||||
const showSignIn = React.useCallback(() => {
|
||||
const showSignIn = useCallback(() => {
|
||||
closeAllActiveElements()
|
||||
requestSwitchToAccount({requestedAccount: 'none'})
|
||||
}, [requestSwitchToAccount, closeAllActiveElements])
|
||||
|
||||
const showCreateAccount = React.useCallback(() => {
|
||||
const showCreateAccount = useCallback(() => {
|
||||
closeAllActiveElements()
|
||||
requestSwitchToAccount({requestedAccount: 'new'})
|
||||
}, [requestSwitchToAccount, closeAllActiveElements])
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {Fragment} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type ModerationCause} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
@@ -25,7 +25,6 @@ export function BlockedByListDialog({
|
||||
return (
|
||||
<Prompt.Outer control={control} testID="blockedByListDialog">
|
||||
<Prompt.TitleText>{_(msg`User blocked by list`)}</Prompt.TitleText>
|
||||
|
||||
<View style={[a.gap_sm, a.pb_lg]}>
|
||||
<Text
|
||||
selectable
|
||||
@@ -39,7 +38,7 @@ export function BlockedByListDialog({
|
||||
{_(msg`Lists blocking this user:`)}{' '}
|
||||
{listBlocks.map((block, i) =>
|
||||
block.source.type === 'list' ? (
|
||||
<React.Fragment key={block.source.list.uri}>
|
||||
<Fragment key={block.source.list.uri}>
|
||||
{i === 0 ? null : ', '}
|
||||
<InlineLinkText
|
||||
label={block.source.list.name}
|
||||
@@ -47,16 +46,14 @@ export function BlockedByListDialog({
|
||||
style={[a.text_md, a.leading_snug]}>
|
||||
{block.source.list.name}
|
||||
</InlineLinkText>
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
) : null,
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<Prompt.Actions>
|
||||
<Prompt.Action cta={_(msg`I understand`)} onPress={() => {}} />
|
||||
</Prompt.Actions>
|
||||
|
||||
<Dialog.Close />
|
||||
</Prompt.Outer>
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useCallback, useMemo, useState} from 'react'
|
||||
import {Pressable, View} from 'react-native'
|
||||
import Animated, {
|
||||
runOnJS,
|
||||
@@ -24,11 +24,11 @@ export function ChatEmptyPill() {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const playHaptic = useHaptics()
|
||||
const [promptIndex, setPromptIndex] = React.useState(lastIndex)
|
||||
const [promptIndex, setPromptIndex] = useState(lastIndex)
|
||||
|
||||
const scale = useSharedValue(1)
|
||||
|
||||
const prompts = React.useMemo(() => {
|
||||
const prompts = useMemo(() => {
|
||||
return [
|
||||
_(msg`Say hello!`),
|
||||
_(msg`Share your favorite feed!`),
|
||||
@@ -40,17 +40,17 @@ export function ChatEmptyPill() {
|
||||
]
|
||||
}, [_])
|
||||
|
||||
const onPressIn = React.useCallback(() => {
|
||||
const onPressIn = useCallback(() => {
|
||||
if (IS_WEB) return
|
||||
scale.set(() => withTiming(1.075, {duration: 100}))
|
||||
}, [scale])
|
||||
|
||||
const onPressOut = React.useCallback(() => {
|
||||
const onPressOut = useCallback(() => {
|
||||
if (IS_WEB) return
|
||||
scale.set(() => withTiming(1, {duration: 100}))
|
||||
}, [scale])
|
||||
|
||||
const onPress = React.useCallback(() => {
|
||||
const onPress = useCallback(() => {
|
||||
runOnJS(playHaptic)()
|
||||
let randomPromptIndex = Math.floor(Math.random() * prompts.length)
|
||||
while (randomPromptIndex === lastIndex) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, {useCallback} from 'react'
|
||||
import {useCallback} from 'react'
|
||||
import * as React from 'react'
|
||||
import {Keyboard, View} from 'react-native'
|
||||
import {type ChatBskyConvoDefs, type ModerationCause} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import {memo} from 'react'
|
||||
import {type ReactNode} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
@@ -28,7 +29,7 @@ const longDateFormatterWithYear = new Intl.DateTimeFormat(undefined, {
|
||||
year: 'numeric',
|
||||
})
|
||||
|
||||
let DateDivider = ({date: dateStr}: {date: string}): React.ReactNode => {
|
||||
let DateDivider = ({date: dateStr}: {date: string}): ReactNode => {
|
||||
const {_} = useLingui()
|
||||
const t = useTheme()
|
||||
|
||||
@@ -78,5 +79,5 @@ let DateDivider = ({date: dateStr}: {date: string}): React.ReactNode => {
|
||||
</View>
|
||||
)
|
||||
}
|
||||
DateDivider = React.memo(DateDivider)
|
||||
DateDivider = memo(DateDivider)
|
||||
export {DateDivider}
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
import React from 'react'
|
||||
import {createContext, useContext} from 'react'
|
||||
import {type ReactNode} from 'react'
|
||||
|
||||
const MessageContext = React.createContext(false)
|
||||
const MessageContext = createContext(false)
|
||||
MessageContext.displayName = 'MessageContext'
|
||||
|
||||
export function MessageContextProvider({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
export function MessageContextProvider({children}: {children: ReactNode}) {
|
||||
return (
|
||||
<MessageContext.Provider value={true}>{children}</MessageContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useIsWithinMessage() {
|
||||
return React.useContext(MessageContext)
|
||||
return useContext(MessageContext)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, {useCallback, useMemo} from 'react'
|
||||
import {memo, useCallback, useMemo} from 'react'
|
||||
import {type ReactNode} from 'react'
|
||||
import {
|
||||
type GestureResponderEvent,
|
||||
type StyleProp,
|
||||
@@ -40,7 +41,7 @@ let MessageItem = ({
|
||||
item,
|
||||
}: {
|
||||
item: ConvoItem & {type: 'message' | 'pending-message'}
|
||||
}): React.ReactNode => {
|
||||
}): ReactNode => {
|
||||
const t = useTheme()
|
||||
const {currentAccount} = useSession()
|
||||
const {_} = useLingui()
|
||||
@@ -233,7 +234,7 @@ let MessageItem = ({
|
||||
</>
|
||||
)
|
||||
}
|
||||
MessageItem = React.memo(MessageItem)
|
||||
MessageItem = memo(MessageItem)
|
||||
export {MessageItem}
|
||||
|
||||
let MessageItemMetadata = ({
|
||||
@@ -242,7 +243,7 @@ let MessageItemMetadata = ({
|
||||
}: {
|
||||
item: ConvoItem & {type: 'message' | 'pending-message'}
|
||||
style: StyleProp<TextStyle>
|
||||
}): React.ReactNode => {
|
||||
}): ReactNode => {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const {message} = item
|
||||
@@ -328,5 +329,5 @@ let MessageItemMetadata = ({
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
MessageItemMetadata = React.memo(MessageItemMetadata)
|
||||
MessageItemMetadata = memo(MessageItemMetadata)
|
||||
export {MessageItemMetadata}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import React from 'react'
|
||||
import {memo} from 'react'
|
||||
import {type ReactNode} from 'react'
|
||||
import {useWindowDimensions, View} from 'react-native'
|
||||
import {type $Typed, type AppBskyEmbedRecord} from '@atproto/api'
|
||||
|
||||
import {atoms as a, native, tokens, useTheme, web} from '#/alf'
|
||||
import {PostEmbedViewContext} from '#/components/Post/Embed'
|
||||
import {Embed} from '#/components/Post/Embed'
|
||||
import {Embed, PostEmbedViewContext} from '#/components/Post/Embed'
|
||||
import {MessageContextProvider} from './MessageContext'
|
||||
|
||||
let MessageItemEmbed = ({
|
||||
embed,
|
||||
}: {
|
||||
embed: $Typed<AppBskyEmbedRecord.View>
|
||||
}): React.ReactNode => {
|
||||
}): ReactNode => {
|
||||
const t = useTheme()
|
||||
const screen = useWindowDimensions()
|
||||
|
||||
@@ -43,5 +43,5 @@ let MessageItemEmbed = ({
|
||||
</MessageContextProvider>
|
||||
)
|
||||
}
|
||||
MessageItemEmbed = React.memo(MessageItemEmbed)
|
||||
MessageItemEmbed = memo(MessageItemEmbed)
|
||||
export {MessageItemEmbed}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useCallback} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type AppBskyActorDefs} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
@@ -39,7 +39,7 @@ export function MessageProfileButton({
|
||||
},
|
||||
})
|
||||
|
||||
const onPress = React.useCallback(() => {
|
||||
const onPress = useCallback(() => {
|
||||
if (!convoAvailability?.canChat) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useCallback, useMemo} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type ModerationDecision} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
@@ -38,7 +38,7 @@ export function MessagesListBlockedFooter({
|
||||
const reportControl = useDialogControl()
|
||||
const blockedByListControl = useDialogControl()
|
||||
|
||||
const {listBlocks, userBlock} = React.useMemo(() => {
|
||||
const {listBlocks, userBlock} = useMemo(() => {
|
||||
const modui = moderation.ui('profileView')
|
||||
const blocks = modui.alerts.filter(alert => alert.type === 'blocking')
|
||||
const listBlocks = blocks.filter(alert => alert.source.type === 'list')
|
||||
@@ -51,7 +51,7 @@ export function MessagesListBlockedFooter({
|
||||
|
||||
const isBlocking = !!userBlock || !!listBlocks.length
|
||||
|
||||
const onUnblockPress = React.useCallback(() => {
|
||||
const onUnblockPress = useCallback(() => {
|
||||
if (listBlocks.length) {
|
||||
blockedByListControl.open()
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useCallback} from 'react'
|
||||
import {Pressable, View} from 'react-native'
|
||||
import Animated, {
|
||||
runOnJS,
|
||||
@@ -33,17 +33,17 @@ export function NewMessagesPill({
|
||||
|
||||
const scale = useSharedValue(1)
|
||||
|
||||
const onPressIn = React.useCallback(() => {
|
||||
const onPressIn = useCallback(() => {
|
||||
if (IS_WEB) return
|
||||
scale.set(() => withTiming(1.075, {duration: 100}))
|
||||
}, [scale])
|
||||
|
||||
const onPressOut = React.useCallback(() => {
|
||||
const onPressOut = useCallback(() => {
|
||||
if (IS_WEB) return
|
||||
scale.set(() => withTiming(1, {duration: 100}))
|
||||
}, [scale])
|
||||
|
||||
const onPress = React.useCallback(() => {
|
||||
const onPress = useCallback(() => {
|
||||
runOnJS(playHaptic)()
|
||||
onPressInner?.()
|
||||
}, [onPressInner, playHaptic])
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useCallback} from 'react'
|
||||
import {Keyboard, View} from 'react-native'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
@@ -28,7 +28,7 @@ export function HostingProvider({
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
|
||||
const onPressSelectService = React.useCallback(() => {
|
||||
const onPressSelectService = useCallback(() => {
|
||||
Keyboard.dismiss()
|
||||
serverInputControl.open()
|
||||
onOpenDialog?.()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import {Children, cloneElement, Fragment, isValidElement} from 'react'
|
||||
import {type PropsWithChildren} from 'react'
|
||||
import {View} from 'react-native'
|
||||
|
||||
import {atoms, useTheme} from '#/alf'
|
||||
@@ -6,21 +7,21 @@ import {atoms, useTheme} from '#/alf'
|
||||
/**
|
||||
* NOT FINISHED, just here as a reference
|
||||
*/
|
||||
export function InputGroup(props: React.PropsWithChildren<{}>) {
|
||||
export function InputGroup(props: PropsWithChildren<{}>) {
|
||||
const t = useTheme()
|
||||
const children = React.Children.toArray(props.children)
|
||||
const children = Children.toArray(props.children)
|
||||
const total = children.length
|
||||
return (
|
||||
<View style={[atoms.w_full]}>
|
||||
{children.map((child, i) => {
|
||||
return React.isValidElement(child) ? (
|
||||
<React.Fragment key={i}>
|
||||
return isValidElement(child) ? (
|
||||
<Fragment key={i}>
|
||||
{i > 0 ? (
|
||||
<View
|
||||
style={[atoms.border_b, {borderColor: t.palette.contrast_500}]}
|
||||
/>
|
||||
) : null}
|
||||
{React.cloneElement(child, {
|
||||
{cloneElement(child, {
|
||||
// @ts-ignore
|
||||
style: [
|
||||
// @ts-ignore
|
||||
@@ -38,7 +39,7 @@ export function InputGroup(props: React.PropsWithChildren<{}>) {
|
||||
},
|
||||
],
|
||||
})}
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
) : null
|
||||
})}
|
||||
</View>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react'
|
||||
import {useEffect, useState} from 'react'
|
||||
|
||||
export function useDelayedLoading(delay: number, initialState: boolean = true) {
|
||||
const [isLoading, setIsLoading] = React.useState(initialState)
|
||||
const [isLoading, setIsLoading] = useState(initialState)
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
let timeout: NodeJS.Timeout
|
||||
// on initial load, show a loading spinner for a hot sec to prevent flash
|
||||
if (isLoading) timeout = setTimeout(() => setIsLoading(false), delay)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useCallback} from 'react'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
@@ -25,7 +25,7 @@ export function useFollowMethods({
|
||||
logContext,
|
||||
)
|
||||
|
||||
const follow = React.useCallback(() => {
|
||||
const follow = useCallback(() => {
|
||||
requireAuth(async () => {
|
||||
try {
|
||||
await queueFollow()
|
||||
@@ -38,7 +38,7 @@ export function useFollowMethods({
|
||||
})
|
||||
}, [_, queueFollow, requireAuth])
|
||||
|
||||
const unfollow = React.useCallback(() => {
|
||||
const unfollow = useCallback(() => {
|
||||
requireAuth(async () => {
|
||||
try {
|
||||
await queueUnfollow()
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import React from 'react'
|
||||
import {useCallback, useMemo, useState} from 'react'
|
||||
|
||||
export function useInteractionState() {
|
||||
const [state, setState] = React.useState(false)
|
||||
const [state, setState] = useState(false)
|
||||
|
||||
const onIn = React.useCallback(() => {
|
||||
const onIn = useCallback(() => {
|
||||
setState(true)
|
||||
}, [])
|
||||
const onOut = React.useCallback(() => {
|
||||
const onOut = useCallback(() => {
|
||||
setState(false)
|
||||
}, [])
|
||||
|
||||
return React.useMemo(
|
||||
return useMemo(
|
||||
() => ({
|
||||
state,
|
||||
onIn,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from 'react'
|
||||
import {useEffect, useState} from 'react'
|
||||
import {RichText as RichTextAPI} from '@atproto/api'
|
||||
|
||||
import {useAgent} from '#/state/session'
|
||||
|
||||
export function useRichText(text: string): [RichTextAPI, boolean] {
|
||||
const [prevText, setPrevText] = React.useState(text)
|
||||
const [rawRT, setRawRT] = React.useState(() => new RichTextAPI({text}))
|
||||
const [resolvedRT, setResolvedRT] = React.useState<RichTextAPI | null>(null)
|
||||
const [prevText, setPrevText] = useState(text)
|
||||
const [rawRT, setRawRT] = useState(() => new RichTextAPI({text}))
|
||||
const [resolvedRT, setResolvedRT] = useState<RichTextAPI | null>(null)
|
||||
const agent = useAgent()
|
||||
if (text !== prevText) {
|
||||
setPrevText(text)
|
||||
@@ -14,7 +14,7 @@ export function useRichText(text: string): [RichTextAPI, boolean] {
|
||||
setResolvedRT(null)
|
||||
// This will queue an immediate re-render
|
||||
}
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
let ignore = false
|
||||
async function resolveRTFacets() {
|
||||
// new each time
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useEffect, useState} from 'react'
|
||||
|
||||
import {
|
||||
createStarterPackLinkFromAndroidReferrer,
|
||||
@@ -10,11 +10,11 @@ import {IS_ANDROID} from '#/env'
|
||||
import {Referrer, SharedPrefs} from '../../../modules/expo-bluesky-swiss-army'
|
||||
|
||||
export function useStarterPackEntry() {
|
||||
const [ready, setReady] = React.useState(false)
|
||||
const [ready, setReady] = useState(false)
|
||||
const setActiveStarterPack = useSetActiveStarterPack()
|
||||
const hasCheckedForStarterPack = useHasCheckedForStarterPack()
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (ready) return
|
||||
|
||||
// On Android, we cannot clear the referral link. It gets stored for 90 days and all we can do is query for it. So,
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import React from 'react'
|
||||
import {useEffect, useState} from 'react'
|
||||
|
||||
import {httpStarterPackUriToAtUri} from '#/lib/strings/starter-pack'
|
||||
import {useSetActiveStarterPack} from '#/state/shell/starter-pack'
|
||||
|
||||
export function useStarterPackEntry() {
|
||||
const [ready, setReady] = React.useState(false)
|
||||
const [ready, setReady] = useState(false)
|
||||
|
||||
const setActiveStarterPack = useSetActiveStarterPack()
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
const href = window.location.href
|
||||
const atUri = httpStarterPackUriToAtUri(href)
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react'
|
||||
import {forwardRef} from 'react'
|
||||
import Svg, {Path} from 'react-native-svg'
|
||||
|
||||
import {type Props, useCommonSVGProps} from '#/components/icons/common'
|
||||
|
||||
export const IconTemplate_Stroke2_Corner0_Rounded = React.forwardRef(
|
||||
export const IconTemplate_Stroke2_Corner0_Rounded = forwardRef(
|
||||
function LogoImpl(props: Props, ref) {
|
||||
const {fill, size, style, ...rest} = useCommonSVGProps(props)
|
||||
|
||||
@@ -41,7 +41,7 @@ export function createSinglePathSVG({
|
||||
strokeLinecap?: 'butt' | 'round' | 'square'
|
||||
strokeLinejoin?: 'miter' | 'round' | 'bevel'
|
||||
}) {
|
||||
return React.forwardRef<Svg, Props>(function LogoImpl(props, ref) {
|
||||
return forwardRef<Svg, Props>(function LogoImpl(props, ref) {
|
||||
const {fill, size, style, gradient, ...rest} = useCommonSVGProps(props)
|
||||
|
||||
const hasStroke = strokeWidth > 0
|
||||
@@ -72,7 +72,7 @@ export function createSinglePathSVG({
|
||||
}
|
||||
|
||||
export function createSinglePathSVG2({path}: {path: string}) {
|
||||
return React.forwardRef<Svg, Props>(function LogoImpl(props, ref) {
|
||||
return forwardRef<Svg, Props>(function LogoImpl(props, ref) {
|
||||
const {fill, size, style, gradient, ...rest} = useCommonSVGProps(props)
|
||||
|
||||
return (
|
||||
@@ -92,7 +92,7 @@ export function createSinglePathSVG2({path}: {path: string}) {
|
||||
}
|
||||
|
||||
export function createMultiPathSVG({paths}: {paths: string[]}) {
|
||||
return React.forwardRef<Svg, Props>(function LogoImpl(props, ref) {
|
||||
return forwardRef<Svg, Props>(function LogoImpl(props, ref) {
|
||||
const {fill, size, style, gradient, ...rest} = useCommonSVGProps(props)
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react'
|
||||
import {forwardRef} from 'react'
|
||||
import Svg, {Circle, Path} from 'react-native-svg'
|
||||
|
||||
import {type Props, useCommonSVGProps} from '#/components/icons/common'
|
||||
|
||||
export const VerifiedCheck = React.forwardRef<Svg, Props>(
|
||||
export const VerifiedCheck = forwardRef<Svg, Props>(
|
||||
function LogoImpl(props, ref) {
|
||||
const {fill, size, style, ...rest} = useCommonSVGProps(props)
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react'
|
||||
import {forwardRef} from 'react'
|
||||
import Svg, {Path} from 'react-native-svg'
|
||||
|
||||
import {type Props, useCommonSVGProps} from '#/components/icons/common'
|
||||
|
||||
export const VerifierCheck = React.forwardRef<Svg, Props>(
|
||||
export const VerifierCheck = forwardRef<Svg, Props>(
|
||||
function LogoImpl(props, ref) {
|
||||
const {fill, size, style, ...rest} = useCommonSVGProps(props)
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import {createContext, useContext, useMemo, useState} from 'react'
|
||||
import {type ReactNode} from 'react'
|
||||
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {type DialogControlProps} from '#/components/Dialog'
|
||||
@@ -10,17 +11,17 @@ interface Context {
|
||||
setVerifyEmailState: (state: {code: string} | undefined) => void
|
||||
}
|
||||
|
||||
const Context = React.createContext({} as Context)
|
||||
const Context = createContext({} as Context)
|
||||
Context.displayName = 'IntentDialogsContext'
|
||||
export const useIntentDialogs = () => React.useContext(Context)
|
||||
export const useIntentDialogs = () => useContext(Context)
|
||||
|
||||
export function Provider({children}: {children: React.ReactNode}) {
|
||||
export function Provider({children}: {children: ReactNode}) {
|
||||
const verifyEmailDialogControl = Dialog.useDialogControl()
|
||||
const [verifyEmailState, setVerifyEmailState] = React.useState<
|
||||
const [verifyEmailState, setVerifyEmailState] = useState<
|
||||
{code: string} | undefined
|
||||
>()
|
||||
|
||||
const value = React.useMemo(
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
verifyEmailDialogControl,
|
||||
verifyEmailState,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useCallback} from 'react'
|
||||
import {ScrollView, View} from 'react-native'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
@@ -36,7 +36,7 @@ export function Inner() {
|
||||
const {data: trending, error, isLoading} = useTrendingTopics()
|
||||
const noTopics = !isLoading && !error && !trending?.topics?.length
|
||||
|
||||
const onConfirmHide = React.useCallback(() => {
|
||||
const onConfirmHide = useCallback(() => {
|
||||
ax.metric('trendingTopics:hide', {context: 'interstitial'})
|
||||
setTrendingDisabled(true)
|
||||
}, [ax, setTrendingDisabled])
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import {createContext, useContext, useState} from 'react'
|
||||
import {type PropsWithChildren, type ReactNode} from 'react'
|
||||
import {type ModerationUI} from '@atproto/api'
|
||||
|
||||
import {
|
||||
@@ -21,24 +22,24 @@ type Context = {
|
||||
}
|
||||
}
|
||||
|
||||
const Context = React.createContext<Context>({} as Context)
|
||||
const Context = createContext<Context>({} as Context)
|
||||
Context.displayName = 'HiderContext'
|
||||
|
||||
export const useHider = () => React.useContext(Context)
|
||||
export const useHider = () => useContext(Context)
|
||||
|
||||
export function Outer({
|
||||
modui,
|
||||
isContentVisibleInitialState,
|
||||
allowOverride,
|
||||
children,
|
||||
}: React.PropsWithChildren<{
|
||||
}: PropsWithChildren<{
|
||||
isContentVisibleInitialState?: boolean
|
||||
allowOverride?: boolean
|
||||
modui: ModerationUI | undefined
|
||||
}>) {
|
||||
const control = useModerationDetailsDialogControl()
|
||||
const blur = modui?.blurs[0]
|
||||
const [isContentVisible, setIsContentVisible] = React.useState(
|
||||
const [isContentVisible, setIsContentVisible] = useState(
|
||||
isContentVisibleInitialState || !blur,
|
||||
)
|
||||
const info = useModerationCauseDescription(blur)
|
||||
@@ -79,12 +80,12 @@ export function Outer({
|
||||
)
|
||||
}
|
||||
|
||||
export function Content({children}: {children: React.ReactNode}) {
|
||||
export function Content({children}: {children: ReactNode}) {
|
||||
const ctx = useHider()
|
||||
return ctx.isContentVisible ? children : null
|
||||
}
|
||||
|
||||
export function Mask({children}: {children: React.ReactNode}) {
|
||||
export function Mask({children}: {children: ReactNode}) {
|
||||
const ctx = useHider()
|
||||
return ctx.isContentVisible ? null : children
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, {useState} from 'react'
|
||||
import {useState} from 'react'
|
||||
import * as React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type ComAtprotoLabelDefs, ToolsOzoneReportDefs} from '@atproto/api'
|
||||
import {XRPCError} from '@atproto/xrpc'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import {useState} from 'react'
|
||||
import {type PropsWithChildren} from 'react'
|
||||
import {
|
||||
type StyleProp,
|
||||
TouchableWithoutFeedback,
|
||||
@@ -30,7 +31,7 @@ export function ScreenHider({
|
||||
style,
|
||||
containerStyle,
|
||||
children,
|
||||
}: React.PropsWithChildren<{
|
||||
}: PropsWithChildren<{
|
||||
testID?: string
|
||||
screenDescription: string
|
||||
modui: ModerationUI
|
||||
@@ -39,7 +40,7 @@ export function ScreenHider({
|
||||
}>) {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const [override, setOverride] = React.useState(false)
|
||||
const [override, setOverride] = useState(false)
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const control = useModerationDetailsDialogControl()
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useMemo} from 'react'
|
||||
|
||||
import {deviceLocales} from '#/locale/deviceLocales'
|
||||
import {useLanguagePrefs} from '#/state/preferences'
|
||||
@@ -277,7 +277,7 @@ export function useFormatCurrency(
|
||||
) {
|
||||
const geolocation = useGeolocation()
|
||||
const {appLanguage} = useLanguagePrefs()
|
||||
return React.useMemo(() => {
|
||||
return useMemo(() => {
|
||||
const locale = deviceLocales.at(0)
|
||||
const languageTag = locale?.languageTag || appLanguage || 'en-US'
|
||||
const countryCode = (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useEffect, useRef, useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import Animated, {
|
||||
Easing,
|
||||
@@ -105,14 +105,14 @@ export function CountWheel({
|
||||
// animation
|
||||
// The initial entering/exiting animations will get skipped, since these will happen on screen mounts and would
|
||||
// be unnecessary
|
||||
const [key, setKey] = React.useState(0)
|
||||
const [prevCount, setPrevCount] = React.useState(likeCount)
|
||||
const prevIsLiked = React.useRef(isLiked)
|
||||
const [key, setKey] = useState(0)
|
||||
const [prevCount, setPrevCount] = useState(likeCount)
|
||||
const prevIsLiked = useRef(isLiked)
|
||||
const formatPostStatCount = useFormatPostStatCount()
|
||||
const formattedCount = formatPostStatCount(likeCount)
|
||||
const formattedPrevCount = formatPostStatCount(prevCount)
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (isLiked === prevIsLiked.current) {
|
||||
return
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useCallback} from 'react'
|
||||
import * as Device from 'expo-device'
|
||||
import {impactAsync, ImpactFeedbackStyle} from 'expo-haptics'
|
||||
|
||||
@@ -8,7 +8,7 @@ import {IS_IOS, IS_WEB} from '#/env'
|
||||
export function useHaptics() {
|
||||
const isHapticsDisabled = useHapticsDisabled()
|
||||
|
||||
return React.useCallback(
|
||||
return useCallback(
|
||||
(strength: 'Light' | 'Medium' | 'Heavy' = 'Medium') => {
|
||||
if (isHapticsDisabled || IS_WEB) {
|
||||
return
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as React from 'react'
|
||||
import {useRef} from 'react'
|
||||
import {Animated} from 'react-native'
|
||||
|
||||
export function useAnimatedValue(initialValue: number) {
|
||||
const lazyRef = React.useRef<Animated.Value>(undefined)
|
||||
const lazyRef = useRef<Animated.Value>(undefined)
|
||||
|
||||
if (lazyRef.current === undefined) {
|
||||
lazyRef.current = new Animated.Value(initialValue)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useCallback, useEffect} from 'react'
|
||||
import {Alert} from 'react-native'
|
||||
import * as Linking from 'expo-linking'
|
||||
import * as WebBrowser from 'expo-web-browser'
|
||||
@@ -28,7 +28,7 @@ export function useIntentHandler() {
|
||||
const {currentAccount} = useSession()
|
||||
const {tryApplyUpdate} = useApplyPullRequestOTAUpdate()
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
const handleIncomingURL = async (url: string) => {
|
||||
if (IS_IOS) {
|
||||
// Close in-app browser if it's open (iOS only)
|
||||
@@ -109,7 +109,7 @@ export function useComposeIntent() {
|
||||
const {openComposer} = useOpenComposer()
|
||||
const {hasSession} = useSession()
|
||||
|
||||
return React.useCallback(
|
||||
return useCallback(
|
||||
({
|
||||
text,
|
||||
imageUrisStr,
|
||||
@@ -166,7 +166,7 @@ function useVerifyEmailIntent() {
|
||||
const closeAllActiveElements = useCloseAllActiveElements()
|
||||
const {verifyEmailDialogControl: control, setVerifyEmailState: setState} =
|
||||
useIntentDialogs()
|
||||
return React.useCallback(
|
||||
return useCallback(
|
||||
(code: string) => {
|
||||
closeAllActiveElements()
|
||||
setState({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import {useMemo} from 'react'
|
||||
import {
|
||||
type AppBskyLabelerDefs,
|
||||
BskyAgent,
|
||||
@@ -122,7 +122,7 @@ export type Subject =
|
||||
export function useLabelSubject({label}: {label: ComAtprotoLabelDefs.Label}): {
|
||||
subject: Subject
|
||||
} {
|
||||
return React.useMemo(() => {
|
||||
return useMemo(() => {
|
||||
const {cid, uri} = label
|
||||
if (cid) {
|
||||
return {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,4 +1,5 @@
|
||||
import React, {useCallback, useEffect} from 'react'
|
||||
import {useCallback, useEffect} from 'react'
|
||||
import * as React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {
|
||||
type AppBskyActorDefs,
|
||||
|
||||
@@ -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 {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]),
|
||||
)
|
||||
|
||||
@@ -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]),
|
||||
)
|
||||
|
||||
@@ -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} 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 {
|
||||
|
||||
@@ -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]),
|
||||
)
|
||||
|
||||
@@ -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]),
|
||||
)
|
||||
|
||||
@@ -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,5 @@
|
||||
import React, {useCallback} from 'react'
|
||||
import {createContext, useCallback, useContext} from 'react'
|
||||
import {type Dispatch} from 'react'
|
||||
import {LayoutAnimation} from 'react-native'
|
||||
import {
|
||||
ComAtprotoServerCreateAccount,
|
||||
@@ -248,11 +249,11 @@ export function reducer(s: SignupState, a: SignupAction): SignupState {
|
||||
|
||||
interface IContext {
|
||||
state: SignupState
|
||||
dispatch: React.Dispatch<SignupAction>
|
||||
dispatch: 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()
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
+11
-11
@@ -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])
|
||||
|
||||
+9
-8
@@ -1,26 +1,27 @@
|
||||
import React from 'react'
|
||||
import {createContext, useContext, useEffect, useMemo, useState} from 'react'
|
||||
import {type PropsWithChildren} from 'react'
|
||||
import {AccessibilityInfo} from 'react-native'
|
||||
|
||||
import {IS_WEB} from '#/env'
|
||||
import {PlatformInfo} from '../../modules/expo-bluesky-swiss-army'
|
||||
|
||||
const Context = React.createContext({
|
||||
const Context = createContext({
|
||||
reduceMotionEnabled: false,
|
||||
screenReaderEnabled: false,
|
||||
})
|
||||
Context.displayName = 'A11yContext'
|
||||
|
||||
export function useA11y() {
|
||||
return React.useContext(Context)
|
||||
return useContext(Context)
|
||||
}
|
||||
|
||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
const [reduceMotionEnabled, setReduceMotionEnabled] = React.useState(() =>
|
||||
export function Provider({children}: PropsWithChildren<{}>) {
|
||||
const [reduceMotionEnabled, setReduceMotionEnabled] = useState(() =>
|
||||
PlatformInfo.getIsReducedMotionEnabled(),
|
||||
)
|
||||
const [screenReaderEnabled, setScreenReaderEnabled] = React.useState(false)
|
||||
const [screenReaderEnabled, setScreenReaderEnabled] = useState(false)
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
const reduceMotionChangedSubscription = AccessibilityInfo.addEventListener(
|
||||
'reduceMotionChanged',
|
||||
enabled => {
|
||||
@@ -49,7 +50,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
}
|
||||
}, [])
|
||||
|
||||
const ctx = React.useMemo(() => {
|
||||
const ctx = useMemo(() => {
|
||||
return {
|
||||
reduceMotionEnabled,
|
||||
/**
|
||||
|
||||
Vendored
+16
-11
@@ -1,4 +1,11 @@
|
||||
import React, {useEffect} from 'react'
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react'
|
||||
import {type PropsWithChildren} from 'react'
|
||||
|
||||
import * as persisted from '#/state/persisted'
|
||||
import {useAgent, useSession} from '../session'
|
||||
@@ -6,17 +13,15 @@ import {useAgent, useSession} from '../session'
|
||||
type StateContext = Map<string, boolean>
|
||||
type SetStateContext = (uri: string, value: boolean) => void
|
||||
|
||||
const stateContext = React.createContext<StateContext>(new Map())
|
||||
const stateContext = createContext<StateContext>(new Map())
|
||||
stateContext.displayName = 'ThreadMutesStateContext'
|
||||
const setStateContext = React.createContext<SetStateContext>(
|
||||
(_: string) => false,
|
||||
)
|
||||
const setStateContext = createContext<SetStateContext>((_: string) => false)
|
||||
setStateContext.displayName = 'ThreadMutesSetStateContext'
|
||||
|
||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
const [state, setState] = React.useState<StateContext>(() => new Map())
|
||||
export function Provider({children}: PropsWithChildren<{}>) {
|
||||
const [state, setState] = useState<StateContext>(() => new Map())
|
||||
|
||||
const setThreadMute = React.useCallback(
|
||||
const setThreadMute = useCallback(
|
||||
(uri: string, value: boolean) => {
|
||||
setState(prev => {
|
||||
const next = new Map(prev)
|
||||
@@ -39,16 +44,16 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
}
|
||||
|
||||
export function useMutedThreads() {
|
||||
return React.useContext(stateContext)
|
||||
return useContext(stateContext)
|
||||
}
|
||||
|
||||
export function useIsThreadMuted(uri: string, defaultValue = false) {
|
||||
const state = React.useContext(stateContext)
|
||||
const state = useContext(stateContext)
|
||||
return state.get(uri) ?? defaultValue
|
||||
}
|
||||
|
||||
export function useSetThreadMute() {
|
||||
return React.useContext(setStateContext)
|
||||
return useContext(setStateContext)
|
||||
}
|
||||
|
||||
function useMigrateMutes(setThreadMute: SetStateContext) {
|
||||
|
||||
+32
-19
@@ -1,4 +1,17 @@
|
||||
import React from 'react'
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import {
|
||||
type Dispatch,
|
||||
type MutableRefObject,
|
||||
type PropsWithChildren,
|
||||
type SetStateAction,
|
||||
} from 'react'
|
||||
|
||||
import {type DialogControlRefProps} from '#/components/Dialog'
|
||||
import {Provider as GlobalDialogsProvider} from '#/components/dialogs/Context'
|
||||
@@ -9,26 +22,26 @@ interface IDialogContext {
|
||||
/**
|
||||
* The currently active `useDialogControl` hooks.
|
||||
*/
|
||||
activeDialogs: React.MutableRefObject<
|
||||
Map<string, React.MutableRefObject<DialogControlRefProps>>
|
||||
activeDialogs: MutableRefObject<
|
||||
Map<string, MutableRefObject<DialogControlRefProps>>
|
||||
>
|
||||
/**
|
||||
* The currently open dialogs, referenced by their IDs, generated from
|
||||
* `useId`.
|
||||
*/
|
||||
openDialogs: React.MutableRefObject<Set<string>>
|
||||
openDialogs: MutableRefObject<Set<string>>
|
||||
}
|
||||
|
||||
interface IDialogControlContext {
|
||||
closeAllDialogs(): boolean
|
||||
setDialogIsOpen(id: string, isOpen: boolean): void
|
||||
setFullyExpandedCount: React.Dispatch<React.SetStateAction<number>>
|
||||
setFullyExpandedCount: Dispatch<SetStateAction<number>>
|
||||
}
|
||||
|
||||
const DialogContext = React.createContext<IDialogContext>({} as IDialogContext)
|
||||
const DialogContext = createContext<IDialogContext>({} as IDialogContext)
|
||||
DialogContext.displayName = 'DialogContext'
|
||||
|
||||
const DialogControlContext = React.createContext<IDialogControlContext>(
|
||||
const DialogControlContext = createContext<IDialogControlContext>(
|
||||
{} as IDialogControlContext,
|
||||
)
|
||||
DialogControlContext.displayName = 'DialogControlContext'
|
||||
@@ -37,31 +50,31 @@ DialogControlContext.displayName = 'DialogControlContext'
|
||||
* The number of dialogs that are fully expanded. This is used to determine the background color of the status bar
|
||||
* on iOS.
|
||||
*/
|
||||
const DialogFullyExpandedCountContext = React.createContext<number>(0)
|
||||
const DialogFullyExpandedCountContext = createContext<number>(0)
|
||||
DialogFullyExpandedCountContext.displayName = 'DialogFullyExpandedCountContext'
|
||||
|
||||
export function useDialogStateContext() {
|
||||
return React.useContext(DialogContext)
|
||||
return useContext(DialogContext)
|
||||
}
|
||||
|
||||
export function useDialogStateControlContext() {
|
||||
return React.useContext(DialogControlContext)
|
||||
return useContext(DialogControlContext)
|
||||
}
|
||||
|
||||
/** The number of dialogs that are fully expanded */
|
||||
export function useDialogFullyExpandedCountContext() {
|
||||
return React.useContext(DialogFullyExpandedCountContext)
|
||||
return useContext(DialogFullyExpandedCountContext)
|
||||
}
|
||||
|
||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
const [fullyExpandedCount, setFullyExpandedCount] = React.useState(0)
|
||||
export function Provider({children}: PropsWithChildren<{}>) {
|
||||
const [fullyExpandedCount, setFullyExpandedCount] = useState(0)
|
||||
|
||||
const activeDialogs = React.useRef<
|
||||
const activeDialogs = useRef<
|
||||
Map<string, React.MutableRefObject<DialogControlRefProps>>
|
||||
>(new Map())
|
||||
const openDialogs = React.useRef<Set<string>>(new Set())
|
||||
const openDialogs = useRef<Set<string>>(new Set())
|
||||
|
||||
const closeAllDialogs = React.useCallback(() => {
|
||||
const closeAllDialogs = useCallback(() => {
|
||||
if (IS_WEB) {
|
||||
openDialogs.current.forEach(id => {
|
||||
const dialog = activeDialogs.current.get(id)
|
||||
@@ -75,7 +88,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
}
|
||||
}, [])
|
||||
|
||||
const setDialogIsOpen = React.useCallback((id: string, isOpen: boolean) => {
|
||||
const setDialogIsOpen = useCallback((id: string, isOpen: boolean) => {
|
||||
if (isOpen) {
|
||||
openDialogs.current.add(id)
|
||||
} else {
|
||||
@@ -83,14 +96,14 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
}
|
||||
}, [])
|
||||
|
||||
const context = React.useMemo<IDialogContext>(
|
||||
const context = useMemo<IDialogContext>(
|
||||
() => ({
|
||||
activeDialogs,
|
||||
openDialogs,
|
||||
}),
|
||||
[activeDialogs, openDialogs],
|
||||
)
|
||||
const controls = React.useMemo(
|
||||
const controls = useMemo(
|
||||
() => ({
|
||||
closeAllDialogs,
|
||||
setDialogIsOpen,
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import React from 'react'
|
||||
import {createContext, useContext, useState} from 'react'
|
||||
import {type PropsWithChildren} from 'react'
|
||||
|
||||
type StateContext = boolean
|
||||
type ApiContext = (hasNew: boolean) => void
|
||||
|
||||
const stateContext = React.createContext<StateContext>(false)
|
||||
const stateContext = createContext<StateContext>(false)
|
||||
stateContext.displayName = 'HomeBadgeStateContext'
|
||||
const apiContext = React.createContext<ApiContext>((_: boolean) => {})
|
||||
const apiContext = createContext<ApiContext>((_: boolean) => {})
|
||||
apiContext.displayName = 'HomeBadgeApiContext'
|
||||
|
||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
const [state, setState] = React.useState(false)
|
||||
export function Provider({children}: PropsWithChildren<{}>) {
|
||||
const [state, setState] = useState(false)
|
||||
return (
|
||||
<stateContext.Provider value={state}>
|
||||
<apiContext.Provider value={setState}>{children}</apiContext.Provider>
|
||||
@@ -18,9 +19,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
}
|
||||
|
||||
export function useHomeBadge() {
|
||||
return React.useContext(stateContext)
|
||||
return useContext(stateContext)
|
||||
}
|
||||
|
||||
export function useSetHomeBadge() {
|
||||
return React.useContext(apiContext)
|
||||
return useContext(apiContext)
|
||||
}
|
||||
|
||||
+10
-11
@@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import {createContext, useContext, useMemo, useState} from 'react'
|
||||
import {type PropsWithChildren} from 'react'
|
||||
import {nanoid} from 'nanoid/non-secure'
|
||||
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
@@ -10,14 +11,14 @@ export type Lightbox = {
|
||||
index: number
|
||||
}
|
||||
|
||||
const LightboxContext = React.createContext<{
|
||||
const LightboxContext = createContext<{
|
||||
activeLightbox: Lightbox | null
|
||||
}>({
|
||||
activeLightbox: null,
|
||||
})
|
||||
LightboxContext.displayName = 'LightboxContext'
|
||||
|
||||
const LightboxControlContext = React.createContext<{
|
||||
const LightboxControlContext = createContext<{
|
||||
openLightbox: (lightbox: Omit<Lightbox, 'id'>) => void
|
||||
closeLightbox: () => boolean
|
||||
}>({
|
||||
@@ -26,10 +27,8 @@ const LightboxControlContext = React.createContext<{
|
||||
})
|
||||
LightboxControlContext.displayName = 'LightboxControlContext'
|
||||
|
||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
const [activeLightbox, setActiveLightbox] = React.useState<Lightbox | null>(
|
||||
null,
|
||||
)
|
||||
export function Provider({children}: PropsWithChildren<{}>) {
|
||||
const [activeLightbox, setActiveLightbox] = useState<Lightbox | null>(null)
|
||||
|
||||
const openLightbox = useNonReactiveCallback(
|
||||
(lightbox: Omit<Lightbox, 'id'>) => {
|
||||
@@ -51,14 +50,14 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
return wasActive
|
||||
})
|
||||
|
||||
const state = React.useMemo(
|
||||
const state = useMemo(
|
||||
() => ({
|
||||
activeLightbox,
|
||||
}),
|
||||
[activeLightbox],
|
||||
)
|
||||
|
||||
const methods = React.useMemo(
|
||||
const methods = useMemo(
|
||||
() => ({
|
||||
openLightbox,
|
||||
closeLightbox,
|
||||
@@ -76,9 +75,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
}
|
||||
|
||||
export function useLightbox() {
|
||||
return React.useContext(LightboxContext)
|
||||
return useContext(LightboxContext)
|
||||
}
|
||||
|
||||
export function useLightboxControls() {
|
||||
return React.useContext(LightboxControlContext)
|
||||
return useContext(LightboxControlContext)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react'
|
||||
import {createContext, useContext, useMemo, useState} from 'react'
|
||||
import {type ReactNode} from 'react'
|
||||
|
||||
const CurrentConvoIdContext = React.createContext<{
|
||||
const CurrentConvoIdContext = createContext<{
|
||||
currentConvoId: string | undefined
|
||||
setCurrentConvoId: (convoId: string | undefined) => void
|
||||
}>({
|
||||
@@ -10,7 +11,7 @@ const CurrentConvoIdContext = React.createContext<{
|
||||
CurrentConvoIdContext.displayName = 'CurrentConvoIdContext'
|
||||
|
||||
export function useCurrentConvoId() {
|
||||
const ctx = React.useContext(CurrentConvoIdContext)
|
||||
const ctx = useContext(CurrentConvoIdContext)
|
||||
if (!ctx) {
|
||||
throw new Error(
|
||||
'useCurrentConvoId must be used within a CurrentConvoIdProvider',
|
||||
@@ -19,15 +20,9 @@ export function useCurrentConvoId() {
|
||||
return ctx
|
||||
}
|
||||
|
||||
export function CurrentConvoIdProvider({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const [currentConvoId, setCurrentConvoId] = React.useState<
|
||||
string | undefined
|
||||
>()
|
||||
const ctx = React.useMemo(
|
||||
export function CurrentConvoIdProvider({children}: {children: ReactNode}) {
|
||||
const [currentConvoId, setCurrentConvoId] = useState<string | undefined>()
|
||||
const ctx = useMemo(
|
||||
() => ({currentConvoId, setCurrentConvoId}),
|
||||
[currentConvoId],
|
||||
)
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
import React, {useEffect, useMemo, useReducer, useRef} from 'react'
|
||||
import {
|
||||
createContext,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useReducer,
|
||||
useRef,
|
||||
} from 'react'
|
||||
import {type ReactNode} from 'react'
|
||||
|
||||
import {useCurrentConvoId} from './current-convo-id'
|
||||
|
||||
const MessageDraftsContext = React.createContext<{
|
||||
const MessageDraftsContext = createContext<{
|
||||
state: State
|
||||
dispatch: React.Dispatch<Actions>
|
||||
} | null>(null)
|
||||
MessageDraftsContext.displayName = 'MessageDraftsContext'
|
||||
|
||||
function useMessageDraftsContext() {
|
||||
const ctx = React.useContext(MessageDraftsContext)
|
||||
const ctx = useContext(MessageDraftsContext)
|
||||
if (!ctx) {
|
||||
throw new Error(
|
||||
'useMessageDrafts must be used within a MessageDraftsContext',
|
||||
@@ -69,7 +77,7 @@ function reducer(state: State, action: Actions): State {
|
||||
}
|
||||
}
|
||||
|
||||
export function MessageDraftsProvider({children}: {children: React.ReactNode}) {
|
||||
export function MessageDraftsProvider({children}: {children: ReactNode}) {
|
||||
const [state, dispatch] = useReducer(reducer, {})
|
||||
|
||||
const ctx = useMemo(() => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import {createContext, useContext, useMemo, useState} from 'react'
|
||||
import {type PropsWithChildren} from 'react'
|
||||
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
|
||||
@@ -25,7 +26,7 @@ export type Modal =
|
||||
// Lists
|
||||
| UserAddRemoveListsModal
|
||||
|
||||
const ModalContext = React.createContext<{
|
||||
const ModalContext = createContext<{
|
||||
isModalActive: boolean
|
||||
activeModals: Modal[]
|
||||
}>({
|
||||
@@ -34,7 +35,7 @@ const ModalContext = React.createContext<{
|
||||
})
|
||||
ModalContext.displayName = 'ModalContext'
|
||||
|
||||
const ModalControlContext = React.createContext<{
|
||||
const ModalControlContext = createContext<{
|
||||
openModal: (modal: Modal) => void
|
||||
closeModal: () => boolean
|
||||
closeAllModals: () => boolean
|
||||
@@ -45,8 +46,8 @@ const ModalControlContext = React.createContext<{
|
||||
})
|
||||
ModalControlContext.displayName = 'ModalControlContext'
|
||||
|
||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
const [activeModals, setActiveModals] = React.useState<Modal[]>([])
|
||||
export function Provider({children}: PropsWithChildren<{}>) {
|
||||
const [activeModals, setActiveModals] = useState<Modal[]>([])
|
||||
|
||||
const openModal = useNonReactiveCallback((modal: Modal) => {
|
||||
setActiveModals(modals => [...modals, modal])
|
||||
@@ -66,7 +67,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
return wasActive
|
||||
})
|
||||
|
||||
const state = React.useMemo(
|
||||
const state = useMemo(
|
||||
() => ({
|
||||
isModalActive: activeModals.length > 0,
|
||||
activeModals,
|
||||
@@ -74,7 +75,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
[activeModals],
|
||||
)
|
||||
|
||||
const methods = React.useMemo(
|
||||
const methods = useMemo(
|
||||
() => ({
|
||||
openModal,
|
||||
closeModal,
|
||||
@@ -96,12 +97,12 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
* @deprecated use the dialog system from `#/components/Dialog.tsx`
|
||||
*/
|
||||
export function useModals() {
|
||||
return React.useContext(ModalContext)
|
||||
return useContext(ModalContext)
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use the dialog system from `#/components/Dialog.tsx`
|
||||
*/
|
||||
export function useModalControls() {
|
||||
return React.useContext(ModalControlContext)
|
||||
return useContext(ModalControlContext)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user