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