run codemod on every file
This commit is contained in:
@@ -9,8 +9,7 @@ import {
|
|||||||
} from 'react'
|
} from 'react'
|
||||||
import {Dimensions, View} from 'react-native'
|
import {Dimensions, View} from 'react-native'
|
||||||
import * as Linking from 'expo-linking'
|
import * as Linking from 'expo-linking'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {retry} from '#/lib/async/retry'
|
import {retry} from '#/lib/async/retry'
|
||||||
@@ -132,7 +131,7 @@ export function Provider({children}: {children?: React.ReactNode}) {
|
|||||||
|
|
||||||
export function RedirectOverlay() {
|
export function RedirectOverlay() {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {isOpen} = useRedirectOverlayContext()
|
const {isOpen} = useRedirectOverlayContext()
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
|
|
||||||
@@ -162,7 +161,7 @@ export function RedirectOverlay() {
|
|||||||
<View
|
<View
|
||||||
role="dialog"
|
role="dialog"
|
||||||
aria-role="dialog"
|
aria-role="dialog"
|
||||||
aria-label={_(msg`Verifying your age assurance status`)}>
|
aria-label={l`Verifying your age assurance status`}>
|
||||||
<View style={[a.pb_3xl, {width: 300}]}>
|
<View style={[a.pb_3xl, {width: 300}]}>
|
||||||
<Inner />
|
<Inner />
|
||||||
</View>
|
</View>
|
||||||
@@ -175,7 +174,7 @@ export function RedirectOverlay() {
|
|||||||
function Inner() {
|
function Inner() {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
const polling = useRef(false)
|
const polling = useRef(false)
|
||||||
const unmounted = useRef(false)
|
const unmounted = useRef(false)
|
||||||
@@ -262,7 +261,7 @@ function Inner() {
|
|||||||
|
|
||||||
<View style={[a.w_full, a.pt_lg]}>
|
<View style={[a.w_full, a.pt_lg]}>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Close`)}
|
label={l`Close`}
|
||||||
size="large"
|
size="large"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
@@ -318,7 +317,7 @@ function Inner() {
|
|||||||
{error && (
|
{error && (
|
||||||
<View style={[a.w_full, a.pt_lg]}>
|
<View style={[a.w_full, a.pt_lg]}>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Close`)}
|
label={l`Close`}
|
||||||
size="large"
|
size="large"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ function createLogger(
|
|||||||
warn: logger.warn.bind(logger),
|
warn: logger.warn.bind(logger),
|
||||||
error: logger.error.bind(logger),
|
error: logger.error.bind(logger),
|
||||||
useChild: (context: Exclude<Logger['context'], undefined>) => {
|
useChild: (context: Exclude<Logger['context'], undefined>) => {
|
||||||
return useMemo(() => createLogger(context, metadata), [context, metadata])
|
return useMemo(() => createLogger(context, metadata), [context])
|
||||||
},
|
},
|
||||||
Context: Logger.Context,
|
Context: Logger.Context,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {Fragment, 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 {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {isJwtExpired} from '#/lib/jwt'
|
import {isJwtExpired} from '#/lib/jwt'
|
||||||
@@ -33,7 +32,7 @@ export function AccountList({
|
|||||||
}) {
|
}) {
|
||||||
const {currentAccount, accounts} = useSession()
|
const {currentAccount, accounts} = useSession()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {data: profiles} = useProfilesQuery({
|
const {data: profiles} = useProfilesQuery({
|
||||||
handles: accounts.map(acc => acc.did),
|
handles: accounts.map(acc => acc.did),
|
||||||
})
|
})
|
||||||
@@ -67,7 +66,7 @@ export function AccountList({
|
|||||||
testID="chooseAddAccountBtn"
|
testID="chooseAddAccountBtn"
|
||||||
style={[a.flex_1]}
|
style={[a.flex_1]}
|
||||||
onPress={pendingDid ? undefined : onPressAddAccount}
|
onPress={pendingDid ? undefined : onPressAddAccount}
|
||||||
label={_(msg`Sign in to account that is not listed`)}>
|
label={l`Sign in to account that is not listed`}>
|
||||||
{({hovered, pressed}) => (
|
{({hovered, pressed}) => (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
@@ -114,7 +113,7 @@ function AccountItem({
|
|||||||
isPendingAccount: boolean
|
isPendingAccount: boolean
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {isActive: live} = useActorStatus(profile)
|
const {isActive: live} = useActorStatus(profile)
|
||||||
|
|
||||||
const onPress = useCallback(() => {
|
const onPress = useCallback(() => {
|
||||||
@@ -131,8 +130,8 @@ function AccountItem({
|
|||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
label={
|
label={
|
||||||
isCurrentAccount
|
isCurrentAccount
|
||||||
? _(msg`Continue as ${account.handle} (currently signed in)`)
|
? l`Continue as ${account.handle} (currently signed in)`
|
||||||
: _(msg`Sign in as ${account.handle}`)
|
: l`Sign in as ${account.handle}`
|
||||||
}>
|
}>
|
||||||
{({hovered, pressed}) => (
|
{({hovered, pressed}) => (
|
||||||
<View
|
<View
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import {useCallback} from 'react'
|
import {useCallback} from 'react'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {useQueryClient} from '@tanstack/react-query'
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {sanitizeAppLanguageSetting} from '#/locale/helpers'
|
import {sanitizeAppLanguageSetting} from '#/locale/helpers'
|
||||||
@@ -14,7 +13,7 @@ import {Earth_Stroke2_Corner2_Rounded as EarthIcon} from './icons/Globe'
|
|||||||
|
|
||||||
export function AppLanguageDropdown() {
|
export function AppLanguageDropdown() {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const langPrefs = useLanguagePrefs()
|
const langPrefs = useLanguagePrefs()
|
||||||
@@ -38,7 +37,7 @@ export function AppLanguageDropdown() {
|
|||||||
<Select.Root
|
<Select.Root
|
||||||
value={sanitizeAppLanguageSetting(langPrefs.appLanguage)}
|
value={sanitizeAppLanguageSetting(langPrefs.appLanguage)}
|
||||||
onValueChange={onChangeAppLanguage}>
|
onValueChange={onChangeAppLanguage}>
|
||||||
<Select.Trigger label={_(msg`Change app language`)}>
|
<Select.Trigger label={l`Change app language`}>
|
||||||
{({props}) => (
|
{({props}) => (
|
||||||
<Button
|
<Button
|
||||||
{...props}
|
{...props}
|
||||||
@@ -60,7 +59,7 @@ export function AppLanguageDropdown() {
|
|||||||
]}>
|
]}>
|
||||||
<ButtonIcon icon={EarthIcon} size="md" />
|
<ButtonIcon icon={EarthIcon} size="md" />
|
||||||
<Select.ValueText
|
<Select.ValueText
|
||||||
placeholder={_(msg`Select an app language`)}
|
placeholder={l`Select an app language`}
|
||||||
style={[t.atoms.text_contrast_medium]}
|
style={[t.atoms.text_contrast_medium]}
|
||||||
/>
|
/>
|
||||||
<Select.Icon style={[t.atoms.text_contrast_medium]} />
|
<Select.Icon style={[t.atoms.text_contrast_medium]} />
|
||||||
@@ -68,7 +67,7 @@ export function AppLanguageDropdown() {
|
|||||||
)}
|
)}
|
||||||
</Select.Trigger>
|
</Select.Trigger>
|
||||||
<Select.Content
|
<Select.Content
|
||||||
label={_(msg`Select language`)}
|
label={l`Select language`}
|
||||||
renderItem={({label, value}) => (
|
renderItem={({label, value}) => (
|
||||||
<Select.Item value={value} label={label} style={web([a.pointer])}>
|
<Select.Item value={value} label={label} style={web([a.pointer])}>
|
||||||
<Select.ItemIndicator />
|
<Select.ItemIndicator />
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ import Animated, {
|
|||||||
useAnimatedStyle,
|
useAnimatedStyle,
|
||||||
} from 'react-native-reanimated'
|
} from 'react-native-reanimated'
|
||||||
import {BlurView} from 'expo-blur'
|
import {BlurView} from 'expo-blur'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {useContextMenuContext} from './context'
|
import {useContextMenuContext} from './context'
|
||||||
@@ -32,7 +31,7 @@ export function Backdrop(props: Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function BlurredBackdrop({animation, intensity = 50, onPress}: Props) {
|
function BlurredBackdrop({animation, intensity = 50, onPress}: Props) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
const animatedProps = useAnimatedProps(() => ({
|
const animatedProps = useAnimatedProps(() => ({
|
||||||
intensity: interpolate(
|
intensity: interpolate(
|
||||||
@@ -50,8 +49,8 @@ function BlurredBackdrop({animation, intensity = 50, onPress}: Props) {
|
|||||||
tint="systemMaterialDark">
|
tint="systemMaterialDark">
|
||||||
<Pressable
|
<Pressable
|
||||||
style={a.flex_1}
|
style={a.flex_1}
|
||||||
accessibilityLabel={_(msg`Close menu`)}
|
accessibilityLabel={l`Close menu`}
|
||||||
accessibilityHint={_(msg`Tap to close context menu`)}
|
accessibilityHint={l`Tap to close context menu`}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
/>
|
/>
|
||||||
</AnimatedBlurView>
|
</AnimatedBlurView>
|
||||||
@@ -60,7 +59,7 @@ function BlurredBackdrop({animation, intensity = 50, onPress}: Props) {
|
|||||||
|
|
||||||
function OpacityBackdrop({animation, onPress}: Props) {
|
function OpacityBackdrop({animation, onPress}: Props) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
const animatedStyle = useAnimatedStyle(() => ({
|
const animatedStyle = useAnimatedStyle(() => ({
|
||||||
opacity: interpolate(
|
opacity: interpolate(
|
||||||
@@ -76,8 +75,8 @@ function OpacityBackdrop({animation, onPress}: Props) {
|
|||||||
style={[a.absolute, a.inset_0, t.atoms.bg_contrast_975, animatedStyle]}>
|
style={[a.absolute, a.inset_0, t.atoms.bg_contrast_975, animatedStyle]}>
|
||||||
<Pressable
|
<Pressable
|
||||||
style={a.flex_1}
|
style={a.flex_1}
|
||||||
accessibilityLabel={_(msg`Close menu`)}
|
accessibilityLabel={l`Close menu`}
|
||||||
accessibilityHint={_(msg`Tap to close context menu`)}
|
accessibilityHint={l`Tap to close context menu`}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
/>
|
/>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ import Animated, {
|
|||||||
type SharedValue,
|
type SharedValue,
|
||||||
useAnimatedStyle,
|
useAnimatedStyle,
|
||||||
} from 'react-native-reanimated'
|
} from 'react-native-reanimated'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {useContextMenuContext} from './context'
|
import {useContextMenuContext} from './context'
|
||||||
@@ -21,7 +20,7 @@ export function Backdrop({
|
|||||||
onPress?: () => void
|
onPress?: () => void
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {mode} = useContextMenuContext()
|
const {mode} = useContextMenuContext()
|
||||||
|
|
||||||
const reduced = mode === 'auxiliary-only'
|
const reduced = mode === 'auxiliary-only'
|
||||||
@@ -42,8 +41,8 @@ export function Backdrop({
|
|||||||
style={[a.absolute, a.inset_0, t.atoms.bg_contrast_975, animatedStyle]}>
|
style={[a.absolute, a.inset_0, t.atoms.bg_contrast_975, animatedStyle]}>
|
||||||
<Pressable
|
<Pressable
|
||||||
style={a.flex_1}
|
style={a.flex_1}
|
||||||
accessibilityLabel={_(msg`Close menu`)}
|
accessibilityLabel={l`Close menu`}
|
||||||
accessibilityHint={_(msg`Tap to close context menu`)}
|
accessibilityHint={l`Tap to close context menu`}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
/>
|
/>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
|
|||||||
@@ -45,8 +45,7 @@ import {
|
|||||||
import {captureRef} from 'react-native-view-shot'
|
import {captureRef} from 'react-native-view-shot'
|
||||||
import {scheduleOnRN} from 'react-native-worklets'
|
import {scheduleOnRN} from 'react-native-worklets'
|
||||||
import {Image, type ImageErrorEventData} from 'expo-image'
|
import {Image, type ImageErrorEventData} from 'expo-image'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {useIsFocused} from '@react-navigation/native'
|
import {useIsFocused} from '@react-navigation/native'
|
||||||
import flattenReactChildren from 'react-keyed-flatten-children'
|
import flattenReactChildren from 'react-keyed-flatten-children'
|
||||||
|
|
||||||
@@ -453,7 +452,7 @@ function TriggerClone({
|
|||||||
onDisplay: () => void
|
onDisplay: () => void
|
||||||
label: string
|
label: string
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
const animatedStyles = useAnimatedStyle(() => {
|
const animatedStyles = useAnimatedStyle(() => {
|
||||||
const anim = animation.get()
|
const anim = animation.get()
|
||||||
@@ -504,7 +503,7 @@ function TriggerClone({
|
|||||||
height: measurement.height,
|
height: measurement.height,
|
||||||
}}
|
}}
|
||||||
accessibilityLabel={label}
|
accessibilityLabel={label}
|
||||||
accessibilityHint={_(msg`The subject of the context menu`)}
|
accessibilityHint={l`The subject of the context menu`}
|
||||||
accessibilityIgnoresInvertColors={false}
|
accessibilityIgnoresInvertColors={false}
|
||||||
cachePolicy="none"
|
cachePolicy="none"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -26,8 +26,7 @@ import Animated, {
|
|||||||
} from 'react-native-reanimated'
|
} from 'react-native-reanimated'
|
||||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||||
import {scheduleOnRN} from 'react-native-worklets'
|
import {scheduleOnRN} from 'react-native-worklets'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {ScrollProvider} from '#/lib/ScrollContext'
|
import {ScrollProvider} from '#/lib/ScrollContext'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
@@ -386,7 +385,7 @@ export function Handle({
|
|||||||
fill?: string
|
fill?: string
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {screenReaderEnabled} = useA11y()
|
const {screenReaderEnabled} = useA11y()
|
||||||
const {close} = useDialogContext()
|
const {close} = useDialogContext()
|
||||||
|
|
||||||
@@ -395,8 +394,8 @@ export function Handle({
|
|||||||
<Pressable
|
<Pressable
|
||||||
accessible={screenReaderEnabled}
|
accessible={screenReaderEnabled}
|
||||||
onPress={() => close()}
|
onPress={() => close()}
|
||||||
accessibilityLabel={_(msg`Dismiss`)}
|
accessibilityLabel={l`Dismiss`}
|
||||||
accessibilityHint={_(msg`Double tap to close the dialog`)}>
|
accessibilityHint={l`Double tap to close the dialog`}>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
a.rounded_sm,
|
a.rounded_sm,
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ import {
|
|||||||
View,
|
View,
|
||||||
type ViewStyle,
|
type ViewStyle,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {DismissableLayer, FocusGuards, FocusScope} from 'radix-ui/internal'
|
import {DismissableLayer, FocusGuards, FocusScope} from 'radix-ui/internal'
|
||||||
import {RemoveScrollBar} from 'react-remove-scroll-bar'
|
import {RemoveScrollBar} from 'react-remove-scroll-bar'
|
||||||
|
|
||||||
@@ -55,7 +54,7 @@ export function Outer({
|
|||||||
onClose,
|
onClose,
|
||||||
webOptions,
|
webOptions,
|
||||||
}: React.PropsWithChildren<DialogOuterProps>) {
|
}: React.PropsWithChildren<DialogOuterProps>) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
const {setDialogIsOpen} = useDialogStateControlContext()
|
const {setDialogIsOpen} = useDialogStateControlContext()
|
||||||
@@ -126,7 +125,7 @@ export function Outer({
|
|||||||
<RemoveScrollBar />
|
<RemoveScrollBar />
|
||||||
<Pressable
|
<Pressable
|
||||||
accessibilityHint={undefined}
|
accessibilityHint={undefined}
|
||||||
accessibilityLabel={_(msg`Close active dialog`)}
|
accessibilityLabel={l`Close active dialog`}
|
||||||
onPress={handleBackgroundPress}>
|
onPress={handleBackgroundPress}>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
@@ -321,7 +320,7 @@ export function FlatListFooter({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Close() {
|
export function Close() {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {close} = useContext(Context)
|
const {close} = useContext(Context)
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
@@ -339,7 +338,7 @@ export function Close() {
|
|||||||
color="secondary"
|
color="secondary"
|
||||||
shape="round"
|
shape="round"
|
||||||
onPress={() => close()}
|
onPress={() => close()}
|
||||||
label={_(msg`Close active dialog`)}>
|
label={l`Close active dialog`}>
|
||||||
<ButtonIcon icon={X} size="md" />
|
<ButtonIcon icon={X} size="md" />
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
View,
|
View,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {useA11y} from '#/state/a11y'
|
import {useA11y} from '#/state/a11y'
|
||||||
|
|
||||||
@@ -40,7 +39,7 @@ export function FocusScope({children}: {children: React.ReactNode}) {
|
|||||||
* remain within the active content section.
|
* remain within the active content section.
|
||||||
*/
|
*/
|
||||||
function FocusTrap({children}: {children: React.ReactNode}) {
|
function FocusTrap({children}: {children: React.ReactNode}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const child = useRef<View>(null)
|
const child = useRef<View>(null)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -84,12 +83,8 @@ function FocusTrap({children}: {children: React.ReactNode}) {
|
|||||||
<>
|
<>
|
||||||
<Pressable
|
<Pressable
|
||||||
accessible
|
accessible
|
||||||
accessibilityLabel={_(
|
accessibilityLabel={l`You've reached the start of the active content.`}
|
||||||
msg`You've reached the start of the active content.`,
|
accessibilityHint={l`Please go back, or activate this element to return to the start of the active content.`}
|
||||||
)}
|
|
||||||
accessibilityHint={_(
|
|
||||||
msg`Please go back, or activate this element to return to the start of the active content.`,
|
|
||||||
)}
|
|
||||||
accessibilityActions={[{name: 'activate', label: 'activate'}]}
|
accessibilityActions={[{name: 'activate', label: 'activate'}]}
|
||||||
onAccessibilityAction={event => {
|
onAccessibilityAction={event => {
|
||||||
switch (event.nativeEvent.actionName) {
|
switch (event.nativeEvent.actionName) {
|
||||||
@@ -108,12 +103,8 @@ function FocusTrap({children}: {children: React.ReactNode}) {
|
|||||||
{decoratedChildren}
|
{decoratedChildren}
|
||||||
</View>
|
</View>
|
||||||
<Pressable
|
<Pressable
|
||||||
accessibilityLabel={_(
|
accessibilityLabel={l`You've reached the end of the active content.`}
|
||||||
msg`You've reached the end of the active content.`,
|
accessibilityHint={l`Please go back, or activate this element to return to the start of the active content.`}
|
||||||
)}
|
|
||||||
accessibilityHint={_(
|
|
||||||
msg`Please go back, or activate this element to return to the start of the active content.`,
|
|
||||||
)}
|
|
||||||
accessibilityActions={[{name: 'activate', label: 'activate'}]}
|
accessibilityActions={[{name: 'activate', label: 'activate'}]}
|
||||||
onAccessibilityAction={event => {
|
onAccessibilityAction={event => {
|
||||||
switch (event.nativeEvent.actionName) {
|
switch (event.nativeEvent.actionName) {
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ import {
|
|||||||
View,
|
View,
|
||||||
type ViewStyle,
|
type ViewStyle,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||||
import {DraggableScrollView} from '#/view/com/pager/DraggableScrollView'
|
import {DraggableScrollView} from '#/view/com/pager/DraggableScrollView'
|
||||||
@@ -46,7 +45,7 @@ export function InterestTabs({
|
|||||||
gutterWidth?: number
|
gutterWidth?: number
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const listRef = useRef<ScrollView>(null)
|
const listRef = useRef<ScrollView>(null)
|
||||||
const [totalWidth, setTotalWidth] = useState(0)
|
const [totalWidth, setTotalWidth] = useState(0)
|
||||||
const [scrollX, setScrollX] = useState(0)
|
const [scrollX, setScrollX] = useState(0)
|
||||||
@@ -255,7 +254,7 @@ export function InterestTabs({
|
|||||||
}),
|
}),
|
||||||
]}>
|
]}>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Scroll left`)}
|
label={l`Scroll left`}
|
||||||
onPress={scrollLeft}
|
onPress={scrollLeft}
|
||||||
onPressIn={() => startContinuousScroll('left')}
|
onPressIn={() => startContinuousScroll('left')}
|
||||||
onPressOut={stopContinuousScroll}
|
onPressOut={stopContinuousScroll}
|
||||||
@@ -289,7 +288,7 @@ export function InterestTabs({
|
|||||||
}),
|
}),
|
||||||
]}>
|
]}>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Scroll right`)}
|
label={l`Scroll right`}
|
||||||
onPress={scrollRight}
|
onPress={scrollRight}
|
||||||
onPressIn={() => startContinuousScroll('right')}
|
onPressIn={() => startContinuousScroll('right')}
|
||||||
onPressOut={stopContinuousScroll}
|
onPressOut={stopContinuousScroll}
|
||||||
@@ -327,22 +326,18 @@ function Tab({
|
|||||||
onLayout: (index: number, x: number, width: number) => void
|
onLayout: (index: number, x: number, width: number) => void
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const label = active
|
const label = active
|
||||||
? _(
|
? l({
|
||||||
msg({
|
message: `"${interestsDisplayName}" category (active)`,
|
||||||
message: `"${interestsDisplayName}" category (active)`,
|
comment:
|
||||||
comment:
|
'Accessibility label for a category (e.g. Art, Video Games, Sports, etc.) that shows suggested accounts for the user to follow. The tab is currently selected.',
|
||||||
'Accessibility label for a category (e.g. Art, Video Games, Sports, etc.) that shows suggested accounts for the user to follow. The tab is currently selected.',
|
})
|
||||||
}),
|
: l({
|
||||||
)
|
message: `Select "${interestsDisplayName}" category`,
|
||||||
: _(
|
comment:
|
||||||
msg({
|
'Accessibility label for a category (e.g. Art, Video Games, Sports, etc.) that shows suggested accounts for the user to follow. The tab is not currently active and can be selected.',
|
||||||
message: `Select "${interestsDisplayName}" category`,
|
})
|
||||||
comment:
|
|
||||||
'Accessibility label for a category (e.g. Art, Video Games, Sports, etc.) that shows suggested accounts for the user to follow. The tab is not currently active and can be selected.',
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {Fragment, useMemo} from 'react'
|
import {Fragment, useMemo} from 'react'
|
||||||
import {Text as RNText} from 'react-native'
|
import {Text as RNText} from 'react-native'
|
||||||
import {Image} from 'expo-image'
|
import {Image} from 'expo-image'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type CountryCode,
|
type CountryCode,
|
||||||
@@ -28,7 +27,7 @@ export function InternationalPhoneCodeSelect({
|
|||||||
value?: CountryCode
|
value?: CountryCode
|
||||||
onChange: (value: CountryCode) => void
|
onChange: (value: CountryCode) => void
|
||||||
}) {
|
}) {
|
||||||
const {_, i18n} = useLingui()
|
const {t: l, i18n} = useLingui()
|
||||||
const location = useGeolocation()
|
const location = useGeolocation()
|
||||||
|
|
||||||
const defaultCountry = useMemo(() => {
|
const defaultCountry = useMemo(() => {
|
||||||
@@ -65,7 +64,7 @@ export function InternationalPhoneCodeSelect({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Select.Root value={value} onValueChange={onChange as (v: string) => void}>
|
<Select.Root value={value} onValueChange={onChange as (v: string) => void}>
|
||||||
<Select.Trigger label={_(msg`Select telephone code`)}>
|
<Select.Trigger label={l`Select telephone code`}>
|
||||||
<Select.ValueText placeholder="+..." webOverrideValue={selected}>
|
<Select.ValueText placeholder="+..." webOverrideValue={selected}>
|
||||||
{selected => (
|
{selected => (
|
||||||
<>
|
<>
|
||||||
@@ -77,7 +76,7 @@ export function InternationalPhoneCodeSelect({
|
|||||||
<Select.Icon />
|
<Select.Icon />
|
||||||
</Select.Trigger>
|
</Select.Trigger>
|
||||||
<Select.Content
|
<Select.Content
|
||||||
label={_(msg`Country code`)}
|
label={l`Country code`}
|
||||||
items={items}
|
items={items}
|
||||||
renderItem={item => (
|
renderItem={item => (
|
||||||
<Fragment key={item.value}>
|
<Fragment key={item.value}>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {type AppBskyLabelerDefs} from '@atproto/api'
|
import {type AppBskyLabelerDefs} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Plural, Trans} from '@lingui/react/macro'
|
import {Plural, Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {getLabelingServiceTitle} from '#/lib/moderation'
|
import {getLabelingServiceTitle} from '#/lib/moderation'
|
||||||
@@ -52,14 +51,14 @@ export function Title({value}: {value: string}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Description({value, handle}: {value?: string; handle: string}) {
|
export function Description({value, handle}: {value?: string; handle: string}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
return value ? (
|
return value ? (
|
||||||
<Text numberOfLines={2}>
|
<Text numberOfLines={2}>
|
||||||
<RichText value={value} />
|
<RichText value={value} />
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
<Text emoji style={[a.leading_snug]}>
|
<Text emoji style={[a.leading_snug]}>
|
||||||
{_(msg`By ${sanitizeHandle(handle, '@')}`)}
|
{l`By ${sanitizeHandle(handle, '@')}`}
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -150,7 +149,7 @@ export function Link({
|
|||||||
children,
|
children,
|
||||||
labeler,
|
labeler,
|
||||||
}: LabelingServiceProps & Pick<LinkProps, 'children'>) {
|
}: LabelingServiceProps & Pick<LinkProps, 'children'>) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InternalLink
|
<InternalLink
|
||||||
@@ -160,9 +159,7 @@ export function Link({
|
|||||||
name: labeler.creator.did,
|
name: labeler.creator.did,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
label={_(
|
label={l`View the labeling service provided by @${labeler.creator.handle}`}>
|
||||||
msg`View the labeling service provided by @${labeler.creator.handle}`,
|
|
||||||
)}>
|
|
||||||
{children}
|
{children}
|
||||||
</InternalLink>
|
</InternalLink>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {sanitizeAppLanguageSetting} from '#/locale/helpers'
|
import {sanitizeAppLanguageSetting} from '#/locale/helpers'
|
||||||
import {APP_LANGUAGES} from '#/locale/languages'
|
import {APP_LANGUAGES} from '#/locale/languages'
|
||||||
@@ -21,7 +20,7 @@ export function LanguageSelect({
|
|||||||
label?: string
|
label?: string
|
||||||
disabledBlueskySupportedLanguageSanitization?: boolean
|
disabledBlueskySupportedLanguageSanitization?: boolean
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const selectValue =
|
const selectValue =
|
||||||
value && !disabledBlueskySupportedLanguageSanitization
|
value && !disabledBlueskySupportedLanguageSanitization
|
||||||
? sanitizeAppLanguageSetting(value)
|
? sanitizeAppLanguageSetting(value)
|
||||||
@@ -38,8 +37,8 @@ export function LanguageSelect({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Select.Root value={selectValue} onValueChange={handleOnChange}>
|
<Select.Root value={selectValue} onValueChange={handleOnChange}>
|
||||||
<Select.Trigger label={_(msg`Select language`)}>
|
<Select.Trigger label={l`Select language`}>
|
||||||
<Select.ValueText placeholder={_(msg`Select language`)} />
|
<Select.ValueText placeholder={l`Select language`} />
|
||||||
<Select.Icon />
|
<Select.Icon />
|
||||||
</Select.Trigger>
|
</Select.Trigger>
|
||||||
<Select.Content
|
<Select.Content
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {createContext, useCallback, useContext} from 'react'
|
import {createContext, useCallback, useContext} from 'react'
|
||||||
import {type GestureResponderEvent, Keyboard, View} from 'react-native'
|
import {type GestureResponderEvent, Keyboard, View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
import {type NavigationProp} from '#/lib/routes/types'
|
import {type NavigationProp} from '#/lib/routes/types'
|
||||||
@@ -112,7 +111,7 @@ export function Slot({children}: {children?: React.ReactNode}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function BackButton({onPress, style, ...props}: Partial<ButtonProps>) {
|
export function BackButton({onPress, style, ...props}: Partial<ButtonProps>) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const navigation = useNavigation<NavigationProp>()
|
const navigation = useNavigation<NavigationProp>()
|
||||||
|
|
||||||
const onPressBack = useCallback(
|
const onPressBack = useCallback(
|
||||||
@@ -131,7 +130,7 @@ export function BackButton({onPress, style, ...props}: Partial<ButtonProps>) {
|
|||||||
return (
|
return (
|
||||||
<Slot>
|
<Slot>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Go back`)}
|
label={l`Go back`}
|
||||||
size="small"
|
size="small"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
@@ -151,7 +150,7 @@ export function BackButton({onPress, style, ...props}: Partial<ButtonProps>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function MenuButton() {
|
export function MenuButton() {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const setDrawerOpen = useSetDrawerOpen()
|
const setDrawerOpen = useSetDrawerOpen()
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
@@ -165,7 +164,7 @@ export function MenuButton() {
|
|||||||
return gtMobile ? null : (
|
return gtMobile ? null : (
|
||||||
<Slot>
|
<Slot>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Open drawer menu`)}
|
label={l`Open drawer menu`}
|
||||||
size="small"
|
size="small"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {useCallback, useMemo, useState} 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 {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
|
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
|
||||||
import {cleanError} from '#/lib/strings/errors'
|
import {cleanError} from '#/lib/strings/errors'
|
||||||
@@ -27,7 +26,7 @@ function keyExtractor(item: GetLikes.Like) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function LikedByList({uri}: {uri: string}) {
|
export function LikedByList({uri}: {uri: string}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const initialNumToRender = useInitialNumToRender()
|
const initialNumToRender = useInitialNumToRender()
|
||||||
const [isPTRing, setIsPTRing] = useState(false)
|
const [isPTRing, setIsPTRing] = useState(false)
|
||||||
|
|
||||||
@@ -81,10 +80,8 @@ export function LikedByList({uri}: {uri: string}) {
|
|||||||
isLoading={isUriLoading || isLikedByLoading}
|
isLoading={isUriLoading || isLikedByLoading}
|
||||||
isError={isError}
|
isError={isError}
|
||||||
emptyType="results"
|
emptyType="results"
|
||||||
emptyTitle={_(msg`No likes yet`)}
|
emptyTitle={l`No likes yet`}
|
||||||
emptyMessage={_(
|
emptyMessage={l`Nobody has liked this yet. Maybe you should be the first!`}
|
||||||
msg`Nobody has liked this yet. Maybe you should be the first!`,
|
|
||||||
)}
|
|
||||||
errorMessage={cleanError(resolveError || error)}
|
errorMessage={cleanError(resolveError || error)}
|
||||||
onRetry={isError ? refetch : undefined}
|
onRetry={isError ? refetch : undefined}
|
||||||
topBorder={false}
|
topBorder={false}
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import {
|
|||||||
moderateUserList,
|
moderateUserList,
|
||||||
type ModerationUI,
|
type ModerationUI,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
import {useQueryClient} from '@tanstack/react-query'
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
@@ -115,7 +114,7 @@ export function TitleAndByline({
|
|||||||
modUi?: ModerationUI
|
modUi?: ModerationUI
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -142,15 +141,14 @@ export function TitleAndByline({
|
|||||||
</Text>
|
</Text>
|
||||||
</Hider.Content>
|
</Hider.Content>
|
||||||
</Hider.Outer>
|
</Hider.Outer>
|
||||||
|
|
||||||
{creator && (
|
{creator && (
|
||||||
<Text
|
<Text
|
||||||
emoji
|
emoji
|
||||||
style={[a.leading_snug, t.atoms.text_contrast_medium]}
|
style={[a.leading_snug, t.atoms.text_contrast_medium]}
|
||||||
numberOfLines={1}>
|
numberOfLines={1}>
|
||||||
{purpose === MODLIST
|
{purpose === MODLIST
|
||||||
? _(msg`Moderation list by ${sanitizeHandle(creator.handle, '@')}`)
|
? l`Moderation list by ${sanitizeHandle(creator.handle, '@')}`
|
||||||
: _(msg`List by ${sanitizeHandle(creator.handle, '@')}`)}
|
: l`List by ${sanitizeHandle(creator.handle, '@')}`}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {memo} from 'react'
|
import {memo} from 'react'
|
||||||
import {type StyleProp, View, type ViewStyle} from 'react-native'
|
import {type StyleProp, View, type ViewStyle} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {cleanError} from '#/lib/strings/errors'
|
import {cleanError} from '#/lib/strings/errors'
|
||||||
@@ -75,7 +74,7 @@ function ListFooterMaybeError({
|
|||||||
onRetry?: () => Promise<unknown>
|
onRetry?: () => Promise<unknown>
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
if (!error) return null
|
if (!error) return null
|
||||||
|
|
||||||
@@ -101,7 +100,7 @@ function ListFooterMaybeError({
|
|||||||
</Text>
|
</Text>
|
||||||
<Button
|
<Button
|
||||||
variant="solid"
|
variant="solid"
|
||||||
label={_(msg`Press to retry`)}
|
label={l`Press to retry`}
|
||||||
style={[
|
style={[
|
||||||
a.align_center,
|
a.align_center,
|
||||||
a.justify_center,
|
a.justify_center,
|
||||||
@@ -156,7 +155,7 @@ let ListMaybePlaceholder = ({
|
|||||||
useEmptyState?: boolean
|
useEmptyState?: boolean
|
||||||
}): React.ReactNode => {
|
}): React.ReactNode => {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {gtMobile, gtTablet} = useBreakpoints()
|
const {gtMobile, gtTablet} = useBreakpoints()
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
@@ -181,8 +180,8 @@ let ListMaybePlaceholder = ({
|
|||||||
if (isError) {
|
if (isError) {
|
||||||
return (
|
return (
|
||||||
<Error
|
<Error
|
||||||
title={errorTitle ?? _(msg`Oops!`)}
|
title={errorTitle ?? l`Oops!`}
|
||||||
message={errorMessage ?? _(msg`Something went wrong!`)}
|
message={errorMessage ?? l`Something went wrong!`}
|
||||||
onRetry={onRetry}
|
onRetry={onRetry}
|
||||||
onGoBack={onGoBack}
|
onGoBack={onGoBack}
|
||||||
hideBackButton={hideBackButton}
|
hideBackButton={hideBackButton}
|
||||||
@@ -199,9 +198,7 @@ let ListMaybePlaceholder = ({
|
|||||||
icon={emptyStateIcon}
|
icon={emptyStateIcon}
|
||||||
message={
|
message={
|
||||||
emptyMessage ??
|
emptyMessage ??
|
||||||
(emptyType === 'results'
|
(emptyType === 'results' ? l`No results found` : l`Page not found`)
|
||||||
? _(msg`No results found`)
|
|
||||||
: _(msg`Page not found`))
|
|
||||||
}
|
}
|
||||||
button={emptyStateButton}
|
button={emptyStateButton}
|
||||||
/>
|
/>
|
||||||
@@ -214,13 +211,11 @@ let ListMaybePlaceholder = ({
|
|||||||
<Error
|
<Error
|
||||||
title={
|
title={
|
||||||
emptyTitle ??
|
emptyTitle ??
|
||||||
(emptyType === 'results'
|
(emptyType === 'results' ? l`No results found` : l`Page not found`)
|
||||||
? _(msg`No results found`)
|
|
||||||
: _(msg`Page not found`))
|
|
||||||
}
|
}
|
||||||
message={
|
message={
|
||||||
emptyMessage ??
|
emptyMessage ??
|
||||||
_(msg`We're sorry! We can't find the page you were looking for.`)
|
l`We're sorry! We can't find the page you were looking for.`
|
||||||
}
|
}
|
||||||
onRetry={onRetry}
|
onRetry={onRetry}
|
||||||
onGoBack={onGoBack}
|
onGoBack={onGoBack}
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import {
|
|||||||
View,
|
View,
|
||||||
type ViewStyle,
|
type ViewStyle,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {DropdownMenu} from 'radix-ui'
|
import {DropdownMenu} from 'radix-ui'
|
||||||
|
|
||||||
import {useA11y} from '#/state/a11y'
|
import {useA11y} from '#/state/a11y'
|
||||||
@@ -62,7 +61,7 @@ export function Root({
|
|||||||
}: React.PropsWithChildren<{
|
}: React.PropsWithChildren<{
|
||||||
control?: Dialog.DialogControlProps
|
control?: Dialog.DialogControlProps
|
||||||
}>) {
|
}>) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const defaultControl = useMenuControl()
|
const defaultControl = useMenuControl()
|
||||||
const context = useMemo<ContextType>(
|
const context = useMemo<ContextType>(
|
||||||
() => ({
|
() => ({
|
||||||
@@ -89,9 +88,7 @@ export function Root({
|
|||||||
style={[a.fixed, a.inset_0, a.z_50]}
|
style={[a.fixed, a.inset_0, a.z_50]}
|
||||||
onPress={() => context.control.close()}
|
onPress={() => context.control.close()}
|
||||||
accessibilityHint=""
|
accessibilityHint=""
|
||||||
accessibilityLabel={_(
|
accessibilityLabel={l`Context menu backdrop, click to close the menu.`}
|
||||||
msg`Context menu backdrop, click to close the menu.`,
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</Portal>
|
</Portal>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {useMemo, useState} from 'react'
|
import {useMemo, useState} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {type AppBskyActorDefs, moderateProfile} from '@atproto/api'
|
import {type AppBskyActorDefs, moderateProfile} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
import {differenceInSeconds} from 'date-fns'
|
import {differenceInSeconds} from 'date-fns'
|
||||||
|
|
||||||
@@ -28,7 +27,7 @@ export function NewskieDialog({
|
|||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const control = useDialogControl()
|
const control = useDialogControl()
|
||||||
|
|
||||||
const createdAt = profile.createdAt
|
const createdAt = profile.createdAt
|
||||||
@@ -45,9 +44,7 @@ export function NewskieDialog({
|
|||||||
<View style={[a.pr_2xs]}>
|
<View style={[a.pr_2xs]}>
|
||||||
<Button
|
<Button
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
label={_(
|
label={l`This user is new here. Press for more info about when they joined.`}
|
||||||
msg`This user is new here. Press for more info about when they joined.`,
|
|
||||||
)}
|
|
||||||
hitSlop={HITSLOP_10}
|
hitSlop={HITSLOP_10}
|
||||||
onPress={control.open}>
|
onPress={control.open}>
|
||||||
{({hovered, pressed}) => (
|
{({hovered, pressed}) => (
|
||||||
@@ -60,7 +57,6 @@ export function NewskieDialog({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
|
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
<DialogInner profile={profile} createdAt={createdAt} now={now} />
|
<DialogInner profile={profile} createdAt={createdAt} now={now} />
|
||||||
@@ -79,7 +75,7 @@ function DialogInner({
|
|||||||
now: number
|
now: number
|
||||||
}) {
|
}) {
|
||||||
const control = Dialog.useDialogContext()
|
const control = Dialog.useDialogContext()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const moderationOpts = useModerationOpts()
|
const moderationOpts = useModerationOpts()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
@@ -100,26 +96,22 @@ function DialogInner({
|
|||||||
|
|
||||||
if (isMe) {
|
if (isMe) {
|
||||||
if (profile.joinedViaStarterPack) {
|
if (profile.joinedViaStarterPack) {
|
||||||
return _(
|
return l`You joined Bluesky using a starter pack ${timeAgoString} ago`
|
||||||
msg`You joined Bluesky using a starter pack ${timeAgoString} ago`,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
return _(msg`You joined Bluesky ${timeAgoString} ago`)
|
return l`You joined Bluesky ${timeAgoString} ago`
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (profile.joinedViaStarterPack) {
|
if (profile.joinedViaStarterPack) {
|
||||||
return _(
|
return l`${profileName} joined Bluesky using a starter pack ${timeAgoString} ago`
|
||||||
msg`${profileName} joined Bluesky using a starter pack ${timeAgoString} ago`,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
return _(msg`${profileName} joined Bluesky ${timeAgoString} ago`)
|
return l`${profileName} joined Bluesky ${timeAgoString} ago`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
label={_(msg`New user info dialog`)}
|
label={l`New user info dialog`}
|
||||||
style={web({maxWidth: 400})}>
|
style={web({maxWidth: 400})}>
|
||||||
<View style={[a.gap_md]}>
|
<View style={[a.gap_md]}>
|
||||||
<View style={[a.align_center]}>
|
<View style={[a.align_center]}>
|
||||||
@@ -166,7 +158,7 @@ function DialogInner({
|
|||||||
|
|
||||||
{IS_NATIVE && (
|
{IS_NATIVE && (
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Close`)}
|
label={l`Close`}
|
||||||
color="secondary"
|
color="secondary"
|
||||||
size="small"
|
size="small"
|
||||||
style={[a.mt_sm]}
|
style={[a.mt_sm]}
|
||||||
@@ -177,7 +169,6 @@ function DialogInner({
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Dialog.Close />
|
<Dialog.Close />
|
||||||
</Dialog.ScrollableInner>
|
</Dialog.ScrollableInner>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {useCallback} from 'react'
|
import {useCallback} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {useA11y} from '#/state/a11y'
|
import {useA11y} from '#/state/a11y'
|
||||||
@@ -16,7 +15,7 @@ import {IS_ANDROID} from '#/env'
|
|||||||
|
|
||||||
export function Content({state}: {state: PolicyUpdateState}) {
|
export function Content({state}: {state: PolicyUpdateState}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {screenReaderEnabled} = useA11y()
|
const {screenReaderEnabled} = useA11y()
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
@@ -28,27 +27,27 @@ export function Content({state}: {state: PolicyUpdateState}) {
|
|||||||
terms: {
|
terms: {
|
||||||
overridePresentation: false,
|
overridePresentation: false,
|
||||||
to: `https://bsky.social/about/support/tos`,
|
to: `https://bsky.social/about/support/tos`,
|
||||||
label: _(msg`Terms of Service`),
|
label: l`Terms of Service`,
|
||||||
},
|
},
|
||||||
privacy: {
|
privacy: {
|
||||||
overridePresentation: false,
|
overridePresentation: false,
|
||||||
to: `https://bsky.social/about/support/privacy-policy`,
|
to: `https://bsky.social/about/support/privacy-policy`,
|
||||||
label: _(msg`Privacy Policy`),
|
label: l`Privacy Policy`,
|
||||||
},
|
},
|
||||||
copyright: {
|
copyright: {
|
||||||
overridePresentation: false,
|
overridePresentation: false,
|
||||||
to: `https://bsky.social/about/support/copyright`,
|
to: `https://bsky.social/about/support/copyright`,
|
||||||
label: _(msg`Copyright Policy`),
|
label: l`Copyright Policy`,
|
||||||
},
|
},
|
||||||
guidelines: {
|
guidelines: {
|
||||||
overridePresentation: false,
|
overridePresentation: false,
|
||||||
to: `https://bsky.social/about/support/community-guidelines`,
|
to: `https://bsky.social/about/support/community-guidelines`,
|
||||||
label: _(msg`Community Guidelines`),
|
label: l`Community Guidelines`,
|
||||||
},
|
},
|
||||||
blog: {
|
blog: {
|
||||||
overridePresentation: false,
|
overridePresentation: false,
|
||||||
to: `https://bsky.social/about/blog/08-14-2025-updated-terms-and-policies`,
|
to: `https://bsky.social/about/blog/08-14-2025-updated-terms-and-policies`,
|
||||||
label: _(msg`Our blog post`),
|
label: l`Our blog post`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
const linkButtonStyles = {
|
const linkButtonStyles = {
|
||||||
@@ -58,10 +57,8 @@ export function Content({state}: {state: PolicyUpdateState}) {
|
|||||||
} as const
|
} as const
|
||||||
|
|
||||||
const label = IS_ANDROID
|
const label = IS_ANDROID
|
||||||
? _(
|
? l`We’re updating our Terms of Service, Privacy Policy, and Copyright Policy, effective September 15th, 2025. We're also updating our Community Guidelines, and we want your input! These new guidelines will take effect on October 15th, 2025. Learn more about these changes and how to share your thoughts with us by reading our blog post.`
|
||||||
msg`We’re updating our Terms of Service, Privacy Policy, and Copyright Policy, effective September 15th, 2025. We're also updating our Community Guidelines, and we want your input! These new guidelines will take effect on October 15th, 2025. Learn more about these changes and how to share your thoughts with us by reading our blog post.`,
|
: l`We're updating our policies`
|
||||||
)
|
|
||||||
: _(msg`We're updating our policies`)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Overlay label={label}>
|
<Overlay label={label}>
|
||||||
@@ -160,10 +157,8 @@ export function Content({state}: {state: PolicyUpdateState}) {
|
|||||||
|
|
||||||
<View style={[a.w_full, a.gap_md]}>
|
<View style={[a.w_full, a.gap_md]}>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Continue`)}
|
label={l`Continue`}
|
||||||
accessibilityHint={_(
|
accessibilityHint={l`Tap to acknowledge that you understand and agree to these updates and continue using Bluesky`}
|
||||||
msg`Tap to acknowledge that you understand and agree to these updates and continue using Bluesky`,
|
|
||||||
)}
|
|
||||||
color="primary"
|
color="primary"
|
||||||
size="large"
|
size="large"
|
||||||
onPress={handleClose}>
|
onPress={handleClose}>
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import {
|
|||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {Image} from 'expo-image'
|
import {Image} from 'expo-image'
|
||||||
import {type AppBskyEmbedExternal} from '@atproto/api'
|
import {type AppBskyEmbedExternal} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {type EmbedPlayerParams} from '#/lib/strings/embed-player'
|
import {type EmbedPlayerParams} from '#/lib/strings/embed-player'
|
||||||
import {useExternalEmbedsPrefs} from '#/state/preferences'
|
import {useExternalEmbedsPrefs} from '#/state/preferences'
|
||||||
@@ -27,7 +26,7 @@ export function ExternalGif({
|
|||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const externalEmbedsPrefs = useExternalEmbedsPrefs()
|
const externalEmbedsPrefs = useExternalEmbedsPrefs()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const consentDialogControl = useDialogControl()
|
const consentDialogControl = useDialogControl()
|
||||||
|
|
||||||
// Tracking if the placer has been activated
|
// Tracking if the placer has been activated
|
||||||
@@ -106,8 +105,8 @@ export function ExternalGif({
|
|||||||
]}
|
]}
|
||||||
onPress={onPlayPress}
|
onPress={onPlayPress}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityHint={_(msg`Plays the GIF`)}
|
accessibilityHint={l`Plays the GIF`}
|
||||||
accessibilityLabel={_(msg`Play ${link.title}`)}>
|
accessibilityLabel={l`Play ${link.title}`}>
|
||||||
<Image
|
<Image
|
||||||
source={{
|
source={{
|
||||||
uri:
|
uri:
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ import {WebView} from 'react-native-webview'
|
|||||||
import {scheduleOnRN} from 'react-native-worklets'
|
import {scheduleOnRN} from 'react-native-worklets'
|
||||||
import {Image} from 'expo-image'
|
import {Image} from 'expo-image'
|
||||||
import {type AppBskyEmbedExternal} from '@atproto/api'
|
import {type AppBskyEmbedExternal} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
import {type NavigationProp} from '#/lib/routes/types'
|
import {type NavigationProp} from '#/lib/routes/types'
|
||||||
@@ -49,7 +48,7 @@ function PlaceholderOverlay({
|
|||||||
isPlayerActive: boolean
|
isPlayerActive: boolean
|
||||||
onPress: (event: GestureResponderEvent) => void
|
onPress: (event: GestureResponderEvent) => void
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
// If the player is active and not loading, we don't want to show the overlay.
|
// If the player is active and not loading, we don't want to show the overlay.
|
||||||
if (isPlayerActive && !isLoading) return null
|
if (isPlayerActive && !isLoading) return null
|
||||||
@@ -58,8 +57,8 @@ function PlaceholderOverlay({
|
|||||||
<View style={[a.absolute, a.inset_0, {zIndex: 2}]}>
|
<View style={[a.absolute, a.inset_0, {zIndex: 2}]}>
|
||||||
<Pressable
|
<Pressable
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Play Video`)}
|
accessibilityLabel={l`Play Video`}
|
||||||
accessibilityHint={_(msg`Plays the video`)}
|
accessibilityHint={l`Plays the video`}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
style={[a.flex_1, a.justify_center, a.align_center]}>
|
style={[a.flex_1, a.justify_center, a.align_center]}>
|
||||||
{!isPlayerActive ? (
|
{!isPlayerActive ? (
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {useRef, useState} from 'react'
|
import {useRef, useState} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {type EmbedPlayerParams} from '#/lib/strings/embed-player'
|
import {type EmbedPlayerParams} from '#/lib/strings/embed-player'
|
||||||
import {useAutoplayDisabled} from '#/state/preferences'
|
import {useAutoplayDisabled} from '#/state/preferences'
|
||||||
@@ -27,7 +26,7 @@ export function GifEmbed({
|
|||||||
hideAlt?: boolean
|
hideAlt?: boolean
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const autoplayDisabled = useAutoplayDisabled()
|
const autoplayDisabled = useAutoplayDisabled()
|
||||||
|
|
||||||
const playerRef = useRef<GifView>(null)
|
const playerRef = useRef<GifView>(null)
|
||||||
@@ -82,7 +81,7 @@ export function GifEmbed({
|
|||||||
autoplay={!autoplayDisabled}
|
autoplay={!autoplayDisabled}
|
||||||
onPlayerStateChange={onPlayerStateChange}
|
onPlayerStateChange={onPlayerStateChange}
|
||||||
ref={playerRef}
|
ref={playerRef}
|
||||||
accessibilityHint={_(msg`Animated GIF`)}
|
accessibilityHint={l`Animated GIF`}
|
||||||
accessibilityLabel={altText}
|
accessibilityLabel={altText}
|
||||||
/>
|
/>
|
||||||
{!playerState.isPlaying && (
|
{!playerState.isPlaying && (
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import {useMemo} from 'react'
|
|||||||
import {type StyleProp, View, type ViewStyle} from 'react-native'
|
import {type StyleProp, View, type ViewStyle} from 'react-native'
|
||||||
import {Image} from 'expo-image'
|
import {Image} from 'expo-image'
|
||||||
import {type AppBskyEmbedExternal} from '@atproto/api'
|
import {type AppBskyEmbedExternal} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {parseAltFromGIFDescription} from '#/lib/gif-alt-text'
|
import {parseAltFromGIFDescription} from '#/lib/gif-alt-text'
|
||||||
import {useHaptics} from '#/lib/haptics'
|
import {useHaptics} from '#/lib/haptics'
|
||||||
@@ -35,7 +34,7 @@ export const ExternalEmbed = ({
|
|||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
hideAlt?: boolean
|
hideAlt?: boolean
|
||||||
}) => {
|
}) => {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const playHaptic = useHaptics()
|
const playHaptic = useHaptics()
|
||||||
const externalEmbedPrefs = useExternalEmbedsPrefs()
|
const externalEmbedPrefs = useExternalEmbedsPrefs()
|
||||||
@@ -85,7 +84,7 @@ export const ExternalEmbed = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
label={link.title || _(msg`Open link to ${niceUrl}`)}
|
label={link.title || l`Open link to ${niceUrl}`}
|
||||||
to={link.uri}
|
to={link.uri}
|
||||||
shouldProxy={true}
|
shouldProxy={true}
|
||||||
peek
|
peek
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import {ActivityIndicator, View} from 'react-native'
|
import {ActivityIndicator, View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
|
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
|
||||||
@@ -22,15 +21,15 @@ export function GifPresentationControls({
|
|||||||
isLoading?: boolean
|
isLoading?: boolean
|
||||||
altText?: string
|
altText?: string
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const largeBadge = useLargeAltBadgeEnabled()
|
const largeBadge = useLargeAltBadgeEnabled()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
label={isPlaying ? _(msg`Pause GIF`) : _(msg`Play GIF`)}
|
label={isPlaying ? l`Pause GIF` : l`Play GIF`}
|
||||||
accessibilityHint={_(msg`Plays or pauses the GIF`)}
|
accessibilityHint={l`Plays or pauses the GIF`}
|
||||||
style={[
|
style={[
|
||||||
a.absolute,
|
a.absolute,
|
||||||
a.align_center,
|
a.align_center,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {type StyleProp, type ViewStyle} from 'react-native'
|
import {type StyleProp, type ViewStyle} from 'react-native'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg, plural} from '@lingui/core/macro'
|
import {plural} from '@lingui/core/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
@@ -18,7 +18,7 @@ export function TimeIndicator({
|
|||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
if (isNaN(time)) {
|
if (isNaN(time)) {
|
||||||
return null
|
return null
|
||||||
@@ -30,12 +30,10 @@ export function TimeIndicator({
|
|||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
pointerEvents="none"
|
pointerEvents="none"
|
||||||
accessibilityLabel={_(
|
accessibilityLabel={l`Time remaining: ${plural(Number(time) || 0, {
|
||||||
msg`Time remaining: ${plural(Number(time) || 0, {
|
one: '# second',
|
||||||
one: '# second',
|
other: '# seconds',
|
||||||
other: '# seconds',
|
})}`}
|
||||||
})}`,
|
|
||||||
)}
|
|
||||||
accessibilityHint=""
|
accessibilityHint=""
|
||||||
style={[
|
style={[
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import {useImperativeHandle, useRef, useState} from 'react'
|
|||||||
import {Pressable, type StyleProp, View, type ViewStyle} from 'react-native'
|
import {Pressable, type StyleProp, View, type ViewStyle} from 'react-native'
|
||||||
import {type AppBskyEmbedVideo} from '@atproto/api'
|
import {type AppBskyEmbedVideo} from '@atproto/api'
|
||||||
import {BlueskyVideoView} from '@bsky.app/video'
|
import {BlueskyVideoView} from '@bsky.app/video'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {HITSLOP_30} from '#/lib/constants'
|
import {HITSLOP_30} from '#/lib/constants'
|
||||||
import {useAutoplayDisabled} from '#/state/preferences'
|
import {useAutoplayDisabled} from '#/state/preferences'
|
||||||
@@ -39,7 +38,7 @@ export function VideoEmbedInnerNative({
|
|||||||
*/
|
*/
|
||||||
onError?: (error: string) => void
|
onError?: (error: string) => void
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const videoRef = useRef<BlueskyVideoView>(null)
|
const videoRef = useRef<BlueskyVideoView>(null)
|
||||||
const autoplayDisabled = useAutoplayDisabled()
|
const autoplayDisabled = useAutoplayDisabled()
|
||||||
const isWithinMessage = useIsWithinMessage()
|
const isWithinMessage = useIsWithinMessage()
|
||||||
@@ -91,9 +90,7 @@ export function VideoEmbedInnerNative({
|
|||||||
setError(e.nativeEvent.error)
|
setError(e.nativeEvent.error)
|
||||||
}}
|
}}
|
||||||
ref={videoRef}
|
ref={videoRef}
|
||||||
accessibilityLabel={
|
accessibilityLabel={embed.alt ? l`Video: ${embed.alt}` : l`Video`}
|
||||||
embed.alt ? _(msg`Video: ${embed.alt}`) : _(msg`Video`)
|
|
||||||
}
|
|
||||||
accessibilityHint=""
|
accessibilityHint=""
|
||||||
/>
|
/>
|
||||||
{isGif ? (
|
{isGif ? (
|
||||||
@@ -144,7 +141,7 @@ function VideoPresentationControls({
|
|||||||
timeRemaining: number
|
timeRemaining: number
|
||||||
isPlaying: boolean
|
isPlaying: boolean
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const [muted] = useVideoMuteState()
|
const [muted] = useVideoMuteState()
|
||||||
|
|
||||||
@@ -159,14 +156,14 @@ function VideoPresentationControls({
|
|||||||
<Pressable
|
<Pressable
|
||||||
onPress={enterFullscreen}
|
onPress={enterFullscreen}
|
||||||
style={a.flex_1}
|
style={a.flex_1}
|
||||||
accessibilityLabel={_(msg`Video`)}
|
accessibilityLabel={l`Video`}
|
||||||
accessibilityHint={_(msg`Enters full screen`)}
|
accessibilityHint={l`Enters full screen`}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
/>
|
/>
|
||||||
<ControlButton
|
<ControlButton
|
||||||
onPress={togglePlayback}
|
onPress={togglePlayback}
|
||||||
label={isPlaying ? _(msg`Pause`) : _(msg`Play`)}
|
label={isPlaying ? l`Pause` : l`Play`}
|
||||||
accessibilityHint={_(msg`Plays or pauses the video`)}
|
accessibilityHint={l`Plays or pauses the video`}
|
||||||
style={{left: 6}}>
|
style={{left: 6}}>
|
||||||
{isPlaying ? (
|
{isPlaying ? (
|
||||||
<PauseIcon width={13} fill={t.palette.white} />
|
<PauseIcon width={13} fill={t.palette.white} />
|
||||||
@@ -175,15 +172,14 @@ function VideoPresentationControls({
|
|||||||
)}
|
)}
|
||||||
</ControlButton>
|
</ControlButton>
|
||||||
{showTime && <TimeIndicator time={timeRemaining} style={{left: 33}} />}
|
{showTime && <TimeIndicator time={timeRemaining} style={{left: 33}} />}
|
||||||
|
|
||||||
<ControlButton
|
<ControlButton
|
||||||
onPress={toggleMuted}
|
onPress={toggleMuted}
|
||||||
label={
|
label={
|
||||||
muted
|
muted
|
||||||
? _(msg({message: `Unmute`, context: 'video'}))
|
? l({message: `Unmute`, context: 'video'})
|
||||||
: _(msg({message: `Mute`, context: 'video'}))
|
: l({message: `Mute`, context: 'video'})
|
||||||
}
|
}
|
||||||
accessibilityHint={_(msg`Toggles the sound`)}
|
accessibilityHint={l`Toggles the sound`}
|
||||||
style={{right: 6}}>
|
style={{right: 6}}>
|
||||||
{muted ? (
|
{muted ? (
|
||||||
<MuteIcon width={13} fill={t.palette.white} />
|
<MuteIcon width={13} fill={t.palette.white} />
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {useCallback, useEffect, useId, useRef, useState} from 'react'
|
import {useCallback, useEffect, useId, useRef, useState} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import type * as HlsTypes from 'hls.js'
|
import type * as HlsTypes from 'hls.js'
|
||||||
|
|
||||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||||
@@ -36,7 +35,7 @@ export function VideoEmbedInnerWeb({
|
|||||||
const [hasSubtitleTrack, setHasSubtitleTrack] = useState(false)
|
const [hasSubtitleTrack, setHasSubtitleTrack] = useState(false)
|
||||||
const [hlsLoading, setHlsLoading] = useState(false)
|
const [hlsLoading, setHlsLoading] = useState(false)
|
||||||
const figId = useId()
|
const figId = useId()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const [isFullscreen] = useFullscreen(containerRef)
|
const [isFullscreen] = useFullscreen(containerRef)
|
||||||
const isGif = embed.presentation === 'gif'
|
const isGif = embed.presentation === 'gif'
|
||||||
|
|
||||||
@@ -63,7 +62,7 @@ export function VideoEmbedInnerWeb({
|
|||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[a.flex_1, a.rounded_md, a.overflow_hidden]}
|
style={[a.flex_1, a.rounded_md, a.overflow_hidden]}
|
||||||
accessibilityLabel={_(msg`Embedded video player`)}
|
accessibilityLabel={l`Embedded video player`}
|
||||||
accessibilityHint="">
|
accessibilityHint="">
|
||||||
<div ref={containerRef} style={{height: '100%', width: '100%'}}>
|
<div ref={containerRef} style={{height: '100%', width: '100%'}}>
|
||||||
<figure style={{margin: 0, position: 'absolute', inset: 0}}>
|
<figure style={{margin: 0, position: 'absolute', inset: 0}}>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
@@ -46,14 +45,14 @@ export function Text({children}: {children: React.ReactNode}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function RetryButton({onPress}: {onPress: () => void}) {
|
export function RetryButton({onPress}: {onPress: () => void}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
size="small"
|
size="small"
|
||||||
color="secondary_inverted"
|
color="secondary_inverted"
|
||||||
label={_(msg`Retry`)}>
|
label={l`Retry`}>
|
||||||
<ButtonIcon icon={ArrowRotateIcon} />
|
<ButtonIcon icon={ArrowRotateIcon} />
|
||||||
<ButtonText>
|
<ButtonText>
|
||||||
<Trans>Retry</Trans>
|
<Trans>Retry</Trans>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {useCallback, useEffect, useRef, useState} from 'react'
|
import {useCallback, useEffect, useRef, useState} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {clamp} from '#/lib/numbers'
|
import {clamp} from '#/lib/numbers'
|
||||||
import {atoms as a, useTheme, web} from '#/alf'
|
import {atoms as a, useTheme, web} from '#/alf'
|
||||||
@@ -30,7 +29,7 @@ export function Scrubber({
|
|||||||
togglePlayPause: () => void
|
togglePlayPause: () => void
|
||||||
drawFocus: () => void
|
drawFocus: () => void
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const [scrubberActive, setScrubberActive] = useState(false)
|
const [scrubberActive, setScrubberActive] = useState(false)
|
||||||
const {
|
const {
|
||||||
@@ -192,16 +191,12 @@ export function Scrubber({
|
|||||||
</View>
|
</View>
|
||||||
<div
|
<div
|
||||||
ref={circleRef}
|
ref={circleRef}
|
||||||
aria-label={_(
|
aria-label={l`Seek slider. Use the arrow keys to seek forwards and backwards, and space to play/pause`}
|
||||||
msg`Seek slider. Use the arrow keys to seek forwards and backwards, and space to play/pause`,
|
|
||||||
)}
|
|
||||||
role="slider"
|
role="slider"
|
||||||
aria-valuemax={duration}
|
aria-valuemax={duration}
|
||||||
aria-valuemin={0}
|
aria-valuemin={0}
|
||||||
aria-valuenow={currentTime}
|
aria-valuenow={currentTime}
|
||||||
aria-valuetext={_(
|
aria-valuetext={l`${formatTime(currentTime)} of ${formatTime(duration)}`}
|
||||||
msg`${formatTime(currentTime)} of ${formatTime(duration)}`,
|
|
||||||
)}
|
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onFocus={onFocus}
|
onFocus={onFocus}
|
||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
|
|||||||
+9
-14
@@ -1,7 +1,6 @@
|
|||||||
import {useCallback, useEffect, useRef, useState} from 'react'
|
import {useCallback, useEffect, useRef, useState} from 'react'
|
||||||
import {Pressable, View} from 'react-native'
|
import {Pressable, View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {clamp} from '#/lib/numbers'
|
import {clamp} from '#/lib/numbers'
|
||||||
@@ -64,7 +63,7 @@ export function Controls({
|
|||||||
canPlay,
|
canPlay,
|
||||||
} = useVideoElement(videoRef)
|
} = useVideoElement(videoRef)
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const subtitlesEnabled = useSubtitlesEnabled()
|
const subtitlesEnabled = useSubtitlesEnabled()
|
||||||
const setSubtitlesEnabled = useSetSubtitlesEnabled()
|
const setSubtitlesEnabled = useSetSubtitlesEnabled()
|
||||||
const {
|
const {
|
||||||
@@ -326,11 +325,7 @@ export function Controls({
|
|||||||
onPointerMove={onPointerMoveEmptySpace}
|
onPointerMove={onPointerMoveEmptySpace}
|
||||||
onPointerLeave={onPointerLeaveEmptySpace}
|
onPointerLeave={onPointerLeaveEmptySpace}
|
||||||
accessibilityLabel={
|
accessibilityLabel={
|
||||||
!focused
|
!focused ? l`Unmute video` : playing ? l`Pause video` : l`Play video`
|
||||||
? _(msg`Unmute video`)
|
|
||||||
: playing
|
|
||||||
? _(msg`Pause video`)
|
|
||||||
: _(msg`Play video`)
|
|
||||||
}
|
}
|
||||||
accessibilityHint=""
|
accessibilityHint=""
|
||||||
style={[
|
style={[
|
||||||
@@ -378,8 +373,8 @@ export function Controls({
|
|||||||
]}>
|
]}>
|
||||||
<ControlButton
|
<ControlButton
|
||||||
active={playing}
|
active={playing}
|
||||||
activeLabel={_(msg`Pause`)}
|
activeLabel={l`Pause`}
|
||||||
inactiveLabel={_(msg`Play`)}
|
inactiveLabel={l`Play`}
|
||||||
activeIcon={PauseIcon}
|
activeIcon={PauseIcon}
|
||||||
inactiveIcon={PlayIcon}
|
inactiveIcon={PlayIcon}
|
||||||
onPress={onPressPlayPause}
|
onPress={onPressPlayPause}
|
||||||
@@ -397,8 +392,8 @@ export function Controls({
|
|||||||
{hasSubtitleTrack && (
|
{hasSubtitleTrack && (
|
||||||
<ControlButton
|
<ControlButton
|
||||||
active={subtitlesEnabled}
|
active={subtitlesEnabled}
|
||||||
activeLabel={_(msg`Disable captions`)}
|
activeLabel={l`Disable captions`}
|
||||||
inactiveLabel={_(msg`Enable captions`)}
|
inactiveLabel={l`Enable captions`}
|
||||||
activeIcon={CCActiveIcon}
|
activeIcon={CCActiveIcon}
|
||||||
inactiveIcon={CCInactiveIcon}
|
inactiveIcon={CCInactiveIcon}
|
||||||
onPress={onPressSubtitles}
|
onPress={onPressSubtitles}
|
||||||
@@ -415,8 +410,8 @@ export function Controls({
|
|||||||
{!IS_WEB_MOBILE_IOS && (
|
{!IS_WEB_MOBILE_IOS && (
|
||||||
<ControlButton
|
<ControlButton
|
||||||
active={isFullscreen}
|
active={isFullscreen}
|
||||||
activeLabel={_(msg`Exit fullscreen`)}
|
activeLabel={l`Exit fullscreen`}
|
||||||
inactiveLabel={_(msg`Enter fullscreen`)}
|
inactiveLabel={l`Enter fullscreen`}
|
||||||
activeIcon={ArrowsInIcon}
|
activeIcon={ArrowsInIcon}
|
||||||
inactiveIcon={ArrowsOutIcon}
|
inactiveIcon={ArrowsOutIcon}
|
||||||
onPress={onPressFullscreen}
|
onPress={onPressFullscreen}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {useCallback} from 'react'
|
import {useCallback} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import Animated, {FadeIn, FadeOut} from 'react-native-reanimated'
|
import Animated, {FadeIn, FadeOut} from 'react-native-reanimated'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {atoms as a} from '#/alf'
|
import {atoms as a} from '#/alf'
|
||||||
import {Mute_Stroke2_Corner0_Rounded as MuteIcon} from '#/components/icons/Mute'
|
import {Mute_Stroke2_Corner0_Rounded as MuteIcon} from '#/components/icons/Mute'
|
||||||
@@ -26,7 +25,7 @@ export function VolumeControl({
|
|||||||
onEndHover: () => void
|
onEndHover: () => void
|
||||||
drawFocus: () => void
|
drawFocus: () => void
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const [volume, setVolume] = useVideoVolumeState()
|
const [volume, setVolume] = useVideoVolumeState()
|
||||||
|
|
||||||
const onVolumeChange = useCallback(
|
const onVolumeChange = useCallback(
|
||||||
@@ -77,7 +76,7 @@ export function VolumeControl({
|
|||||||
min={0}
|
min={0}
|
||||||
max={100}
|
max={100}
|
||||||
value={sliderVolume}
|
value={sliderVolume}
|
||||||
aria-label={_(msg`Volume`)}
|
aria-label={l`Volume`}
|
||||||
style={
|
style={
|
||||||
// Ridiculous safari hack for old version of safari. Fixed in sonoma beta -h
|
// Ridiculous safari hack for old version of safari. Fixed in sonoma beta -h
|
||||||
IS_WEB_SAFARI
|
IS_WEB_SAFARI
|
||||||
@@ -93,8 +92,8 @@ export function VolumeControl({
|
|||||||
)}
|
)}
|
||||||
<ControlButton
|
<ControlButton
|
||||||
active={muted || volume === 0}
|
active={muted || volume === 0}
|
||||||
activeLabel={_(msg({message: `Unmute`, context: 'video'}))}
|
activeLabel={l({message: `Unmute`, context: 'video'})}
|
||||||
inactiveLabel={_(msg({message: `Mute`, context: 'video'}))}
|
inactiveLabel={l({message: `Mute`, context: 'video'})}
|
||||||
activeIcon={MuteIcon}
|
activeIcon={MuteIcon}
|
||||||
inactiveIcon={UnmuteIcon}
|
inactiveIcon={UnmuteIcon}
|
||||||
onPress={onPressMute}
|
onPress={onPressMute}
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import {useCallback, useEffect, useRef, useState} from 'react'
|
|||||||
import {ActivityIndicator, View} from 'react-native'
|
import {ActivityIndicator, View} from 'react-native'
|
||||||
import {ImageBackground} from 'expo-image'
|
import {ImageBackground} from 'expo-image'
|
||||||
import {type AppBskyEmbedVideo} from '@atproto/api'
|
import {type AppBskyEmbedVideo} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -70,7 +69,7 @@ export function VideoEmbed({embed}: Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function InnerWrapper({embed}: Props) {
|
function InnerWrapper({embed}: Props) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const ref = useRef<{togglePlayback: () => void}>(null)
|
const ref = useRef<{togglePlayback: () => void}>(null)
|
||||||
|
|
||||||
@@ -175,7 +174,7 @@ function InnerWrapper({embed}: Props) {
|
|||||||
onPress={() => {
|
onPress={() => {
|
||||||
ref.current?.togglePlayback()
|
ref.current?.togglePlayback()
|
||||||
}}
|
}}
|
||||||
label={_(msg`Play video`)}>
|
label={l`Play video`}>
|
||||||
{showSpinner ? (
|
{showSpinner ? (
|
||||||
<View style={[a.align_center, a.justify_center]}>
|
<View style={[a.align_center, a.justify_center]}>
|
||||||
<ActivityIndicator size="large" color="white" />
|
<ActivityIndicator size="large" color="white" />
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ import {
|
|||||||
} from 'react'
|
} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {type AppBskyEmbedVideo} from '@atproto/api'
|
import {type AppBskyEmbedVideo} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {ErrorBoundary} from '#/view/com/util/ErrorBoundary'
|
import {ErrorBoundary} from '#/view/com/util/ErrorBoundary'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
@@ -288,7 +287,7 @@ function VideoError({
|
|||||||
error: unknown
|
error: unknown
|
||||||
retry: () => void
|
retry: () => void
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
|
|
||||||
let showRetryButton = true
|
let showRetryButton = true
|
||||||
@@ -296,16 +295,14 @@ function VideoError({
|
|||||||
let errorClass: string
|
let errorClass: string
|
||||||
|
|
||||||
if (error instanceof VideoNotFoundError) {
|
if (error instanceof VideoNotFoundError) {
|
||||||
text = _(msg`Video not found.`)
|
text = l`Video not found.`
|
||||||
errorClass = 'VideoNotFoundError'
|
errorClass = 'VideoNotFoundError'
|
||||||
} else if (error instanceof HLSUnsupportedError) {
|
} else if (error instanceof HLSUnsupportedError) {
|
||||||
showRetryButton = false
|
showRetryButton = false
|
||||||
text = _(
|
text = l`This video can’t be played on your device. Your browser or system may be missing the required video codecs (H.264/AAC).`
|
||||||
msg`This video can’t be played on your device. Your browser or system may be missing the required video codecs (H.264/AAC).`,
|
|
||||||
)
|
|
||||||
errorClass = 'HLSUnsupportedError'
|
errorClass = 'HLSUnsupportedError'
|
||||||
} else {
|
} else {
|
||||||
text = _(msg`An error occurred while loading the video. Please try again.`)
|
text = l`An error occurred while loading the video. Please try again.`
|
||||||
if (error instanceof HLSFatalError) {
|
if (error instanceof HLSFatalError) {
|
||||||
errorClass = error.detail
|
errorClass = error.detail
|
||||||
} else if (error instanceof Error) {
|
} else if (error instanceof Error) {
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {memo} from 'react'
|
import {memo} from 'react'
|
||||||
import {type Insets} from 'react-native'
|
import {type Insets} from 'react-native'
|
||||||
import {type AppBskyFeedDefs} from '@atproto/api'
|
import {type AppBskyFeedDefs} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {useCleanError} from '#/lib/hooks/useCleanError'
|
import {useCleanError} from '#/lib/hooks/useCleanError'
|
||||||
@@ -30,7 +29,7 @@ export const BookmarkButton = memo(function BookmarkButton({
|
|||||||
}): React.ReactNode {
|
}): React.ReactNode {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {mutateAsync: bookmark} = useBookmarkMutation()
|
const {mutateAsync: bookmark} = useBookmarkMutation()
|
||||||
const cleanError = useCleanError()
|
const cleanError = useCleanError()
|
||||||
const requireAuth = useRequireAuth()
|
const requireAuth = useRequireAuth()
|
||||||
@@ -39,12 +38,10 @@ export const BookmarkButton = memo(function BookmarkButton({
|
|||||||
const {viewer} = post
|
const {viewer} = post
|
||||||
const isBookmarked = !!viewer?.bookmarked
|
const isBookmarked = !!viewer?.bookmarked
|
||||||
|
|
||||||
const undoLabel = _(
|
const undoLabel = l({
|
||||||
msg({
|
message: `Undo`,
|
||||||
message: `Undo`,
|
context: `Button label to undo saving/removing a post from saved posts.`,
|
||||||
context: `Button label to undo saving/removing a post from saved posts.`,
|
})
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
const save = async () => {
|
const save = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -115,11 +112,7 @@ export const BookmarkButton = memo(function BookmarkButton({
|
|||||||
big={big}
|
big={big}
|
||||||
active={isBookmarked}
|
active={isBookmarked}
|
||||||
activeColor={t.palette.primary_500}
|
activeColor={t.palette.primary_500}
|
||||||
label={
|
label={isBookmarked ? l`Remove from saved posts` : l`Add to saved posts`}
|
||||||
isBookmarked
|
|
||||||
? _(msg`Remove from saved posts`)
|
|
||||||
: _(msg`Add to saved posts`)
|
|
||||||
}
|
|
||||||
onPress={onHandlePress}
|
onPress={onHandlePress}
|
||||||
hitSlop={hitSlop}>
|
hitSlop={hitSlop}>
|
||||||
<PostControlButtonIcon icon={isBookmarked ? BookmarkFilled : Bookmark} />
|
<PostControlButtonIcon icon={isBookmarked ? BookmarkFilled : Bookmark} />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {memo, useCallback} from 'react'
|
import {memo, useCallback} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg, plural} from '@lingui/core/macro'
|
import {plural} from '@lingui/core/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {useHaptics} from '#/lib/haptics'
|
import {useHaptics} from '#/lib/haptics'
|
||||||
@@ -37,7 +37,7 @@ let RepostButton = ({
|
|||||||
embeddingDisabled,
|
embeddingDisabled,
|
||||||
}: Props): React.ReactNode => {
|
}: Props): React.ReactNode => {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const requireAuth = useRequireAuth()
|
const requireAuth = useRequireAuth()
|
||||||
const dialogControl = Dialog.useDialogControl()
|
const dialogControl = Dialog.useDialogControl()
|
||||||
const formatPostStatCount = useFormatPostStatCount()
|
const formatPostStatCount = useFormatPostStatCount()
|
||||||
@@ -64,26 +64,22 @@ let RepostButton = ({
|
|||||||
onLongPress={onLongPress}
|
onLongPress={onLongPress}
|
||||||
label={
|
label={
|
||||||
isReposted
|
isReposted
|
||||||
? _(
|
? l({
|
||||||
msg({
|
message: `Undo repost (${plural(repostCount || 0, {
|
||||||
message: `Undo repost (${plural(repostCount || 0, {
|
one: '# repost',
|
||||||
one: '# repost',
|
other: '# reposts',
|
||||||
other: '# reposts',
|
})})`,
|
||||||
})})`,
|
comment:
|
||||||
comment:
|
'Accessibility label for the repost button when the post has been reposted, verb followed by number of reposts and noun',
|
||||||
'Accessibility label for the repost button when the post has been reposted, verb followed by number of reposts and noun',
|
})
|
||||||
}),
|
: l({
|
||||||
)
|
message: `Repost (${plural(repostCount || 0, {
|
||||||
: _(
|
one: '# repost',
|
||||||
msg({
|
other: '# reposts',
|
||||||
message: `Repost (${plural(repostCount || 0, {
|
})})`,
|
||||||
one: '# repost',
|
comment:
|
||||||
other: '# reposts',
|
'Accessibility label for the repost button when the post has not been reposted, verb form followed by number of reposts and noun form',
|
||||||
})})`,
|
})
|
||||||
comment:
|
|
||||||
'Accessibility label for the repost button when the post has not been reposted, verb form followed by number of reposts and noun form',
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}>
|
}>
|
||||||
<PostControlButtonIcon icon={RepostIcon} />
|
<PostControlButtonIcon icon={RepostIcon} />
|
||||||
{typeof repostCount !== 'undefined' && repostCount > 0 && (
|
{typeof repostCount !== 'undefined' && repostCount > 0 && (
|
||||||
@@ -121,7 +117,7 @@ let RepostButtonDialogInner = ({
|
|||||||
embeddingDisabled: boolean
|
embeddingDisabled: boolean
|
||||||
}): React.ReactNode => {
|
}): React.ReactNode => {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const playHaptic = useHaptics()
|
const playHaptic = useHaptics()
|
||||||
const control = Dialog.useDialogContext()
|
const control = Dialog.useDialogContext()
|
||||||
|
|
||||||
@@ -143,15 +139,15 @@ let RepostButtonDialogInner = ({
|
|||||||
const onPressClose = useCallback(() => control.close(), [control])
|
const onPressClose = useCallback(() => control.close(), [control])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.ScrollableInner label={_(msg`Repost or quote post`)}>
|
<Dialog.ScrollableInner label={l`Repost or quote post`}>
|
||||||
<View style={a.gap_xl}>
|
<View style={a.gap_xl}>
|
||||||
<View style={a.gap_xs}>
|
<View style={a.gap_xs}>
|
||||||
<Button
|
<Button
|
||||||
style={[a.justify_start, a.px_md, a.gap_sm]}
|
style={[a.justify_start, a.px_md, a.gap_sm]}
|
||||||
label={
|
label={
|
||||||
isReposted
|
isReposted
|
||||||
? _(msg`Remove repost`)
|
? l`Remove repost`
|
||||||
: _(msg({message: `Repost`, context: 'action'}))
|
: l({message: `Repost`, context: 'action'})
|
||||||
}
|
}
|
||||||
onPress={onPressRepost}
|
onPress={onPressRepost}
|
||||||
size="large"
|
size="large"
|
||||||
@@ -170,11 +166,7 @@ let RepostButtonDialogInner = ({
|
|||||||
disabled={embeddingDisabled}
|
disabled={embeddingDisabled}
|
||||||
testID="quoteBtn"
|
testID="quoteBtn"
|
||||||
style={[a.justify_start, a.px_md, a.gap_sm]}
|
style={[a.justify_start, a.px_md, a.gap_sm]}
|
||||||
label={
|
label={embeddingDisabled ? l`Quote posts disabled` : l`Quote post`}
|
||||||
embeddingDisabled
|
|
||||||
? _(msg`Quote posts disabled`)
|
|
||||||
: _(msg`Quote post`)
|
|
||||||
}
|
|
||||||
onPress={onPressQuote}
|
onPress={onPressQuote}
|
||||||
size="large"
|
size="large"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@@ -202,7 +194,7 @@ let RepostButtonDialogInner = ({
|
|||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Cancel quote post`)}
|
label={l`Cancel quote post`}
|
||||||
onPress={onPressClose}
|
onPress={onPressClose}
|
||||||
size="large"
|
size="large"
|
||||||
color="secondary">
|
color="secondary">
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {useRequireAuth, useSession} from '#/state/session'
|
import {useRequireAuth, useSession} from '#/state/session'
|
||||||
import {EventStopper} from '#/view/com/util/EventStopper'
|
import {EventStopper} from '#/view/com/util/EventStopper'
|
||||||
@@ -32,7 +31,7 @@ export const RepostButton = ({
|
|||||||
embeddingDisabled,
|
embeddingDisabled,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {hasSession} = useSession()
|
const {hasSession} = useSession()
|
||||||
const requireAuth = useRequireAuth()
|
const requireAuth = useRequireAuth()
|
||||||
const formatPostStatCount = useFormatPostStatCount()
|
const formatPostStatCount = useFormatPostStatCount()
|
||||||
@@ -40,7 +39,7 @@ export const RepostButton = ({
|
|||||||
return hasSession ? (
|
return hasSession ? (
|
||||||
<EventStopper onKeyDown={false}>
|
<EventStopper onKeyDown={false}>
|
||||||
<Menu.Root>
|
<Menu.Root>
|
||||||
<Menu.Trigger label={_(msg`Repost or quote post`)}>
|
<Menu.Trigger label={l`Repost or quote post`}>
|
||||||
{({props}) => {
|
{({props}) => {
|
||||||
return (
|
return (
|
||||||
<PostControlButton
|
<PostControlButton
|
||||||
@@ -64,31 +63,25 @@ export const RepostButton = ({
|
|||||||
<Menu.Item
|
<Menu.Item
|
||||||
label={
|
label={
|
||||||
isReposted
|
isReposted
|
||||||
? _(msg`Undo repost`)
|
? l`Undo repost`
|
||||||
: _(msg({message: `Repost`, context: `action`}))
|
: l({message: `Repost`, context: `action`})
|
||||||
}
|
}
|
||||||
testID="repostDropdownRepostBtn"
|
testID="repostDropdownRepostBtn"
|
||||||
onPress={onRepost}>
|
onPress={onRepost}>
|
||||||
<Menu.ItemText>
|
<Menu.ItemText>
|
||||||
{isReposted
|
{isReposted
|
||||||
? _(msg`Undo repost`)
|
? l`Undo repost`
|
||||||
: _(msg({message: `Repost`, context: `action`}))}
|
: l({message: `Repost`, context: `action`})}
|
||||||
</Menu.ItemText>
|
</Menu.ItemText>
|
||||||
<Menu.ItemIcon icon={Repost} position="right" />
|
<Menu.ItemIcon icon={Repost} position="right" />
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
disabled={embeddingDisabled}
|
disabled={embeddingDisabled}
|
||||||
label={
|
label={embeddingDisabled ? l`Quote posts disabled` : l`Quote post`}
|
||||||
embeddingDisabled
|
|
||||||
? _(msg`Quote posts disabled`)
|
|
||||||
: _(msg`Quote post`)
|
|
||||||
}
|
|
||||||
testID="repostDropdownQuoteBtn"
|
testID="repostDropdownQuoteBtn"
|
||||||
onPress={onQuote}>
|
onPress={onQuote}>
|
||||||
<Menu.ItemText>
|
<Menu.ItemText>
|
||||||
{embeddingDisabled
|
{embeddingDisabled ? l`Quote posts disabled` : l`Quote post`}
|
||||||
? _(msg`Quote posts disabled`)
|
|
||||||
: _(msg`Quote post`)}
|
|
||||||
</Menu.ItemText>
|
</Menu.ItemText>
|
||||||
<Menu.ItemIcon icon={Quote} position="right" />
|
<Menu.ItemIcon icon={Quote} position="right" />
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
@@ -100,7 +93,7 @@ export const RepostButton = ({
|
|||||||
onPress={() => requireAuth(() => {})}
|
onPress={() => requireAuth(() => {})}
|
||||||
active={isReposted}
|
active={isReposted}
|
||||||
activeColor={t.palette.positive_500}
|
activeColor={t.palette.positive_500}
|
||||||
label={_(msg`Repost or quote post`)}
|
label={l`Repost or quote post`}
|
||||||
big={big}>
|
big={big}>
|
||||||
<PostControlButtonIcon icon={Repost} />
|
<PostControlButtonIcon icon={Repost} />
|
||||||
{typeof repostCount !== 'undefined' && repostCount > 0 && (
|
{typeof repostCount !== 'undefined' && repostCount > 0 && (
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ import {
|
|||||||
moderateProfile,
|
moderateProfile,
|
||||||
type ModerationOpts,
|
type ModerationOpts,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
@@ -118,7 +117,7 @@ function RecentChatItem({
|
|||||||
convo: ConvoWithDetails
|
convo: ConvoWithDetails
|
||||||
primaryMember: ChatBskyActorDefs.ProfileViewBasic
|
primaryMember: ChatBskyActorDefs.ProfileViewBasic
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
|
||||||
const primaryProfile = useProfileShadow(primaryMember)
|
const primaryProfile = useProfileShadow(primaryMember)
|
||||||
@@ -143,7 +142,7 @@ function RecentChatItem({
|
|||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
label={_(msg`Send post to ${name}`)}
|
label={l`Send post to ${name}`}
|
||||||
style={[
|
style={[
|
||||||
a.flex_col,
|
a.flex_col,
|
||||||
{width: WIDTH},
|
{width: WIDTH},
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ import {
|
|||||||
AtUri,
|
AtUri,
|
||||||
type RichText as RichTextAPI,
|
type RichText as RichTextAPI,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {makeProfileLink} from '#/lib/routes/links'
|
import {makeProfileLink} from '#/lib/routes/links'
|
||||||
import {shareUrl} from '#/lib/sharing'
|
import {shareUrl} from '#/lib/sharing'
|
||||||
@@ -48,7 +47,7 @@ let ShareMenuButton = ({
|
|||||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
||||||
}): React.ReactNode => {
|
}): React.ReactNode => {
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {feedDescriptor} = useFeedFeedbackContext()
|
const {feedDescriptor} = useFeedFeedbackContext()
|
||||||
|
|
||||||
const menuControl = useMenuControl()
|
const menuControl = useMenuControl()
|
||||||
@@ -95,7 +94,7 @@ let ShareMenuButton = ({
|
|||||||
return (
|
return (
|
||||||
<EventStopper onKeyDown={false}>
|
<EventStopper onKeyDown={false}>
|
||||||
<Menu.Root control={lazyMenuControl}>
|
<Menu.Root control={lazyMenuControl}>
|
||||||
<Menu.Trigger label={_(msg`Open share menu`)}>
|
<Menu.Trigger label={l`Open share menu`}>
|
||||||
{({props}) => {
|
{({props}) => {
|
||||||
return (
|
return (
|
||||||
<PostControlButton
|
<PostControlButton
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {useCallback} from 'react'
|
import {useCallback} from 'react'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This matches `formatCount` from `view/com/util/numeric/format.ts`, but has
|
* This matches `formatCount` from `view/com/util/numeric/format.ts`, but has
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import {
|
|||||||
type ModerationOpts,
|
type ModerationOpts,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {flip, offset, shift, size, useFloating} from '@floating-ui/react-dom'
|
import {flip, offset, shift, size, useFloating} from '@floating-ui/react-dom'
|
||||||
import {msg, plural} from '@lingui/core/macro'
|
import {plural} from '@lingui/core/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
import {getModerationCauseKey} from '#/lib/moderation'
|
import {getModerationCauseKey} from '#/lib/moderation'
|
||||||
@@ -423,7 +423,7 @@ function Inner({
|
|||||||
hide: () => void
|
hide: () => void
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_, i18n} = useLingui()
|
const {t: l, i18n} = useLingui()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const moderation = useMemo(
|
const moderation = useMemo(
|
||||||
() => moderateProfile(profile, moderationOpts),
|
() => moderateProfile(profile, moderationOpts),
|
||||||
@@ -462,7 +462,7 @@ function Inner({
|
|||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<View style={[a.flex_row, a.justify_between, a.align_start]}>
|
<View style={[a.flex_row, a.justify_between, a.align_start]}>
|
||||||
<Link to={profileURL} label={_(msg`View profile`)} onPress={hide}>
|
<Link to={profileURL} label={l`View profile`} onPress={hide}>
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
size={64}
|
size={64}
|
||||||
avatar={profile.avatar}
|
avatar={profile.avatar}
|
||||||
@@ -476,24 +476,20 @@ function Inner({
|
|||||||
(isBlockedUser ? (
|
(isBlockedUser ? (
|
||||||
<Link
|
<Link
|
||||||
to={profileURL}
|
to={profileURL}
|
||||||
label={_(msg`View blocked user's profile`)}
|
label={l`View blocked user's profile`}
|
||||||
onPress={hide}
|
onPress={hide}
|
||||||
size="small"
|
size="small"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
style={[a.rounded_full]}>
|
style={[a.rounded_full]}>
|
||||||
<ButtonText>{_(msg`View profile`)}</ButtonText>
|
<ButtonText>{l`View profile`}</ButtonText>
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
color={profileShadow.viewer?.following ? 'secondary' : 'primary'}
|
color={profileShadow.viewer?.following ? 'secondary' : 'primary'}
|
||||||
variant="solid"
|
variant="solid"
|
||||||
label={
|
label={profileShadow.viewer?.following ? l`Following` : l`Follow`}
|
||||||
profileShadow.viewer?.following
|
|
||||||
? _(msg`Following`)
|
|
||||||
: _(msg`Follow`)
|
|
||||||
}
|
|
||||||
style={[a.rounded_full]}
|
style={[a.rounded_full]}
|
||||||
onPress={profileShadow.viewer?.following ? unfollow : follow}>
|
onPress={profileShadow.viewer?.following ? unfollow : follow}>
|
||||||
<ButtonIcon
|
<ButtonIcon
|
||||||
@@ -501,15 +497,12 @@ function Inner({
|
|||||||
icon={profileShadow.viewer?.following ? Check : Plus}
|
icon={profileShadow.viewer?.following ? Check : Plus}
|
||||||
/>
|
/>
|
||||||
<ButtonText>
|
<ButtonText>
|
||||||
{profileShadow.viewer?.following
|
{profileShadow.viewer?.following ? l`Following` : l`Follow`}
|
||||||
? _(msg`Following`)
|
|
||||||
: _(msg`Follow`)}
|
|
||||||
</ButtonText>
|
</ButtonText>
|
||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
|
<Link to={profileURL} label={l`View profile`} onPress={hide}>
|
||||||
<Link to={profileURL} label={_(msg`View profile`)} onPress={hide}>
|
|
||||||
<View style={[a.pb_sm, a.flex_1]}>
|
<View style={[a.pb_sm, a.flex_1]}>
|
||||||
<View style={[a.flex_row, a.align_center, a.pt_md, a.pb_xs]}>
|
<View style={[a.flex_row, a.align_center, a.pt_md, a.pb_xs]}>
|
||||||
<Text
|
<Text
|
||||||
@@ -540,7 +533,6 @@ function Inner({
|
|||||||
<ProfileHeaderHandle profile={profileShadow} disableTaps />
|
<ProfileHeaderHandle profile={profileShadow} disableTaps />
|
||||||
</View>
|
</View>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{isBlockedUser && (
|
{isBlockedUser && (
|
||||||
<View style={[a.flex_row, a.flex_wrap, a.gap_xs]}>
|
<View style={[a.flex_row, a.flex_wrap, a.gap_xs]}>
|
||||||
{moderation.ui('profileView').alerts.map(cause => (
|
{moderation.ui('profileView').alerts.map(cause => (
|
||||||
@@ -553,7 +545,6 @@ function Inner({
|
|||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!isBlockedUser && (
|
{!isBlockedUser && (
|
||||||
<>
|
<>
|
||||||
<View style={[a.flex_row, a.flex_wrap, a.gap_md, a.pt_xs]}>
|
<View style={[a.flex_row, a.flex_wrap, a.gap_md, a.pt_xs]}>
|
||||||
@@ -569,7 +560,7 @@ function Inner({
|
|||||||
</InlineLinkText>
|
</InlineLinkText>
|
||||||
<InlineLinkText
|
<InlineLinkText
|
||||||
to={makeProfileLink(profile, 'follows')}
|
to={makeProfileLink(profile, 'follows')}
|
||||||
label={_(msg`${following} following`)}
|
label={l`${following} following`}
|
||||||
style={[t.atoms.text]}
|
style={[t.atoms.text]}
|
||||||
onPress={hide}>
|
onPress={hide}>
|
||||||
<Text style={[a.text_md, a.font_semi_bold]}>{following} </Text>
|
<Text style={[a.text_md, a.font_semi_bold]}>{following} </Text>
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ import Animated, {
|
|||||||
} from 'react-native-reanimated'
|
} from 'react-native-reanimated'
|
||||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||||
import {scheduleOnRN} from 'react-native-worklets'
|
import {scheduleOnRN} from 'react-native-worklets'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {Portal} from '#/components/Portal'
|
import {Portal} from '#/components/Portal'
|
||||||
@@ -40,7 +39,7 @@ export const ProgressGuideToast = forwardRef<
|
|||||||
ProgressGuideToastProps
|
ProgressGuideToastProps
|
||||||
>(function ProgressGuideToast({title, subtitle, visibleDuration}, ref) {
|
>(function ProgressGuideToast({title, subtitle, visibleDuration}, ref) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const insets = useSafeAreaInsets()
|
const insets = useSafeAreaInsets()
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
const translateY = useSharedValue(0)
|
const translateY = useSharedValue(0)
|
||||||
@@ -156,7 +155,7 @@ export const ProgressGuideToast = forwardRef<
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
onPress={close}
|
onPress={close}
|
||||||
accessibilityLabel={_(msg`Tap to dismiss`)}
|
accessibilityLabel={l`Tap to dismiss`}
|
||||||
accessibilityHint="">
|
accessibilityHint="">
|
||||||
<AnimatedCheck
|
<AnimatedCheck
|
||||||
fill={t.palette.primary_500}
|
fill={t.palette.primary_500}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {useMemo} 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 {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
@@ -35,7 +34,7 @@ export function RichTextTag({
|
|||||||
authorHandle?: string
|
authorHandle?: string
|
||||||
textStyle: StyleProp<TextStyle>
|
textStyle: StyleProp<TextStyle>
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {isLoading: isPreferencesLoading, data: preferences} =
|
const {isLoading: isPreferencesLoading, data: preferences} =
|
||||||
usePreferencesQuery()
|
usePreferencesQuery()
|
||||||
const {
|
const {
|
||||||
@@ -50,10 +49,10 @@ export function RichTextTag({
|
|||||||
} = useRemoveMutedWordsMutation()
|
} = useRemoveMutedWordsMutation()
|
||||||
const navigation = useNavigation<NavigationProp>()
|
const navigation = useNavigation<NavigationProp>()
|
||||||
const isCashtag = tag.startsWith('$')
|
const isCashtag = tag.startsWith('$')
|
||||||
const label = isCashtag ? _(msg`Cashtag ${tag}`) : _(msg`Hashtag ${tag}`)
|
const label = isCashtag ? l`Cashtag ${tag}` : l`Hashtag ${tag}`
|
||||||
const hint = IS_NATIVE
|
const hint = IS_NATIVE
|
||||||
? _(msg`Long press to open tag menu for ${isCashtag ? tag : `#${tag}`}`)
|
? l`Long press to open tag menu for ${isCashtag ? tag : `#${tag}`}`
|
||||||
: _(msg`Click to open tag menu for ${isCashtag ? tag : `#${tag}`}`)
|
: l`Click to open tag menu for ${isCashtag ? tag : `#${tag}`}`
|
||||||
|
|
||||||
const isMuted = Boolean(
|
const isMuted = Boolean(
|
||||||
(preferences?.moderationPrefs.mutedWords?.find(
|
(preferences?.moderationPrefs.mutedWords?.find(
|
||||||
@@ -111,7 +110,7 @@ export function RichTextTag({
|
|||||||
<Menu.Outer>
|
<Menu.Outer>
|
||||||
<Menu.Group>
|
<Menu.Group>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
label={_(msg`See ${isCashtag ? tag : `#${tag}`} posts`)}
|
label={l`See ${isCashtag ? tag : `#${tag}`} posts`}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
navigation.push('Hashtag', {
|
navigation.push('Hashtag', {
|
||||||
tag: encodeURIComponent(tag),
|
tag: encodeURIComponent(tag),
|
||||||
@@ -128,7 +127,7 @@ export function RichTextTag({
|
|||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
{authorHandle && !isInvalidHandle(authorHandle) && (
|
{authorHandle && !isInvalidHandle(authorHandle) && (
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
label={_(msg`See ${isCashtag ? tag : `#${tag}`} posts by user`)}
|
label={l`See ${isCashtag ? tag : `#${tag}`} posts by user`}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
navigation.push('Hashtag', {
|
navigation.push('Hashtag', {
|
||||||
tag: encodeURIComponent(tag),
|
tag: encodeURIComponent(tag),
|
||||||
@@ -150,8 +149,8 @@ export function RichTextTag({
|
|||||||
<Menu.Item
|
<Menu.Item
|
||||||
label={
|
label={
|
||||||
isMuted
|
isMuted
|
||||||
? _(msg`Unmute ${isCashtag ? tag : `#${tag}`}`)
|
? l`Unmute ${isCashtag ? tag : `#${tag}`}`
|
||||||
: _(msg`Mute ${isCashtag ? tag : `#${tag}`}`)
|
: l`Mute ${isCashtag ? tag : `#${tag}`}`
|
||||||
}
|
}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
if (isMuted) {
|
if (isMuted) {
|
||||||
@@ -166,8 +165,8 @@ export function RichTextTag({
|
|||||||
}}>
|
}}>
|
||||||
<Menu.ItemText>
|
<Menu.ItemText>
|
||||||
{isMuted
|
{isMuted
|
||||||
? _(msg`Unmute ${isCashtag ? tag : `#${tag}`}`)
|
? l`Unmute ${isCashtag ? tag : `#${tag}`}`
|
||||||
: _(msg`Mute ${isCashtag ? tag : `#${tag}`}`)}
|
: l`Mute ${isCashtag ? tag : `#${tag}`}`}
|
||||||
</Menu.ItemText>
|
</Menu.ItemText>
|
||||||
<Menu.ItemIcon icon={isPreferencesLoading ? Loader : Mute} />
|
<Menu.ItemIcon icon={isPreferencesLoading ? Loader : Mute} />
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||||
@@ -170,7 +169,7 @@ function ContentInner<T>({
|
|||||||
valueExtractor,
|
valueExtractor,
|
||||||
...context
|
...context
|
||||||
}: ContentProps<T> & ContextType) {
|
}: ContentProps<T> & ContextType) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const [headerHeight, setHeaderHeight] = useState(61)
|
const [headerHeight, setHeaderHeight] = useState(61)
|
||||||
|
|
||||||
const render = useCallback(
|
const render = useCallback(
|
||||||
@@ -196,7 +195,7 @@ function ContentInner<T>({
|
|||||||
]}>
|
]}>
|
||||||
<Dialog.HeaderText
|
<Dialog.HeaderText
|
||||||
style={[a.flex_1, a.px_xl, a.text_left, a.font_bold, a.text_2xl]}>
|
style={[a.flex_1, a.px_xl, a.text_left, a.font_bold, a.text_2xl]}>
|
||||||
{label ?? _(msg`Select an option`)}
|
{label ?? l`Select an option`}
|
||||||
</Dialog.HeaderText>
|
</Dialog.HeaderText>
|
||||||
</Dialog.Header>
|
</Dialog.Header>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {forwardRef, useCallback, useImperativeHandle} from 'react'
|
import {forwardRef, useCallback, useImperativeHandle} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {type FeedDescriptor} from '#/state/queries/post-feed'
|
import {type FeedDescriptor} from '#/state/queries/post-feed'
|
||||||
import {PostFeed} from '#/view/com/posts/PostFeed'
|
import {PostFeed} from '#/view/com/posts/PostFeed'
|
||||||
@@ -20,7 +19,7 @@ interface ProfilesListProps {
|
|||||||
export const PostsList = forwardRef<SectionRef, ProfilesListProps>(
|
export const PostsList = forwardRef<SectionRef, ProfilesListProps>(
|
||||||
function PostsListImpl({listUri, headerHeight, scrollElRef}, ref) {
|
function PostsListImpl({listUri, headerHeight, scrollElRef}, ref) {
|
||||||
const feed: FeedDescriptor = `list|${listUri}`
|
const feed: FeedDescriptor = `list|${listUri}`
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
const onScrollToTop = useCallback(() => {
|
const onScrollToTop = useCallback(() => {
|
||||||
scrollElRef.current?.scrollToOffset({
|
scrollElRef.current?.scrollToOffset({
|
||||||
@@ -38,10 +37,10 @@ export const PostsList = forwardRef<SectionRef, ProfilesListProps>(
|
|||||||
<EmptyState
|
<EmptyState
|
||||||
icon={HashtagWideIcon}
|
icon={HashtagWideIcon}
|
||||||
iconSize="2xl"
|
iconSize="2xl"
|
||||||
message={_(msg`This feed is empty.`)}
|
message={l`This feed is empty.`}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}, [_])
|
}, [l])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ import {
|
|||||||
type ViewStyle,
|
type ViewStyle,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {type AppBskyGraphDefs} from '@atproto/api'
|
import {type AppBskyGraphDefs} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
@@ -90,7 +89,7 @@ export function ProfileStarterPacks({
|
|||||||
const {isTabletOrDesktop} = useWebMediaQueries()
|
const {isTabletOrDesktop} = useWebMediaQueries()
|
||||||
|
|
||||||
const items = data?.pages.flatMap(page => page.starterPacks)
|
const items = data?.pages.flatMap(page => page.starterPacks)
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
const EmptyComponent = useCallback(() => {
|
const EmptyComponent = useCallback(() => {
|
||||||
if (emptyStateMessage || emptyStateButton || emptyStateIcon) {
|
if (emptyStateMessage || emptyStateButton || emptyStateIcon) {
|
||||||
@@ -101,9 +100,7 @@ export function ProfileStarterPacks({
|
|||||||
iconSize="3xl"
|
iconSize="3xl"
|
||||||
message={
|
message={
|
||||||
emptyStateMessage ??
|
emptyStateMessage ??
|
||||||
_(
|
l`Starter packs let you share your favorite feeds and people with your friends.`
|
||||||
msg`Starter packs let you share your favorite feeds and people with your friends.`,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
button={emptyStateButton}
|
button={emptyStateButton}
|
||||||
/>
|
/>
|
||||||
@@ -111,7 +108,7 @@ export function ProfileStarterPacks({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
return <Empty />
|
return <Empty />
|
||||||
}, [_, emptyStateMessage, emptyStateButton, emptyStateIcon])
|
}, [l, emptyStateMessage, emptyStateButton, emptyStateIcon])
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
scrollToTop: () => {},
|
scrollToTop: () => {},
|
||||||
@@ -193,7 +190,7 @@ export function ProfileStarterPacks({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function CreateAnother() {
|
function CreateAnother() {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const navigation = useNavigation<NavigationProp>()
|
const navigation = useNavigation<NavigationProp>()
|
||||||
|
|
||||||
@@ -207,7 +204,7 @@ function CreateAnother() {
|
|||||||
t.atoms.border_contrast_low,
|
t.atoms.border_contrast_low,
|
||||||
]}>
|
]}>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Create a starter pack`)}
|
label={l`Create a starter pack`}
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -223,7 +220,7 @@ function CreateAnother() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Empty() {
|
function Empty() {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const navigation = useNavigation<NavigationProp>()
|
const navigation = useNavigation<NavigationProp>()
|
||||||
const confirmDialogControl = useDialogControl()
|
const confirmDialogControl = useDialogControl()
|
||||||
const followersDialogControl = useDialogControl()
|
const followersDialogControl = useDialogControl()
|
||||||
@@ -303,7 +300,7 @@ function Empty() {
|
|||||||
</View>
|
</View>
|
||||||
<View style={[a.flex_row, a.gap_md, {marginLeft: 'auto'}]}>
|
<View style={[a.flex_row, a.gap_md, {marginLeft: 'auto'}]}>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Create a starter pack for me`)}
|
label={l`Create a starter pack for me`}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
color="primary"
|
color="primary"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -316,7 +313,7 @@ function Empty() {
|
|||||||
{isGenerating && <Loader size="md" />}
|
{isGenerating && <Loader size="md" />}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Create a starter pack`)}
|
label={l`Create a starter pack`}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
color="primary"
|
color="primary"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -333,7 +330,6 @@ function Empty() {
|
|||||||
</ButtonText>
|
</ButtonText>
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Prompt.Outer control={confirmDialogControl}>
|
<Prompt.Outer control={confirmDialogControl}>
|
||||||
<Prompt.Content>
|
<Prompt.Content>
|
||||||
<Prompt.TitleText>
|
<Prompt.TitleText>
|
||||||
@@ -349,12 +345,12 @@ function Empty() {
|
|||||||
<Prompt.Actions>
|
<Prompt.Actions>
|
||||||
<Prompt.Action
|
<Prompt.Action
|
||||||
color="primary"
|
color="primary"
|
||||||
cta={_(msg`Choose for me`)}
|
cta={l`Choose for me`}
|
||||||
onPress={generate}
|
onPress={generate}
|
||||||
/>
|
/>
|
||||||
<Prompt.Action
|
<Prompt.Action
|
||||||
color="secondary"
|
color="secondary"
|
||||||
cta={_(msg`Let me choose`)}
|
cta={l`Let me choose`}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
navigation.navigate('StarterPackWizard', {})
|
navigation.navigate('StarterPackWizard', {})
|
||||||
}}
|
}}
|
||||||
@@ -363,21 +359,17 @@ function Empty() {
|
|||||||
</Prompt.Outer>
|
</Prompt.Outer>
|
||||||
<Prompt.Basic
|
<Prompt.Basic
|
||||||
control={followersDialogControl}
|
control={followersDialogControl}
|
||||||
title={_(msg`Oops!`)}
|
title={l`Oops!`}
|
||||||
description={_(
|
description={l`You must be following at least seven other people to generate a starter pack.`}
|
||||||
msg`You must be following at least seven other people to generate a starter pack.`,
|
|
||||||
)}
|
|
||||||
onConfirm={() => {}}
|
onConfirm={() => {}}
|
||||||
showCancel={false}
|
showCancel={false}
|
||||||
/>
|
/>
|
||||||
<Prompt.Basic
|
<Prompt.Basic
|
||||||
control={errorDialogControl}
|
control={errorDialogControl}
|
||||||
title={_(msg`Oops!`)}
|
title={l`Oops!`}
|
||||||
description={_(
|
description={l`An error occurred while generating your starter pack. Want to try again?`}
|
||||||
msg`An error occurred while generating your starter pack. Want to try again?`,
|
|
||||||
)}
|
|
||||||
onConfirm={generate}
|
onConfirm={generate}
|
||||||
confirmButtonCta={_(msg`Retry`)}
|
confirmButtonCta={l`Retry`}
|
||||||
/>
|
/>
|
||||||
</LinearGradientBackground>
|
</LinearGradientBackground>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ import type ViewShot from 'react-native-view-shot'
|
|||||||
import {requestPermissionsAsync, saveToLibraryAsync} from 'expo-media-library'
|
import {requestPermissionsAsync, saveToLibraryAsync} from 'expo-media-library'
|
||||||
import * as Sharing from 'expo-sharing'
|
import * as Sharing from 'expo-sharing'
|
||||||
import {type AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
|
import {type AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
@@ -32,7 +31,7 @@ export function QrCodeDialog({
|
|||||||
link?: string
|
link?: string
|
||||||
control: DialogControlProps
|
control: DialogControlProps
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
const [isSaveProcessing, setIsSaveProcessing] = useState(false)
|
const [isSaveProcessing, setIsSaveProcessing] = useState(false)
|
||||||
@@ -65,9 +64,7 @@ export function QrCodeDialog({
|
|||||||
|
|
||||||
if (!res.granted) {
|
if (!res.granted) {
|
||||||
Toast.show(
|
Toast.show(
|
||||||
_(
|
l`You must grant access to your photo library to save a QR code`,
|
||||||
msg`You must grant access to your photo library to save a QR code`,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -78,7 +75,7 @@ export function QrCodeDialog({
|
|||||||
// works with the add-only permission on iOS (APP-2374)
|
// works with the add-only permission on iOS (APP-2374)
|
||||||
await saveToLibraryAsync(`file://${uri}`)
|
await saveToLibraryAsync(`file://${uri}`)
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
Toast.show(_(msg`An error occurred while saving the QR code!`), {
|
Toast.show(l`An error occurred while saving the QR code!`, {
|
||||||
type: 'error',
|
type: 'error',
|
||||||
})
|
})
|
||||||
logger.error('Failed to save QR code', {error: e})
|
logger.error('Failed to save QR code', {error: e})
|
||||||
@@ -118,8 +115,8 @@ export function QrCodeDialog({
|
|||||||
setIsSaveProcessing(false)
|
setIsSaveProcessing(false)
|
||||||
Toast.show(
|
Toast.show(
|
||||||
IS_WEB
|
IS_WEB
|
||||||
? _(msg`QR code has been downloaded!`)
|
? l`QR code has been downloaded!`
|
||||||
: _(msg`QR code saved to your camera roll!`),
|
: l`QR code saved to your camera roll!`,
|
||||||
)
|
)
|
||||||
control.close()
|
control.close()
|
||||||
})
|
})
|
||||||
@@ -140,7 +137,7 @@ export function QrCodeDialog({
|
|||||||
shareType: 'qrcode',
|
shareType: 'qrcode',
|
||||||
qrShareType: 'copy',
|
qrShareType: 'copy',
|
||||||
})
|
})
|
||||||
Toast.show(_(msg`QR code copied to your clipboard!`))
|
Toast.show(l`QR code copied to your clipboard!`)
|
||||||
setIsCopyProcessing(false)
|
setIsCopyProcessing(false)
|
||||||
control.close()
|
control.close()
|
||||||
})
|
})
|
||||||
@@ -165,8 +162,7 @@ export function QrCodeDialog({
|
|||||||
return (
|
return (
|
||||||
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
|
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner label={l`Create a QR code for a starter pack`}>
|
||||||
label={_(msg`Create a QR code for a starter pack`)}>
|
|
||||||
<View style={[a.flex_1, a.align_center, a.gap_5xl]}>
|
<View style={[a.flex_1, a.align_center, a.gap_5xl]}>
|
||||||
<Suspense fallback={<Loading />}>
|
<Suspense fallback={<Loading />}>
|
||||||
{!link ? (
|
{!link ? (
|
||||||
@@ -181,7 +177,7 @@ export function QrCodeDialog({
|
|||||||
gtMobile && [a.flex_row, a.justify_center, a.flex_wrap],
|
gtMobile && [a.flex_row, a.justify_center, a.flex_wrap],
|
||||||
]}>
|
]}>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Copy QR code`)}
|
label={l`Copy QR code`}
|
||||||
color="primary_subtle"
|
color="primary_subtle"
|
||||||
size="large"
|
size="large"
|
||||||
onPress={IS_WEB ? onCopyPress : onSharePress}>
|
onPress={IS_WEB ? onCopyPress : onSharePress}>
|
||||||
@@ -199,7 +195,7 @@ export function QrCodeDialog({
|
|||||||
</ButtonText>
|
</ButtonText>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Save QR code`)}
|
label={l`Save QR code`}
|
||||||
color="secondary"
|
color="secondary"
|
||||||
size="large"
|
size="large"
|
||||||
onPress={onSavePress}>
|
onPress={onSavePress}>
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {Image} from 'expo-image'
|
import {Image} from 'expo-image'
|
||||||
import {type AppBskyGraphDefs} from '@atproto/api'
|
import {type AppBskyGraphDefs} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {useSaveImageToMediaLibrary} from '#/lib/media/save-image'
|
import {useSaveImageToMediaLibrary} from '#/lib/media/save-image'
|
||||||
@@ -46,7 +45,7 @@ function ShareDialogInner({
|
|||||||
qrDialogControl,
|
qrDialogControl,
|
||||||
control,
|
control,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
@@ -71,7 +70,7 @@ function ShareDialogInner({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Dialog.ScrollableInner label={_(msg`Share link dialog`)}>
|
<Dialog.ScrollableInner label={l`Share link dialog`}>
|
||||||
{!imageLoaded || !link ? (
|
{!imageLoaded || !link ? (
|
||||||
<View style={[a.align_center, a.justify_center, {minHeight: 350}]}>
|
<View style={[a.align_center, a.justify_center, {minHeight: 350}]}>
|
||||||
<Loader size="xl" />
|
<Loader size="xl" />
|
||||||
@@ -112,7 +111,7 @@ function ShareDialogInner({
|
|||||||
],
|
],
|
||||||
]}>
|
]}>
|
||||||
<Button
|
<Button
|
||||||
label={IS_WEB ? _(msg`Copy link`) : _(msg`Share link`)}
|
label={IS_WEB ? l`Copy link` : l`Share link`}
|
||||||
color="primary_subtle"
|
color="primary_subtle"
|
||||||
size="large"
|
size="large"
|
||||||
onPress={onShareLink}>
|
onPress={onShareLink}>
|
||||||
@@ -126,7 +125,7 @@ function ShareDialogInner({
|
|||||||
</ButtonText>
|
</ButtonText>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Share QR code`)}
|
label={l`Share QR code`}
|
||||||
color="primary_subtle"
|
color="primary_subtle"
|
||||||
size="large"
|
size="large"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
@@ -141,7 +140,7 @@ function ShareDialogInner({
|
|||||||
</Button>
|
</Button>
|
||||||
{IS_NATIVE && (
|
{IS_NATIVE && (
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Save image`)}
|
label={l`Save image`}
|
||||||
color="secondary"
|
color="secondary"
|
||||||
size="large"
|
size="large"
|
||||||
onPress={onSave}>
|
onPress={onSave}>
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ 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'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Plural, Trans} from '@lingui/react/macro'
|
import {Plural, Trans} from '@lingui/react/macro'
|
||||||
import {useQueryClient} from '@tanstack/react-query'
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
@@ -58,7 +57,7 @@ export function Card({
|
|||||||
}) {
|
}) {
|
||||||
const {record, creator, joinedAllTimeCount} = starterPack
|
const {record, creator, joinedAllTimeCount} = starterPack
|
||||||
|
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
|
|
||||||
@@ -87,8 +86,8 @@ export function Card({
|
|||||||
style={[a.leading_snug, t.atoms.text_contrast_medium]}
|
style={[a.leading_snug, t.atoms.text_contrast_medium]}
|
||||||
numberOfLines={1}>
|
numberOfLines={1}>
|
||||||
{creator?.did === currentAccount?.did
|
{creator?.did === currentAccount?.did
|
||||||
? _(msg`Starter pack by you`)
|
? l`Starter pack by you`
|
||||||
: _(msg`Starter pack by ${sanitizeHandle(creator.handle, '@')}`)}
|
: l`Starter pack by ${sanitizeHandle(creator.handle, '@')}`}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -113,7 +112,7 @@ export function useStarterPackLink({
|
|||||||
}: {
|
}: {
|
||||||
view: bsky.starterPack.AnyStarterPackView
|
view: bsky.starterPack.AnyStarterPackView
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const qc = useQueryClient()
|
const qc = useQueryClient()
|
||||||
const {rkey, handleOrDid} = useMemo(() => {
|
const {rkey, handleOrDid} = useMemo(() => {
|
||||||
const rkey = new AtUri(view.uri).rkey
|
const rkey = new AtUri(view.uri).rkey
|
||||||
@@ -131,8 +130,8 @@ export function useStarterPackLink({
|
|||||||
view.record,
|
view.record,
|
||||||
AppBskyGraphStarterpack.isRecord,
|
AppBskyGraphStarterpack.isRecord,
|
||||||
)
|
)
|
||||||
? _(msg`Navigate to ${view.record.name}`)
|
? l`Navigate to ${view.record.name}`
|
||||||
: _(msg`Navigate to starter pack`),
|
: l`Navigate to starter pack`,
|
||||||
precache,
|
precache,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,7 +144,7 @@ export function Link({
|
|||||||
onPress?: () => void
|
onPress?: () => void
|
||||||
children: BaseLinkProps['children']
|
children: BaseLinkProps['children']
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const {record} = starterPack
|
const {record} = starterPack
|
||||||
const {rkey, handleOrDid} = useMemo(() => {
|
const {rkey, handleOrDid} = useMemo(() => {
|
||||||
@@ -166,7 +165,7 @@ export function Link({
|
|||||||
return (
|
return (
|
||||||
<BaseLink
|
<BaseLink
|
||||||
to={`/starter-pack/${handleOrDid}/${rkey}`}
|
to={`/starter-pack/${handleOrDid}/${rkey}`}
|
||||||
label={_(msg`Navigate to ${record.name}`)}
|
label={l`Navigate to ${record.name}`}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
precacheResolvedUri(
|
precacheResolvedUri(
|
||||||
queryClient,
|
queryClient,
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import {
|
|||||||
type AppBskyFeedDefs,
|
type AppBskyFeedDefs,
|
||||||
type ModerationOpts,
|
type ModerationOpts,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
|
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
|
||||||
@@ -46,7 +45,7 @@ export function WizardEditListDialog({
|
|||||||
moderationOpts: ModerationOpts
|
moderationOpts: ModerationOpts
|
||||||
profile: AppBskyActorDefs.ProfileViewDetailed
|
profile: AppBskyActorDefs.ProfileViewDetailed
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const initialNumToRender = useInitialNumToRender()
|
const initialNumToRender = useInitialNumToRender()
|
||||||
|
|
||||||
@@ -119,7 +118,7 @@ export function WizardEditListDialog({
|
|||||||
<View style={{width: 60}}>
|
<View style={{width: 60}}>
|
||||||
{IS_WEB && (
|
{IS_WEB && (
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Close`)}
|
label={l`Close`}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
color="primary"
|
color="primary"
|
||||||
size="small"
|
size="small"
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ import {
|
|||||||
type ModerationOpts,
|
type ModerationOpts,
|
||||||
type ModerationUI,
|
type ModerationUI,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {DISCOVER_FEED_URI, STARTER_PACK_MAX_SIZE} from '#/lib/constants'
|
import {DISCOVER_FEED_URI, STARTER_PACK_MAX_SIZE} from '#/lib/constants'
|
||||||
@@ -52,15 +51,15 @@ function WizardListCard({
|
|||||||
moderationUi: ModerationUI
|
moderationUi: ModerationUI
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Toggle.Item
|
<Toggle.Item
|
||||||
name={type === 'user' ? _(msg`Person toggle`) : _(msg`Feed toggle`)}
|
name={type === 'user' ? l`Person toggle` : l`Feed toggle`}
|
||||||
label={
|
label={
|
||||||
included
|
included
|
||||||
? _(msg`Remove ${displayName} from starter pack`)
|
? l`Remove ${displayName} from starter pack`
|
||||||
: _(msg`Add ${displayName} to starter pack`)
|
: l`Add ${displayName} to starter pack`
|
||||||
}
|
}
|
||||||
value={included}
|
value={included}
|
||||||
disabled={btnType === 'remove' || disabled}
|
disabled={btnType === 'remove' || disabled}
|
||||||
@@ -103,7 +102,7 @@ function WizardListCard({
|
|||||||
<Checkbox />
|
<Checkbox />
|
||||||
) : !disabled ? (
|
) : !disabled ? (
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Remove`)}
|
label={l`Remove`}
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
size="small"
|
size="small"
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ import {
|
|||||||
type AppBskyGraphDefs,
|
type AppBskyGraphDefs,
|
||||||
AtUri,
|
AtUri,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {HITSLOP_10} from '#/lib/constants'
|
import {HITSLOP_10} from '#/lib/constants'
|
||||||
@@ -49,7 +48,7 @@ interface WhoCanReplyProps {
|
|||||||
export function WhoCanReply({post, isThreadAuthor, style}: WhoCanReplyProps) {
|
export function WhoCanReply({post, isThreadAuthor, style}: WhoCanReplyProps) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const infoDialogControl = useDialogControl()
|
const infoDialogControl = useDialogControl()
|
||||||
const editDialogControl = useDialogControl()
|
const editDialogControl = useDialogControl()
|
||||||
|
|
||||||
@@ -82,10 +81,10 @@ export function WhoCanReply({post, isThreadAuthor, style}: WhoCanReplyProps) {
|
|||||||
settings.length === 1 && settings[0].type === 'everybody'
|
settings.length === 1 && settings[0].type === 'everybody'
|
||||||
const noOneCanReply = settings.length === 1 && settings[0].type === 'nobody'
|
const noOneCanReply = settings.length === 1 && settings[0].type === 'nobody'
|
||||||
const description = anyoneCanReply
|
const description = anyoneCanReply
|
||||||
? _(msg`Everybody can reply`)
|
? l`Everybody can reply`
|
||||||
: noOneCanReply
|
: noOneCanReply
|
||||||
? _(msg`Replies disabled`)
|
? l`Replies disabled`
|
||||||
: _(msg`Some people can reply`)
|
: l`Some people can reply`
|
||||||
|
|
||||||
const onPressOpen = () => {
|
const onPressOpen = () => {
|
||||||
if (IS_NATIVE && Keyboard.isVisible()) {
|
if (IS_NATIVE && Keyboard.isVisible()) {
|
||||||
@@ -112,9 +111,7 @@ export function WhoCanReply({post, isThreadAuthor, style}: WhoCanReplyProps) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
label={
|
label={isThreadAuthor ? l`Edit who can reply` : l`Who can reply`}
|
||||||
isThreadAuthor ? _(msg`Edit who can reply`) : _(msg`Who can reply`)
|
|
||||||
}
|
|
||||||
onPress={onPressOpen}
|
onPress={onPressOpen}
|
||||||
{...(isThreadAuthor
|
{...(isThreadAuthor
|
||||||
? Platform.select({
|
? Platform.select({
|
||||||
@@ -161,7 +158,6 @@ export function WhoCanReply({post, isThreadAuthor, style}: WhoCanReplyProps) {
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{isThreadAuthor ? (
|
{isThreadAuthor ? (
|
||||||
<PostInteractionSettingsDialog
|
<PostInteractionSettingsDialog
|
||||||
postUri={post.uri}
|
postUri={post.uri}
|
||||||
@@ -213,13 +209,13 @@ function WhoCanReplyDialog({
|
|||||||
settings: ThreadgateAllowUISetting[]
|
settings: ThreadgateAllowUISetting[]
|
||||||
embeddingDisabled: boolean
|
embeddingDisabled: boolean
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
|
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
label={_(msg`Dialog: adjust who can interact with this post`)}
|
label={l`Dialog: adjust who can interact with this post`}
|
||||||
style={web({maxWidth: 400})}>
|
style={web({maxWidth: 400})}>
|
||||||
<View style={[a.gap_sm]}>
|
<View style={[a.gap_sm]}>
|
||||||
<Text style={[a.font_semi_bold, a.text_xl, a.pb_sm]}>
|
<Text style={[a.font_semi_bold, a.text_xl, a.pb_sm]}>
|
||||||
@@ -233,7 +229,7 @@ function WhoCanReplyDialog({
|
|||||||
</View>
|
</View>
|
||||||
{IS_NATIVE && (
|
{IS_NATIVE && (
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Close`)}
|
label={l`Close`}
|
||||||
onPress={() => control.close()}
|
onPress={() => control.close()}
|
||||||
size="small"
|
size="small"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import {
|
|||||||
type ModerationOpts,
|
type ModerationOpts,
|
||||||
type Un$Typed,
|
type Un$Typed,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
import {
|
import {
|
||||||
type InfiniteData,
|
type InfiniteData,
|
||||||
@@ -72,7 +71,7 @@ function DialogInner({
|
|||||||
includeProfile?: boolean
|
includeProfile?: boolean
|
||||||
}) {
|
}) {
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
const control = Dialog.useDialogContext()
|
const control = Dialog.useDialogContext()
|
||||||
@@ -136,9 +135,7 @@ function DialogInner({
|
|||||||
if (!activitySubscription.post && !activitySubscription.reply) {
|
if (!activitySubscription.post && !activitySubscription.reply) {
|
||||||
ax.metric('activitySubscription:disable', {})
|
ax.metric('activitySubscription:disable', {})
|
||||||
Toast.show(
|
Toast.show(
|
||||||
_(
|
l`You will no longer receive notifications for ${sanitizeHandle(profile.handle, '@')}`,
|
||||||
msg`You will no longer receive notifications for ${sanitizeHandle(profile.handle, '@')}`,
|
|
||||||
),
|
|
||||||
{
|
{
|
||||||
type: 'success',
|
type: 'success',
|
||||||
},
|
},
|
||||||
@@ -168,15 +165,13 @@ function DialogInner({
|
|||||||
})
|
})
|
||||||
if (!initialState.post && !initialState.reply) {
|
if (!initialState.post && !initialState.reply) {
|
||||||
Toast.show(
|
Toast.show(
|
||||||
_(
|
l`You'll start receiving notifications for ${sanitizeHandle(profile.handle, '@')}!`,
|
||||||
msg`You'll start receiving notifications for ${sanitizeHandle(profile.handle, '@')}!`,
|
|
||||||
),
|
|
||||||
{
|
{
|
||||||
type: 'success',
|
type: 'success',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Toast.show(_(msg`Changes saved`), {
|
Toast.show(l`Changes saved`, {
|
||||||
type: 'success',
|
type: 'success',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -195,7 +190,7 @@ function DialogInner({
|
|||||||
|
|
||||||
if (isDirty) {
|
if (isDirty) {
|
||||||
return {
|
return {
|
||||||
label: _(msg`Save changes`),
|
label: l`Save changes`,
|
||||||
color: hasAny ? 'primary' : 'negative',
|
color: hasAny ? 'primary' : 'negative',
|
||||||
onPress: () => saveChanges(state),
|
onPress: () => saveChanges(state),
|
||||||
disabled: isSaving,
|
disabled: isSaving,
|
||||||
@@ -204,26 +199,26 @@ function DialogInner({
|
|||||||
// on web, a disabled save button feels more natural than a massive close button
|
// on web, a disabled save button feels more natural than a massive close button
|
||||||
if (IS_WEB) {
|
if (IS_WEB) {
|
||||||
return {
|
return {
|
||||||
label: _(msg`Save changes`),
|
label: l`Save changes`,
|
||||||
color: 'secondary',
|
color: 'secondary',
|
||||||
disabled: true,
|
disabled: true,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
label: _(msg`Cancel`),
|
label: l`Cancel`,
|
||||||
color: 'secondary',
|
color: 'secondary',
|
||||||
onPress: () => control.close(),
|
onPress: () => control.close(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [state, initialState, control, _, isSaving, saveChanges])
|
}, [state, initialState, control, l, isSaving, saveChanges])
|
||||||
|
|
||||||
const name = createSanitizedDisplayName(profile, false)
|
const name = createSanitizedDisplayName(profile, false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
style={web({maxWidth: 400})}
|
style={web({maxWidth: 400})}
|
||||||
label={_(msg`Get notified of new posts from ${name}`)}>
|
label={l`Get notified of new posts from ${name}`}>
|
||||||
<View style={[a.gap_lg]}>
|
<View style={[a.gap_lg]}>
|
||||||
<View style={[a.gap_xs]}>
|
<View style={[a.gap_xs]}>
|
||||||
<Text style={[a.font_bold, a.text_2xl]}>
|
<Text style={[a.font_bold, a.text_2xl]}>
|
||||||
@@ -249,12 +244,12 @@ function DialogInner({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<Toggle.Group
|
<Toggle.Group
|
||||||
label={_(msg`Subscribe to account activity`)}
|
label={l`Subscribe to account activity`}
|
||||||
values={values}
|
values={values}
|
||||||
onChange={onChange}>
|
onChange={onChange}>
|
||||||
<View style={[a.gap_sm]}>
|
<View style={[a.gap_sm]}>
|
||||||
<Toggle.Item
|
<Toggle.Item
|
||||||
label={_(msg`Posts`)}
|
label={l`Posts`}
|
||||||
name="post"
|
name="post"
|
||||||
style={[
|
style={[
|
||||||
a.flex_1,
|
a.flex_1,
|
||||||
@@ -271,7 +266,7 @@ function DialogInner({
|
|||||||
<Toggle.Switch />
|
<Toggle.Switch />
|
||||||
</Toggle.Item>
|
</Toggle.Item>
|
||||||
<Toggle.Item
|
<Toggle.Item
|
||||||
label={_(msg`Replies`)}
|
label={l`Replies`}
|
||||||
name="reply"
|
name="reply"
|
||||||
style={[
|
style={[
|
||||||
a.flex_1,
|
a.flex_1,
|
||||||
@@ -301,7 +296,6 @@ function DialogInner({
|
|||||||
{isSaving && <ButtonIcon icon={Loader} />}
|
{isSaving && <ButtonIcon icon={Loader} />}
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Dialog.Close />
|
<Dialog.Close />
|
||||||
</Dialog.ScrollableInner>
|
</Dialog.ScrollableInner>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {atoms as a, select, useTheme, type ViewStyleProp} from '#/alf'
|
import {atoms as a, select, useTheme, type ViewStyleProp} from '#/alf'
|
||||||
@@ -40,7 +39,7 @@ function Inner({
|
|||||||
control: Dialog.DialogControlProps
|
control: Dialog.DialogControlProps
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -90,7 +89,7 @@ function Inner({
|
|||||||
<Trans>
|
<Trans>
|
||||||
Learn more in your{' '}
|
Learn more in your{' '}
|
||||||
<InlineLinkText
|
<InlineLinkText
|
||||||
label={_(msg`Go to account settings`)}
|
label={l`Go to account settings`}
|
||||||
to={'/settings/account'}
|
to={'/settings/account'}
|
||||||
style={[a.text_sm, a.leading_snug, a.font_semi_bold]}
|
style={[a.text_sm, a.leading_snug, a.font_semi_bold]}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {useState} 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 {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
import {useMutation} from '@tanstack/react-query'
|
import {useMutation} from '@tanstack/react-query'
|
||||||
|
|
||||||
@@ -23,12 +22,12 @@ export function AgeAssuranceAppealDialog({
|
|||||||
}: {
|
}: {
|
||||||
control: Dialog.DialogControlProps
|
control: Dialog.DialogControlProps
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
return (
|
return (
|
||||||
<Dialog.Outer control={control}>
|
<Dialog.Outer control={control}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
label={_(msg`Contact our moderation team`)}
|
label={l`Contact our moderation team`}
|
||||||
style={[web({maxWidth: 400})]}>
|
style={[web({maxWidth: 400})]}>
|
||||||
<Inner control={control} />
|
<Inner control={control} />
|
||||||
<Dialog.Close />
|
<Dialog.Close />
|
||||||
@@ -38,7 +37,7 @@ export function AgeAssuranceAppealDialog({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Inner({control}: {control: Dialog.DialogControlProps}) {
|
function Inner({control}: {control: Dialog.DialogControlProps}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const {gtPhone} = useBreakpoints()
|
const {gtPhone} = useBreakpoints()
|
||||||
@@ -68,22 +67,17 @@ function Inner({control}: {control: Dialog.DialogControlProps}) {
|
|||||||
},
|
},
|
||||||
onError: err => {
|
onError: err => {
|
||||||
logger.error('AgeAssuranceAppealDialog failed', {safeMessage: err})
|
logger.error('AgeAssuranceAppealDialog failed', {safeMessage: err})
|
||||||
Toast.show(
|
Toast.show(l`Age assurance inquiry failed to send, please try again.`, {
|
||||||
_(msg`Age assurance inquiry failed to send, please try again.`),
|
type: 'error',
|
||||||
{
|
})
|
||||||
type: 'error',
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
control.close()
|
control.close()
|
||||||
Toast.show(
|
Toast.show(
|
||||||
_(
|
l({
|
||||||
msg({
|
message: 'Age assurance inquiry was submitted',
|
||||||
message: 'Age assurance inquiry was submitted',
|
context: 'toast',
|
||||||
context: 'toast',
|
}),
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -93,18 +87,15 @@ function Inner({control}: {control: Dialog.DialogControlProps}) {
|
|||||||
<View style={[a.align_start]}>
|
<View style={[a.align_start]}>
|
||||||
<AgeAssuranceBadge />
|
<AgeAssuranceBadge />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Text style={[a.text_2xl, a.font_bold, a.pt_md, a.leading_tight]}>
|
<Text style={[a.text_2xl, a.font_bold, a.pt_md, a.leading_tight]}>
|
||||||
<Trans>Contact us</Trans>
|
<Trans>Contact us</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Text style={[a.text_sm, a.pt_sm, a.leading_snug]}>
|
<Text style={[a.text_sm, a.pt_sm, a.leading_snug]}>
|
||||||
<Trans>
|
<Trans>
|
||||||
Please provide any additional details you feel moderators may need in
|
Please provide any additional details you feel moderators may need in
|
||||||
order to properly assess your Age Assurance status.
|
order to properly assess your Age Assurance status.
|
||||||
</Trans>
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<View style={[a.pt_md]}>
|
<View style={[a.pt_md]}>
|
||||||
<Dialog.Input
|
<Dialog.Input
|
||||||
multiline
|
multiline
|
||||||
@@ -113,13 +104,13 @@ function Inner({control}: {control: Dialog.DialogControlProps}) {
|
|||||||
onChangeText={details => {
|
onChangeText={details => {
|
||||||
setDetails(details)
|
setDetails(details)
|
||||||
}}
|
}}
|
||||||
label={_(msg`Additional details (limit 1000 characters)`)}
|
label={l`Additional details (limit 1000 characters)`}
|
||||||
numberOfLines={4}
|
numberOfLines={4}
|
||||||
onSubmitEditing={() => mutate()}
|
onSubmitEditing={() => mutate()}
|
||||||
/>
|
/>
|
||||||
<View style={[a.pt_md, a.gap_sm, gtPhone && [a.flex_row_reverse]]}>
|
<View style={[a.pt_md, a.gap_sm, gtPhone && [a.flex_row_reverse]]}>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Submit`)}
|
label={l`Submit`}
|
||||||
size="small"
|
size="small"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="primary"
|
color="primary"
|
||||||
@@ -130,7 +121,7 @@ function Inner({control}: {control: Dialog.DialogControlProps}) {
|
|||||||
{isPending && <ButtonIcon icon={Loader} position="right" />}
|
{isPending && <ButtonIcon icon={Loader} position="right" />}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Cancel`)}
|
label={l`Cancel`}
|
||||||
size="small"
|
size="small"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {useMemo} from 'react'
|
import {useMemo} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {Nux, useNux, useSaveNux} from '#/state/queries/nuxs'
|
import {Nux, useNux, useSaveNux} from '#/state/queries/nuxs'
|
||||||
import {atoms as a, select, useTheme} from '#/alf'
|
import {atoms as a, select, useTheme} from '#/alf'
|
||||||
@@ -43,7 +42,7 @@ export function useInternalState() {
|
|||||||
export function AgeAssuranceDismissibleFeedBanner() {
|
export function AgeAssuranceDismissibleFeedBanner() {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {visible, close} = useInternalState()
|
const {visible, close} = useInternalState()
|
||||||
const copy = useAgeAssuranceCopy()
|
const copy = useAgeAssuranceCopy()
|
||||||
|
|
||||||
@@ -63,7 +62,7 @@ export function AgeAssuranceDismissibleFeedBanner() {
|
|||||||
},
|
},
|
||||||
]}>
|
]}>
|
||||||
<Link
|
<Link
|
||||||
label={_(msg`Learn more about age assurance`)}
|
label={l`Learn more about age assurance`}
|
||||||
to="/settings/account"
|
to="/settings/account"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
close()
|
close()
|
||||||
@@ -100,9 +99,8 @@ export function AgeAssuranceDismissibleFeedBanner() {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Don't show again`)}
|
label={l`Don't show again`}
|
||||||
size="small"
|
size="small"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
close()
|
close()
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {Nux, useNux, useSaveNux} from '#/state/queries/nuxs'
|
import {Nux, useNux, useSaveNux} from '#/state/queries/nuxs'
|
||||||
import {atoms as a, type ViewStyleProp} from '#/alf'
|
import {atoms as a, type ViewStyleProp} from '#/alf'
|
||||||
@@ -13,7 +12,7 @@ import {useAgeAssurance} from '#/ageAssurance'
|
|||||||
import {useAnalytics} from '#/analytics'
|
import {useAnalytics} from '#/analytics'
|
||||||
|
|
||||||
export function AgeAssuranceDismissibleNotice({style}: ViewStyleProp & {}) {
|
export function AgeAssuranceDismissibleNotice({style}: ViewStyleProp & {}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const aa = useAgeAssurance()
|
const aa = useAgeAssurance()
|
||||||
const {nux} = useNux(Nux.AgeAssuranceDismissibleNotice)
|
const {nux} = useNux(Nux.AgeAssuranceDismissibleNotice)
|
||||||
@@ -35,7 +34,7 @@ export function AgeAssuranceDismissibleNotice({style}: ViewStyleProp & {}) {
|
|||||||
<AgeAssuranceAdmonition>{copy.notice}</AgeAssuranceAdmonition>
|
<AgeAssuranceAdmonition>{copy.notice}</AgeAssuranceAdmonition>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Don't show again`)}
|
label={l`Don't show again`}
|
||||||
size="tiny"
|
size="tiny"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="secondary_inverted"
|
color="secondary_inverted"
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {type ViewStyleProp} from '#/alf'
|
import {type ViewStyleProp} from '#/alf'
|
||||||
@@ -9,7 +8,7 @@ import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as RetryIcon} from '
|
|||||||
import {refetchConfig} from '#/ageAssurance/data'
|
import {refetchConfig} from '#/ageAssurance/data'
|
||||||
|
|
||||||
export function AgeAssuranceConfigUnavailableError(props: ViewStyleProp) {
|
export function AgeAssuranceConfigUnavailableError(props: ViewStyleProp) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
return (
|
return (
|
||||||
<Admonition.Outer type="error" style={props.style}>
|
<Admonition.Outer type="error" style={props.style}>
|
||||||
<Admonition.Row>
|
<Admonition.Row>
|
||||||
@@ -25,7 +24,7 @@ export function AgeAssuranceConfigUnavailableError(props: ViewStyleProp) {
|
|||||||
</Admonition.Content>
|
</Admonition.Content>
|
||||||
<Admonition.Button
|
<Admonition.Button
|
||||||
color="negative_subtle"
|
color="negative_subtle"
|
||||||
label={_(msg`Retry`)}
|
label={l`Retry`}
|
||||||
onPress={() => refetchConfig().catch(() => {})}>
|
onPress={() => refetchConfig().catch(() => {})}>
|
||||||
<ButtonText>
|
<ButtonText>
|
||||||
<Trans>Retry</Trans>
|
<Trans>Retry</Trans>
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {useState} from 'react'
|
import {useState} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {XRPCError} from '@atproto/api'
|
import {XRPCError} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
import {validate as validateEmail} from 'email-validator'
|
import {validate as validateEmail} from 'email-validator'
|
||||||
|
|
||||||
@@ -41,15 +40,12 @@ export function AgeAssuranceInitDialog({
|
|||||||
}: {
|
}: {
|
||||||
control: Dialog.DialogControlProps
|
control: Dialog.DialogControlProps
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
return (
|
return (
|
||||||
<Dialog.Outer control={control}>
|
<Dialog.Outer control={control}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
|
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
label={_(
|
label={l`Begin the age assurance process by completing the fields below.`}
|
||||||
msg`Begin the age assurance process by completing the fields below.`,
|
|
||||||
)}
|
|
||||||
style={[
|
style={[
|
||||||
web({
|
web({
|
||||||
maxWidth: 400,
|
maxWidth: 400,
|
||||||
@@ -63,7 +59,7 @@ export function AgeAssuranceInitDialog({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Inner() {
|
function Inner() {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const langPrefs = useLanguagePrefs()
|
const langPrefs = useLanguagePrefs()
|
||||||
@@ -98,9 +94,7 @@ function Inner() {
|
|||||||
|
|
||||||
if (tlds && isEmailMaybeInvalid(email, tlds)) {
|
if (tlds && isEmailMaybeInvalid(email, tlds)) {
|
||||||
setEmailError(
|
setEmailError(
|
||||||
_(
|
l`Please double-check that you have entered your email address correctly.`,
|
||||||
msg`Please double-check that you have entered your email address correctly.`,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
return {status: 'maybe'}
|
return {status: 'maybe'}
|
||||||
}
|
}
|
||||||
@@ -108,7 +102,7 @@ function Inner() {
|
|||||||
return {status: 'valid'}
|
return {status: 'valid'}
|
||||||
}
|
}
|
||||||
|
|
||||||
setEmailError(_(msg`Please enter a valid email address.`))
|
setEmailError(l`Please enter a valid email address.`)
|
||||||
setDisabled(true)
|
setDisabled(true)
|
||||||
|
|
||||||
return {status: 'invalid'}
|
return {status: 'invalid'}
|
||||||
@@ -135,15 +129,11 @@ function Inner() {
|
|||||||
|
|
||||||
setSuccess(true)
|
setSuccess(true)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
let error: React.ReactNode = _(
|
let error: React.ReactNode = l`Something went wrong, please try again`
|
||||||
msg`Something went wrong, please try again`,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (e instanceof XRPCError) {
|
if (e instanceof XRPCError) {
|
||||||
if (e.error === 'InvalidEmail') {
|
if (e.error === 'InvalidEmail') {
|
||||||
error = _(
|
error = l`Please enter a valid, non-temporary email address. You may need to access this email in the future.`
|
||||||
msg`Please enter a valid, non-temporary email address. You may need to access this email in the future.`,
|
|
||||||
)
|
|
||||||
ax.metric('ageAssurance:initDialogError', {code: 'InvalidEmail'})
|
ax.metric('ageAssurance:initDialogError', {code: 'InvalidEmail'})
|
||||||
} else if (e.error === 'DidTooLong') {
|
} else if (e.error === 'DidTooLong') {
|
||||||
error = (
|
error = (
|
||||||
@@ -153,7 +143,7 @@ function Inner() {
|
|||||||
your account. Please{' '}
|
your account. Please{' '}
|
||||||
<SimpleInlineLinkText
|
<SimpleInlineLinkText
|
||||||
to={createSupportLink({code: SupportCode.AA_DID, email})}
|
to={createSupportLink({code: SupportCode.AA_DID, email})}
|
||||||
label={_(msg`Contact support`)}>
|
label={l`Contact support`}>
|
||||||
contact support
|
contact support
|
||||||
</SimpleInlineLinkText>{' '}
|
</SimpleInlineLinkText>{' '}
|
||||||
for assistance.
|
for assistance.
|
||||||
@@ -197,7 +187,7 @@ function Inner() {
|
|||||||
<Trans>
|
<Trans>
|
||||||
We have partnered with{' '}
|
We have partnered with{' '}
|
||||||
<SimpleInlineLinkText
|
<SimpleInlineLinkText
|
||||||
label={_(msg`KWS website`)}
|
label={l`KWS website`}
|
||||||
to={urls.kwsHome}
|
to={urls.kwsHome}
|
||||||
style={[a.text_sm, a.leading_snug]}>
|
style={[a.text_sm, a.leading_snug]}>
|
||||||
KWS
|
KWS
|
||||||
@@ -220,7 +210,7 @@ function Inner() {
|
|||||||
{success ? (
|
{success ? (
|
||||||
<View style={[a.w_full]}>
|
<View style={[a.w_full]}>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Close dialog`)}
|
label={l`Close dialog`}
|
||||||
size="large"
|
size="large"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
@@ -253,8 +243,8 @@ function Inner() {
|
|||||||
</TextField.LabelText>
|
</TextField.LabelText>
|
||||||
<TextField.Root isInvalid={!!emailError}>
|
<TextField.Root isInvalid={!!emailError}>
|
||||||
<TextField.Input
|
<TextField.Input
|
||||||
label={_(msg`Your email`)}
|
label={l`Your email`}
|
||||||
placeholder={_(msg`Your email`)}
|
placeholder={l`Your email`}
|
||||||
value={email}
|
value={email}
|
||||||
onChangeText={setEmail}
|
onChangeText={setEmail}
|
||||||
onFocus={() => setEmailError('')}
|
onFocus={() => setEmailError('')}
|
||||||
@@ -289,7 +279,7 @@ function Inner() {
|
|||||||
<Trans>Your preferred language</Trans>
|
<Trans>Your preferred language</Trans>
|
||||||
</TextField.LabelText>
|
</TextField.LabelText>
|
||||||
<LanguageSelect
|
<LanguageSelect
|
||||||
label={_(msg`Preferred language`)}
|
label={l`Preferred language`}
|
||||||
value={language}
|
value={language}
|
||||||
onChange={value => {
|
onChange={value => {
|
||||||
setLanguage(value)
|
setLanguage(value)
|
||||||
@@ -310,7 +300,7 @@ function Inner() {
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
label={_(msg`Begin age assurance process`)}
|
label={l`Begin age assurance process`}
|
||||||
size="large"
|
size="large"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {useEffect, useRef, useState} from 'react'
|
import {useEffect, useRef, useState} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {retry} from '#/lib/async/retry'
|
import {retry} from '#/lib/async/retry'
|
||||||
@@ -61,7 +60,7 @@ export function useAgeAssuranceRedirectDialogControl() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function AgeAssuranceRedirectDialog() {
|
export function AgeAssuranceRedirectDialog() {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const control = useAgeAssuranceRedirectDialogControl()
|
const control = useAgeAssuranceRedirectDialogControl()
|
||||||
|
|
||||||
// for testing
|
// for testing
|
||||||
@@ -70,9 +69,8 @@ export function AgeAssuranceRedirectDialog() {
|
|||||||
return (
|
return (
|
||||||
<Dialog.Outer control={control.control} onClose={() => control.clear()}>
|
<Dialog.Outer control={control.control} onClose={() => control.clear()}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
|
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
label={_(msg`Verifying your age assurance status`)}
|
label={l`Verifying your age assurance status`}
|
||||||
style={[web({maxWidth: 400})]}>
|
style={[web({maxWidth: 400})]}>
|
||||||
<Inner optimisticState={control.value} />
|
<Inner optimisticState={control.value} />
|
||||||
</Dialog.ScrollableInner>
|
</Dialog.ScrollableInner>
|
||||||
@@ -83,7 +81,7 @@ export function AgeAssuranceRedirectDialog() {
|
|||||||
export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
|
export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
const polling = useRef(false)
|
const polling = useRef(false)
|
||||||
const unmounted = useRef(false)
|
const unmounted = useRef(false)
|
||||||
@@ -171,7 +169,7 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
|
|||||||
{IS_NATIVE && (
|
{IS_NATIVE && (
|
||||||
<View style={[a.w_full, a.pt_lg]}>
|
<View style={[a.w_full, a.pt_lg]}>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Close`)}
|
label={l`Close`}
|
||||||
size="large"
|
size="large"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
@@ -183,7 +181,6 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Dialog.Close />
|
<Dialog.Close />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
@@ -230,7 +227,7 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
|
|||||||
{error && IS_NATIVE && (
|
{error && IS_NATIVE && (
|
||||||
<View style={[a.w_full, a.pt_lg]}>
|
<View style={[a.w_full, a.pt_lg]}>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Close`)}
|
label={l`Close`}
|
||||||
size="large"
|
size="large"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
@@ -242,7 +239,6 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{error && <Dialog.Close />}
|
{error && <Dialog.Close />}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {atoms as a} from '#/alf'
|
import {atoms as a} from '#/alf'
|
||||||
@@ -27,7 +26,7 @@ export function AgeRestrictedScreen({
|
|||||||
infoText?: string
|
infoText?: string
|
||||||
rightHeaderSlot?: React.ReactNode
|
rightHeaderSlot?: React.ReactNode
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const copy = useAgeAssuranceCopy()
|
const copy = useAgeAssuranceCopy()
|
||||||
const aa = useAgeAssurance()
|
const aa = useAgeAssurance()
|
||||||
@@ -70,7 +69,7 @@ export function AgeRestrictedScreen({
|
|||||||
<View
|
<View
|
||||||
style={[a.flex_row, a.justify_between, a.align_center, a.pb_xl]}>
|
style={[a.flex_row, a.justify_between, a.align_center, a.pb_xl]}>
|
||||||
<Link
|
<Link
|
||||||
label={_(msg`Go to account settings`)}
|
label={l`Go to account settings`}
|
||||||
to="/settings/account"
|
to="/settings/account"
|
||||||
size="small"
|
size="small"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import {useMemo} from 'react'
|
import {useMemo} from 'react'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {useAgeAssurance} from '#/ageAssurance'
|
import {useAgeAssurance} from '#/ageAssurance'
|
||||||
|
|
||||||
export function useAgeAssuranceCopy() {
|
export function useAgeAssuranceCopy() {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const aa = useAgeAssurance()
|
const aa = useAgeAssurance()
|
||||||
|
|
||||||
const hasCompletedFlow = aa.state.status === aa.Status.Assured
|
const hasCompletedFlow = aa.state.status === aa.Status.Assured
|
||||||
@@ -13,18 +12,10 @@ export function useAgeAssuranceCopy() {
|
|||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
return {
|
return {
|
||||||
notice: hasCompletedFlow
|
notice: hasCompletedFlow
|
||||||
? _(
|
? l`You have completed the Age Assurance process, but based on the results, we cannot be sure that you are 18 years of age or older. Due to laws in your region, certain features on Bluesky must remain restricted until you're able to verify you're an adult.`
|
||||||
msg`You have completed the Age Assurance process, but based on the results, we cannot be sure that you are 18 years of age or older. Due to laws in your region, certain features on Bluesky must remain restricted until you're able to verify you're an adult.`,
|
: l`Due to laws in your region, certain features on Bluesky are currently restricted until you're able to verify you're an adult.`,
|
||||||
)
|
banner: l`The laws in your region require you to verify you're an adult to access certain features. Tap to learn more.`,
|
||||||
: _(
|
chatsInfoText: l`Don't worry! All existing messages and settings are saved and will be available after you verify you're an adult.`,
|
||||||
msg`Due to laws in your region, certain features on Bluesky are currently restricted until you're able to verify you're an adult.`,
|
|
||||||
),
|
|
||||||
banner: _(
|
|
||||||
msg`The laws in your region require you to verify you're an adult to access certain features. Tap to learn more.`,
|
|
||||||
),
|
|
||||||
chatsInfoText: _(
|
|
||||||
msg`Don't worry! All existing messages and settings are saved and will be available after you verify you're an adult.`,
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}, [_, hasCompletedFlow])
|
}, [l, hasCompletedFlow])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ 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 {LinearGradient} from 'expo-linear-gradient'
|
import {LinearGradient} from 'expo-linear-gradient'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {HITSLOP_10} from '#/lib/constants'
|
import {HITSLOP_10} from '#/lib/constants'
|
||||||
@@ -19,7 +18,7 @@ import {useIsFindContactsFeatureEnabledBasedOnGeolocation} from './country-allow
|
|||||||
|
|
||||||
export function FindContactsBannerNUX() {
|
export function FindContactsBannerNUX() {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const {visible, close} = useInternalState()
|
const {visible, close} = useInternalState()
|
||||||
|
|
||||||
@@ -30,7 +29,7 @@ export function FindContactsBannerNUX() {
|
|||||||
<View style={a.w_full}>
|
<View style={a.w_full}>
|
||||||
<Link
|
<Link
|
||||||
to={{screen: 'FindContactsFlow'}}
|
to={{screen: 'FindContactsFlow'}}
|
||||||
label={_(msg`Import contacts to find your friends`)}
|
label={l`Import contacts to find your friends`}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
ax.metric('contacts:nux:bannerPressed', {})
|
ax.metric('contacts:nux:bannerPressed', {})
|
||||||
}}
|
}}
|
||||||
@@ -75,7 +74,7 @@ export function FindContactsBannerNUX() {
|
|||||||
</LinearGradient>
|
</LinearGradient>
|
||||||
</Link>
|
</Link>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Dismiss banner`)}
|
label={l`Dismiss banner`}
|
||||||
hitSlop={HITSLOP_10}
|
hitSlop={HITSLOP_10}
|
||||||
onPress={close}
|
onPress={close}
|
||||||
style={[a.absolute, {top: 14, right: 14}]}
|
style={[a.absolute, {top: 14, right: 14}]}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {Image} from 'expo-image'
|
import {Image} from 'expo-image'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
|
|
||||||
export function ContactsHeroImage() {
|
export function ContactsHeroImage() {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
@@ -24,9 +23,7 @@ export function ContactsHeroImage() {
|
|||||||
source={require('../../../../assets/images/find_friends_illustration.webp')}
|
source={require('../../../../assets/images/find_friends_illustration.webp')}
|
||||||
accessibilityIgnoresInvertColors
|
accessibilityIgnoresInvertColors
|
||||||
style={[a.w_full, {aspectRatio: 1278 / 661}]}
|
style={[a.w_full, {aspectRatio: 1278 / 661}]}
|
||||||
alt={_(
|
alt={l`An illustration depicting user avatars flowing from a contact book into the Bluesky app`}
|
||||||
msg`An illustration depicting user avatars flowing from a contact book into the Bluesky app`,
|
|
||||||
)}
|
|
||||||
useAppleWebpCodec
|
useAppleWebpCodec
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import {type StyleProp, type TextStyle} from 'react-native'
|
import {type StyleProp, type TextStyle} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {HITSLOP_20} from '#/lib/constants'
|
import {HITSLOP_20} from '#/lib/constants'
|
||||||
@@ -22,7 +21,7 @@ export function InviteInfo({
|
|||||||
*/
|
*/
|
||||||
iconOffset?: number
|
iconOffset?: number
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const control = Dialog.useDialogControl()
|
const control = Dialog.useDialogControl()
|
||||||
|
|
||||||
const style = [a.text_md, a.leading_snug, a.mt_xs]
|
const style = [a.text_md, a.leading_snug, a.mt_xs]
|
||||||
@@ -30,16 +29,15 @@ export function InviteInfo({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Learn more about how inviting friends works`)}
|
label={l`Learn more about how inviting friends works`}
|
||||||
onPress={control.open}
|
onPress={control.open}
|
||||||
hitSlop={HITSLOP_20}
|
hitSlop={HITSLOP_20}
|
||||||
style={android({transform: [{translateY: iconOffset}]})}>
|
style={android({transform: [{translateY: iconOffset}]})}>
|
||||||
<InfoIcon style={iconStyle} size="sm" />
|
<InfoIcon style={iconStyle} size="sm" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
|
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
<Dialog.ScrollableInner label={_(msg`Invite Friends`)}>
|
<Dialog.ScrollableInner label={l`Invite Friends`}>
|
||||||
<Text style={[a.text_2xl, a.font_bold]}>
|
<Text style={[a.text_2xl, a.font_bold]}>
|
||||||
<Trans>Invite Friends</Trans>
|
<Trans>Invite Friends</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
@@ -70,7 +68,7 @@ export function InviteInfo({
|
|||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Done`)}
|
label={l`Done`}
|
||||||
onPress={() => control.close()}
|
onPress={() => control.close()}
|
||||||
size="large"
|
size="large"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ import {
|
|||||||
type TextInputSelectionChangeEvent,
|
type TextInputSelectionChangeEvent,
|
||||||
View,
|
View,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {mergeRefs} from '#/lib/merge-refs'
|
import {mergeRefs} from '#/lib/merge-refs'
|
||||||
import {atoms as a, ios, platform, useTheme} from '#/alf'
|
import {atoms as a, ios, platform, useTheme} from '#/alf'
|
||||||
@@ -30,7 +29,7 @@ export function OTPInput({
|
|||||||
onComplete?: (code: string) => void
|
onComplete?: (code: string) => void
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const innerRef = useRef<TextInput>(null)
|
const innerRef = useRef<TextInput>(null)
|
||||||
const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState()
|
const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState()
|
||||||
const [selection, setSelection] = useState({start: 0, end: 0})
|
const [selection, setSelection] = useState({start: 0, end: 0})
|
||||||
@@ -52,7 +51,7 @@ export function OTPInput({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
accessibilityLabel={_(msg`Focus code input`)}
|
accessibilityLabel={l`Focus code input`}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityHint=""
|
accessibilityHint=""
|
||||||
style={[a.w_full, a.relative]}
|
style={[a.w_full, a.relative]}
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import {
|
|||||||
AppBskyContactImportContacts,
|
AppBskyContactImportContacts,
|
||||||
type Un$Typed,
|
type Un$Typed,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {msg, t} from '@lingui/core/macro'
|
import {t} from '@lingui/core/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
import {useMutation, useQueryClient} from '@tanstack/react-query'
|
import {useMutation, useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ export function GetContacts({
|
|||||||
onCancel: () => void
|
onCancel: () => void
|
||||||
context: 'Onboarding' | 'Standalone'
|
context: 'Onboarding' | 'Standalone'
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
const insets = useSafeAreaInsets()
|
const insets = useSafeAreaInsets()
|
||||||
@@ -143,32 +143,26 @@ export function GetContacts({
|
|||||||
})
|
})
|
||||||
if (isNetworkError(err)) {
|
if (isNetworkError(err)) {
|
||||||
Toast.show(
|
Toast.show(
|
||||||
_(
|
l`There was a problem with your internet connection, please try again`,
|
||||||
msg`There was a problem with your internet connection, please try again`,
|
|
||||||
),
|
|
||||||
{type: 'error'},
|
{type: 'error'},
|
||||||
)
|
)
|
||||||
} else if (
|
} else if (
|
||||||
err instanceof AppBskyContactImportContacts.TooManyContactsError
|
err instanceof AppBskyContactImportContacts.TooManyContactsError
|
||||||
) {
|
) {
|
||||||
Toast.show(
|
Toast.show(
|
||||||
_(
|
l`Too many contacts - you've exceeded the number of contacts you can import to find your friends`,
|
||||||
msg`Too many contacts - you've exceeded the number of contacts you can import to find your friends`,
|
|
||||||
),
|
|
||||||
{type: 'error'},
|
{type: 'error'},
|
||||||
)
|
)
|
||||||
} else if (
|
} else if (
|
||||||
err instanceof AppBskyContactImportContacts.InvalidTokenError
|
err instanceof AppBskyContactImportContacts.InvalidTokenError
|
||||||
) {
|
) {
|
||||||
Toast.show(
|
Toast.show(
|
||||||
_(
|
l`Could not upload contacts. You need to re-verify your phone number to proceed`,
|
||||||
msg`Could not upload contacts. You need to re-verify your phone number to proceed`,
|
|
||||||
),
|
|
||||||
{type: 'error'},
|
{type: 'error'},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
logger.error('Error uploading contacts', {safeMessage: err})
|
logger.error('Error uploading contacts', {safeMessage: err})
|
||||||
Toast.show(_(msg`Could not upload contacts. ${cleanError(err)}`), {
|
Toast.show(l`Could not upload contacts. ${cleanError(err)}`, {
|
||||||
type: 'error',
|
type: 'error',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -270,7 +264,7 @@ export function GetContacts({
|
|||||||
</Trans>
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Find my friends`)}
|
label={l`Find my friends`}
|
||||||
size="large"
|
size="large"
|
||||||
color="primary"
|
color="primary"
|
||||||
onPress={() => getContacts()}
|
onPress={() => getContacts()}
|
||||||
@@ -289,7 +283,7 @@ export function GetContacts({
|
|||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Cancel`)}
|
label={l`Cancel`}
|
||||||
size="large"
|
size="large"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
onPress={onCancel}>
|
onPress={onCancel}>
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ import {Keyboard, View} from 'react-native'
|
|||||||
import {KeyboardAvoidingView} from 'react-native-keyboard-controller'
|
import {KeyboardAvoidingView} from 'react-native-keyboard-controller'
|
||||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||||
import {AppBskyContactStartPhoneVerification} from '@atproto/api'
|
import {AppBskyContactStartPhoneVerification} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
import {useMutation} from '@tanstack/react-query'
|
import {useMutation} from '@tanstack/react-query'
|
||||||
|
|
||||||
@@ -53,7 +52,7 @@ export function PhoneInput({
|
|||||||
context: 'Onboarding' | 'Standalone'
|
context: 'Onboarding' | 'Standalone'
|
||||||
onSkip: () => void
|
onSkip: () => void
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
@@ -98,26 +97,22 @@ export function PhoneInput({
|
|||||||
onError: err => {
|
onError: err => {
|
||||||
if (isNetworkError(err)) {
|
if (isNetworkError(err)) {
|
||||||
setError(
|
setError(
|
||||||
_(
|
l`A network error occurred. Please check your internet connection`,
|
||||||
msg`A network error occurred. Please check your internet connection`,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
} else if (
|
} else if (
|
||||||
err instanceof
|
err instanceof
|
||||||
AppBskyContactStartPhoneVerification.RateLimitExceededError
|
AppBskyContactStartPhoneVerification.RateLimitExceededError
|
||||||
) {
|
) {
|
||||||
setError(_(msg`Rate limit exceeded. Please try again later.`))
|
setError(l`Rate limit exceeded. Please try again later.`)
|
||||||
} else if (
|
} else if (
|
||||||
err instanceof AppBskyContactStartPhoneVerification.InvalidPhoneError
|
err instanceof AppBskyContactStartPhoneVerification.InvalidPhoneError
|
||||||
) {
|
) {
|
||||||
setError(
|
setError(
|
||||||
_(
|
l`The verification provider was unable to send a code to your phone number. Please check your phone number and try again.`,
|
||||||
msg`The verification provider was unable to send a code to your phone number. Please check your phone number and try again.`,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
logger.error('Verify phone number failed', {safeMessage: err})
|
logger.error('Verify phone number failed', {safeMessage: err})
|
||||||
setError(_(msg`An error occurred. ${cleanError(err)}`))
|
setError(l`An error occurred. ${cleanError(err)}`)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -139,7 +134,7 @@ export function PhoneInput({
|
|||||||
phoneNumber: result.formatted,
|
phoneNumber: result.formatted,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
setFormatError(result.reason ?? _(msg`Invalid phone number`))
|
setFormatError(result.reason ?? l`Invalid phone number`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +152,7 @@ export function PhoneInput({
|
|||||||
size="small"
|
size="small"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
label={_(msg`Skip contact sharing and continue to the app`)}
|
label={l`Skip contact sharing and continue to the app`}
|
||||||
onPress={onSkip}>
|
onPress={onSkip}>
|
||||||
<ButtonText>
|
<ButtonText>
|
||||||
<Trans>Skip</Trans>
|
<Trans>Skip</Trans>
|
||||||
@@ -201,7 +196,7 @@ export function PhoneInput({
|
|||||||
<View style={[a.flex_1]}>
|
<View style={[a.flex_1]}>
|
||||||
<TextField.Root isInvalid={!!formatError || !isFeatureEnabled}>
|
<TextField.Root isInvalid={!!formatError || !isFeatureEnabled}>
|
||||||
<TextField.Input
|
<TextField.Input
|
||||||
label={_(msg`Phone number`)}
|
label={l`Phone number`}
|
||||||
value={phoneNumber}
|
value={phoneNumber}
|
||||||
onChangeText={text => {
|
onChangeText={text => {
|
||||||
if (formatError) setFormatError('')
|
if (formatError) setFormatError('')
|
||||||
@@ -252,7 +247,7 @@ export function PhoneInput({
|
|||||||
<View style={[gutters, {paddingBottom}]}>
|
<View style={[gutters, {paddingBottom}]}>
|
||||||
<Button
|
<Button
|
||||||
disabled={!phoneNumber || isPending}
|
disabled={!phoneNumber || isPending}
|
||||||
label={_(msg`Send code`)}
|
label={l`Send code`}
|
||||||
size="large"
|
size="large"
|
||||||
color="primary"
|
color="primary"
|
||||||
onPress={onSubmitNumber}>
|
onPress={onSubmitNumber}>
|
||||||
@@ -269,7 +264,7 @@ export function PhoneInput({
|
|||||||
|
|
||||||
function LegalDisclaimer() {
|
function LegalDisclaimer() {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
const style = [a.text_xs, t.atoms.text_contrast_medium, a.leading_snug]
|
const style = [a.text_xs, t.atoms.text_contrast_medium, a.leading_snug]
|
||||||
|
|
||||||
@@ -299,12 +294,10 @@ function LegalDisclaimer() {
|
|||||||
time by visiting settings.{' '}
|
time by visiting settings.{' '}
|
||||||
<InlineLinkText
|
<InlineLinkText
|
||||||
to={urls.website.support.findFriendsPrivacyPolicy}
|
to={urls.website.support.findFriendsPrivacyPolicy}
|
||||||
label={_(
|
label={l({
|
||||||
msg({
|
message: `Learn more about importing contacts`,
|
||||||
message: `Learn more about importing contacts`,
|
context: `english-only-resource`,
|
||||||
context: `english-only-resource`,
|
})}
|
||||||
}),
|
|
||||||
)}
|
|
||||||
style={[a.text_xs, a.leading_snug]}>
|
style={[a.text_xs, a.leading_snug]}>
|
||||||
<Trans context="english-only-resource">Learn more</Trans>
|
<Trans context="english-only-resource">Learn more</Trans>
|
||||||
</InlineLinkText>
|
</InlineLinkText>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ import {
|
|||||||
AppBskyContactStartPhoneVerification,
|
AppBskyContactStartPhoneVerification,
|
||||||
AppBskyContactVerifyPhone,
|
AppBskyContactVerifyPhone,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
import {useMutation} from '@tanstack/react-query'
|
import {useMutation} from '@tanstack/react-query'
|
||||||
|
|
||||||
@@ -41,7 +40,7 @@ export function VerifyNumber({
|
|||||||
onSkip: () => void
|
onSkip: () => void
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
const gutters = useGutters([0, 'wide'])
|
const gutters = useGutters([0, 'wide'])
|
||||||
@@ -95,23 +94,19 @@ export function VerifyNumber({
|
|||||||
setError({
|
setError({
|
||||||
retryable: true,
|
retryable: true,
|
||||||
isResendError: false,
|
isResendError: false,
|
||||||
message: _(
|
message: l`A network error occurred. Please check your internet connection.`,
|
||||||
msg`A network error occurred. Please check your internet connection.`,
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
} else if (err instanceof AppBskyContactVerifyPhone.InvalidCodeError) {
|
} else if (err instanceof AppBskyContactVerifyPhone.InvalidCodeError) {
|
||||||
setError({
|
setError({
|
||||||
retryable: true,
|
retryable: true,
|
||||||
isResendError: true,
|
isResendError: true,
|
||||||
message: _(msg`This code is invalid. Resend to get a new code.`),
|
message: l`This code is invalid. Resend to get a new code.`,
|
||||||
})
|
})
|
||||||
} else if (err instanceof AppBskyContactVerifyPhone.InvalidPhoneError) {
|
} else if (err instanceof AppBskyContactVerifyPhone.InvalidPhoneError) {
|
||||||
setError({
|
setError({
|
||||||
retryable: false,
|
retryable: false,
|
||||||
isResendError: false,
|
isResendError: false,
|
||||||
message: _(
|
message: l`The verification provider was unable to send a code to your phone number. Please check your phone number and try again.`,
|
||||||
msg`The verification provider was unable to send a code to your phone number. Please check your phone number and try again.`,
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
} else if (
|
} else if (
|
||||||
err instanceof AppBskyContactVerifyPhone.RateLimitExceededError
|
err instanceof AppBskyContactVerifyPhone.RateLimitExceededError
|
||||||
@@ -119,16 +114,14 @@ export function VerifyNumber({
|
|||||||
setError({
|
setError({
|
||||||
retryable: true,
|
retryable: true,
|
||||||
isResendError: false,
|
isResendError: false,
|
||||||
message: _(
|
message: l`Too many attempts. Please wait a few minutes and try again.`,
|
||||||
msg`Too many attempts. Please wait a few minutes and try again.`,
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
logger.error('Verify phone number failed', {safeMessage: err})
|
logger.error('Verify phone number failed', {safeMessage: err})
|
||||||
setError({
|
setError({
|
||||||
retryable: true,
|
retryable: true,
|
||||||
isResendError: false,
|
isResendError: false,
|
||||||
message: _(msg`An error occurred. ${cleanError(err)}`),
|
message: l`An error occurred. ${cleanError(err)}`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -140,7 +133,7 @@ export function VerifyNumber({
|
|||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
dispatch({type: 'RESEND_VERIFICATION_CODE'})
|
dispatch({type: 'RESEND_VERIFICATION_CODE'})
|
||||||
Toast.show(_(msg`A new code has been sent`))
|
Toast.show(l`A new code has been sent`)
|
||||||
},
|
},
|
||||||
onMutate: () => {
|
onMutate: () => {
|
||||||
setOtpCode('')
|
setOtpCode('')
|
||||||
@@ -151,9 +144,7 @@ export function VerifyNumber({
|
|||||||
setError({
|
setError({
|
||||||
retryable: true,
|
retryable: true,
|
||||||
isResendError: true,
|
isResendError: true,
|
||||||
message: _(
|
message: l`A network error occurred. Please check your internet connection.`,
|
||||||
msg`A network error occurred. Please check your internet connection.`,
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
} else if (
|
} else if (
|
||||||
err instanceof AppBskyContactStartPhoneVerification.InvalidPhoneError
|
err instanceof AppBskyContactStartPhoneVerification.InvalidPhoneError
|
||||||
@@ -161,9 +152,7 @@ export function VerifyNumber({
|
|||||||
setError({
|
setError({
|
||||||
retryable: false,
|
retryable: false,
|
||||||
isResendError: true,
|
isResendError: true,
|
||||||
message: _(
|
message: l`The verification provider was unable to send a code to your phone number. Please check your phone number and try again.`,
|
||||||
msg`The verification provider was unable to send a code to your phone number. Please check your phone number and try again.`,
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
} else if (
|
} else if (
|
||||||
err instanceof
|
err instanceof
|
||||||
@@ -172,16 +161,14 @@ export function VerifyNumber({
|
|||||||
setError({
|
setError({
|
||||||
retryable: true,
|
retryable: true,
|
||||||
isResendError: true,
|
isResendError: true,
|
||||||
message: _(
|
message: l`Too many codes sent. Please wait a few minutes and try again.`,
|
||||||
msg`Too many codes sent. Please wait a few minutes and try again.`,
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
logger.error('Resend failed', {safeMessage: err})
|
logger.error('Resend failed', {safeMessage: err})
|
||||||
setError({
|
setError({
|
||||||
retryable: true,
|
retryable: true,
|
||||||
isResendError: true,
|
isResendError: true,
|
||||||
message: _(msg`An error occurred. ${cleanError(err)}`),
|
message: l`An error occurred. ${cleanError(err)}`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -199,7 +186,7 @@ export function VerifyNumber({
|
|||||||
size="small"
|
size="small"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
label={_(msg`Skip contact sharing and continue to the app`)}
|
label={l`Skip contact sharing and continue to the app`}
|
||||||
onPress={onSkip}>
|
onPress={onSkip}>
|
||||||
<ButtonText>
|
<ButtonText>
|
||||||
<Trans>Skip</Trans>
|
<Trans>Skip</Trans>
|
||||||
@@ -227,9 +214,7 @@ export function VerifyNumber({
|
|||||||
</Text>
|
</Text>
|
||||||
<View style={[a.mt_2xl]}>
|
<View style={[a.mt_2xl]}>
|
||||||
<OTPInput
|
<OTPInput
|
||||||
label={_(
|
label={l`Enter 6-digit code that was sent to your phone number`}
|
||||||
msg`Enter 6-digit code that was sent to your phone number`,
|
|
||||||
)}
|
|
||||||
value={otpCode}
|
value={otpCode}
|
||||||
onChange={setOtpCode}
|
onChange={setOtpCode}
|
||||||
onComplete={code => verifyNumber(code)}
|
onComplete={code => verifyNumber(code)}
|
||||||
@@ -276,7 +261,7 @@ function OTPStatus({
|
|||||||
onRetry: () => void
|
onRetry: () => void
|
||||||
lastCodeSentAt: Date | null
|
lastCodeSentAt: Date | null
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
|
||||||
const [time, setTime] = useState(Date.now())
|
const [time, setTime] = useState(Date.now())
|
||||||
@@ -301,10 +286,10 @@ function OTPStatus({
|
|||||||
|
|
||||||
if (isSuccess) {
|
if (isSuccess) {
|
||||||
Icon = CircleCheckIcon
|
Icon = CircleCheckIcon
|
||||||
text = _(msg`Phone number verified`)
|
text = l`Phone number verified`
|
||||||
textColor = t.palette.positive_500
|
textColor = t.palette.positive_500
|
||||||
} else if (isPending) {
|
} else if (isPending) {
|
||||||
text = _(msg`Verifying...`)
|
text = l`Verifying...`
|
||||||
} else if (error) {
|
} else if (error) {
|
||||||
Icon = WarningIcon
|
Icon = WarningIcon
|
||||||
text = error.message
|
text = error.message
|
||||||
@@ -342,12 +327,11 @@ function OTPStatus({
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{showRetryButton && (
|
{showRetryButton && (
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
color="secondary_inverted"
|
color="secondary_inverted"
|
||||||
label={_(msg`Retry`)}
|
label={l`Retry`}
|
||||||
onPress={onRetry}
|
onPress={onRetry}
|
||||||
style={[a.mt_2xl]}>
|
style={[a.mt_2xl]}>
|
||||||
<ButtonIcon icon={RetryIcon} />
|
<ButtonIcon icon={RetryIcon} />
|
||||||
@@ -356,13 +340,12 @@ function OTPStatus({
|
|||||||
</ButtonText>
|
</ButtonText>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{showResendButton && (
|
{showResendButton && (
|
||||||
<Button
|
<Button
|
||||||
size="large"
|
size="large"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
label={_(msg`Resend code`)}
|
label={l`Resend code`}
|
||||||
disabled={isResendingCode || isWaiting}
|
disabled={isResendingCode || isWaiting}
|
||||||
onPress={onResend}
|
onPress={onResend}
|
||||||
style={[a.mt_2xl]}>
|
style={[a.mt_2xl]}>
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ import {View} from 'react-native'
|
|||||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||||
import * as SMS from 'expo-sms'
|
import * as SMS from 'expo-sms'
|
||||||
import {type ModerationOpts} from '@atproto/api'
|
import {type ModerationOpts} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Plural, Trans} from '@lingui/react/macro'
|
import {Plural, Trans} from '@lingui/react/macro'
|
||||||
import {useMutation, useQueryClient} from '@tanstack/react-query'
|
import {useMutation, useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
@@ -84,7 +83,7 @@ export function ViewMatches({
|
|||||||
onNext: () => void
|
onNext: () => void
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const gutter = useGutters([0, 'wide'])
|
const gutter = useGutters([0, 'wide'])
|
||||||
const moderationOpts = useModerationOpts()
|
const moderationOpts = useModerationOpts()
|
||||||
@@ -141,11 +140,11 @@ export function ViewMatches({
|
|||||||
}),
|
}),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
setDidFollowAll(true)
|
setDidFollowAll(true)
|
||||||
Toast.show(_(msg`All friends followed!`), {type: 'success'})
|
Toast.show(l`All friends followed!`, {type: 'success'})
|
||||||
cumulativeFollowCount.current += followableDids.length
|
cumulativeFollowCount.current += followableDids.length
|
||||||
},
|
},
|
||||||
onError: _err => {
|
onError: _err => {
|
||||||
Toast.show(_(msg`Failed to follow all your friends, please try again`), {
|
Toast.show(l`Failed to follow all your friends, please try again`, {
|
||||||
type: 'error',
|
type: 'error',
|
||||||
})
|
})
|
||||||
for (const did of followableDids) {
|
for (const did of followableDids) {
|
||||||
@@ -232,15 +231,13 @@ export function ViewMatches({
|
|||||||
dispatch({type: 'DISMISS_MATCH_FAILED', payload: {did}})
|
dispatch({type: 'DISMISS_MATCH_FAILED', payload: {did}})
|
||||||
if (isNetworkError(err)) {
|
if (isNetworkError(err)) {
|
||||||
Toast.show(
|
Toast.show(
|
||||||
_(
|
l`Failed to hide suggestion, please check your internet connection`,
|
||||||
msg`Failed to hide suggestion, please check your internet connection`,
|
|
||||||
),
|
|
||||||
{type: 'error'},
|
{type: 'error'},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
logger.error('Dismissing match failed', {safeMessage: err})
|
logger.error('Dismissing match failed', {safeMessage: err})
|
||||||
Toast.show(
|
Toast.show(
|
||||||
_(msg`An error occurred while hiding suggestion. ${cleanError(err)}`),
|
l`An error occurred while hiding suggestion. ${cleanError(err)}`,
|
||||||
{type: 'error'},
|
{type: 'error'},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -273,7 +270,7 @@ export function ViewMatches({
|
|||||||
}>
|
}>
|
||||||
{item.count > 1 && (
|
{item.count > 1 && (
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Follow all`)}
|
label={l`Follow all`}
|
||||||
size="small"
|
size="small"
|
||||||
color="primary_subtle"
|
color="primary_subtle"
|
||||||
onPress={() => followAll()}
|
onPress={() => followAll()}
|
||||||
@@ -309,7 +306,7 @@ export function ViewMatches({
|
|||||||
case 'no matches header':
|
case 'no matches header':
|
||||||
return (
|
return (
|
||||||
<Header
|
<Header
|
||||||
titleText={_(msg`You got here first`)}
|
titleText={l`You got here first`}
|
||||||
largeTitle
|
largeTitle
|
||||||
subtitleText={
|
subtitleText={
|
||||||
<Trans>
|
<Trans>
|
||||||
@@ -353,7 +350,7 @@ export function ViewMatches({
|
|||||||
]}>
|
]}>
|
||||||
{context === 'Onboarding' && <OnboardingPosition />}
|
{context === 'Onboarding' && <OnboardingPosition />}
|
||||||
<SearchInput
|
<SearchInput
|
||||||
placeholder={_(msg`Search contacts`)}
|
placeholder={l`Search contacts`}
|
||||||
value={search}
|
value={search}
|
||||||
onFocus={() => {
|
onFocus={() => {
|
||||||
onFocus()
|
onFocus()
|
||||||
@@ -392,7 +389,7 @@ export function ViewMatches({
|
|||||||
{paddingBottom: insets.bottom + tokens.space.md},
|
{paddingBottom: insets.bottom + tokens.space.md},
|
||||||
]}>
|
]}>
|
||||||
<Button
|
<Button
|
||||||
label={context === 'Onboarding' ? _(msg`Next`) : _(msg`Done`)}
|
label={context === 'Onboarding' ? l`Next` : l`Done`}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
if (context === 'Onboarding') {
|
if (context === 'Onboarding') {
|
||||||
ax.metric('onboarding:contacts:nextPressed', {
|
ax.metric('onboarding:contacts:nextPressed', {
|
||||||
@@ -444,19 +441,19 @@ function MatchItem({
|
|||||||
}) {
|
}) {
|
||||||
const gutter = useGutters([0, 'wide'])
|
const gutter = useGutters([0, 'wide'])
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const shadow = useProfileShadow(profile)
|
const shadow = useProfileShadow(profile)
|
||||||
|
|
||||||
const contactName = useMemo(() => {
|
const contactName = useMemo(() => {
|
||||||
if (!contact) return null
|
if (!contact) return null
|
||||||
|
|
||||||
const name = contact.name ?? contact.firstName ?? contact.lastName
|
const name = contact.name ?? contact.firstName ?? contact.lastName
|
||||||
if (name) return _(msg`Your contact ${name}`)
|
if (name) return l`Your contact ${name}`
|
||||||
const phone =
|
const phone =
|
||||||
contact.phoneNumbers?.find(p => p.isPrimary) ?? contact.phoneNumbers?.[0]
|
contact.phoneNumbers?.find(p => p.isPrimary) ?? contact.phoneNumbers?.[0]
|
||||||
if (phone?.number) return phone.number
|
if (phone?.number) return phone.number
|
||||||
return null
|
return null
|
||||||
}, [contact, _])
|
}, [contact, l])
|
||||||
|
|
||||||
if (!moderationOpts) return null
|
if (!moderationOpts) return null
|
||||||
|
|
||||||
@@ -497,7 +494,7 @@ function MatchItem({
|
|||||||
<Button
|
<Button
|
||||||
color="secondary"
|
color="secondary"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
label={_(msg`Remove suggestion`)}
|
label={l`Remove suggestion`}
|
||||||
onPress={() => onRemoveSuggestion(profile.did)}
|
onPress={() => onRemoveSuggestion(profile.did)}
|
||||||
hoverStyle={[a.bg_transparent, {opacity: 0.5}]}
|
hoverStyle={[a.bg_transparent, {opacity: 0.5}]}
|
||||||
hitSlop={8}>
|
hitSlop={8}>
|
||||||
@@ -518,7 +515,7 @@ function ContactItem({
|
|||||||
}) {
|
}) {
|
||||||
const gutter = useGutters([0, 'wide'])
|
const gutter = useGutters([0, 'wide'])
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
|
|
||||||
@@ -564,7 +561,7 @@ function ContactItem({
|
|||||||
</Text>
|
</Text>
|
||||||
{phoneNumber && currentAccount && (
|
{phoneNumber && currentAccount && (
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Invite ${name} to join Bluesky`)}
|
label={l`Invite ${name} to join Bluesky`}
|
||||||
color="secondary"
|
color="secondary"
|
||||||
size="small"
|
size="small"
|
||||||
onPress={async () => {
|
onPress={async () => {
|
||||||
@@ -574,12 +571,10 @@ function ContactItem({
|
|||||||
try {
|
try {
|
||||||
await SMS.sendSMSAsync(
|
await SMS.sendSMSAsync(
|
||||||
[phoneNumber],
|
[phoneNumber],
|
||||||
_(
|
l`I'm on Bluesky as ${currentAccount.handle} - come find me! https://bsky.app/download`,
|
||||||
msg`I'm on Bluesky as ${currentAccount.handle} - come find me! https://bsky.app/download`,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Toast.show(_(msg`Failed to launch SMS app`), {type: 'error'})
|
Toast.show(l`Failed to launch SMS app`, {type: 'error'})
|
||||||
logger.error('Could not launch SMS', {safeMessage: err})
|
logger.error('Could not launch SMS', {safeMessage: err})
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {useState} from 'react'
|
import {useState} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {wait} from '#/lib/async/wait'
|
import {wait} from '#/lib/async/wait'
|
||||||
@@ -32,13 +31,12 @@ export function DeviceLocationRequestDialog({
|
|||||||
}: Props & {
|
}: Props & {
|
||||||
control: Dialog.DialogOuterProps['control']
|
control: Dialog.DialogOuterProps['control']
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
return (
|
return (
|
||||||
<Dialog.Outer control={control}>
|
<Dialog.Outer control={control}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
|
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
label={_(msg`Update your location`)}
|
label={l`Update your location`}
|
||||||
style={[web({maxWidth: 380})]}>
|
style={[web({maxWidth: 380})]}>
|
||||||
<DeviceLocationRequestDialogInner
|
<DeviceLocationRequestDialogInner
|
||||||
onLocationAcquired={onLocationAcquired}
|
onLocationAcquired={onLocationAcquired}
|
||||||
@@ -51,7 +49,7 @@ export function DeviceLocationRequestDialog({
|
|||||||
|
|
||||||
function DeviceLocationRequestDialogInner({onLocationAcquired}: Props) {
|
function DeviceLocationRequestDialogInner({onLocationAcquired}: Props) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {close} = Dialog.useDialogContext()
|
const {close} = Dialog.useDialogContext()
|
||||||
const requestDeviceLocation = useRequestDeviceGeolocation()
|
const requestDeviceLocation = useRequestDeviceGeolocation()
|
||||||
const cleanError = useCleanError()
|
const cleanError = useCleanError()
|
||||||
@@ -78,13 +76,11 @@ function DeviceLocationRequestDialogInner({onLocationAcquired}: Props) {
|
|||||||
closeDialog: close,
|
closeDialog: close,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
setError(_(msg`Failed to resolve location. Please try again.`))
|
setError(l`Failed to resolve location. Please try again.`)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setError(
|
setError(
|
||||||
_(
|
l`Unable to access location. You'll need to visit your system settings to enable location services for Bluesky.`,
|
||||||
msg`Unable to access location. You'll need to visit your system settings to enable location services for Bluesky.`,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
@@ -114,18 +110,16 @@ function DeviceLocationRequestDialogInner({onLocationAcquired}: Props) {
|
|||||||
</Trans>
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<View style={[a.pb_xs]}>
|
<View style={[a.pb_xs]}>
|
||||||
<Admonition type="error">{error}</Admonition>
|
<Admonition type="error">{error}</Admonition>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<View style={[a.gap_sm]}>
|
<View style={[a.gap_sm]}>
|
||||||
{!dialogDisabled && (
|
{!dialogDisabled && (
|
||||||
<Button
|
<Button
|
||||||
disabled={isRequesting}
|
disabled={isRequesting}
|
||||||
label={_(msg`Allow location access`)}
|
label={l`Allow location access`}
|
||||||
onPress={onPressConfirm}
|
onPress={onPressConfirm}
|
||||||
size={IS_WEB ? 'small' : 'large'}
|
size={IS_WEB ? 'small' : 'large'}
|
||||||
color="primary">
|
color="primary">
|
||||||
@@ -138,7 +132,7 @@ function DeviceLocationRequestDialogInner({onLocationAcquired}: Props) {
|
|||||||
|
|
||||||
{!IS_WEB && (
|
{!IS_WEB && (
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Cancel`)}
|
label={l`Cancel`}
|
||||||
onPress={() => close()}
|
onPress={() => close()}
|
||||||
size={IS_WEB ? 'small' : 'large'}
|
size={IS_WEB ? 'small' : 'large'}
|
||||||
color="secondary">
|
color="secondary">
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {memo, useEffect, useMemo, useState} from 'react'
|
import {memo, useEffect, useMemo, useState} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {type AppBskyActorDefs, type AppBskyFeedPost, AtUri} from '@atproto/api'
|
import {type AppBskyActorDefs, type AppBskyFeedPost, AtUri} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {EMBED_SCRIPT} from '#/lib/constants'
|
import {EMBED_SCRIPT} from '#/lib/constants'
|
||||||
@@ -51,7 +50,7 @@ function EmbedDialogInner({
|
|||||||
timestamp,
|
timestamp,
|
||||||
}: Omit<EmbedDialogProps, 'control'>) {
|
}: Omit<EmbedDialogProps, 'control'>) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_, i18n} = useLingui()
|
const {t: l, i18n} = useLingui()
|
||||||
const [copied, setCopied] = useState(false)
|
const [copied, setCopied] = useState(false)
|
||||||
const [showCustomisation, setShowCustomisation] = useState(false)
|
const [showCustomisation, setShowCustomisation] = useState(false)
|
||||||
const [colorMode, setColorMode] = useState<ColorModeValues>('system')
|
const [colorMode, setColorMode] = useState<ColorModeValues>('system')
|
||||||
@@ -102,7 +101,7 @@ function EmbedDialogInner({
|
|||||||
}, [i18n, postUri, postCid, record, timestamp, postAuthor, colorMode])
|
}, [i18n, postUri, postCid, record, timestamp, postAuthor, colorMode])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.Inner label={_(msg`Embed post`)} style={[{maxWidth: 500}]}>
|
<Dialog.Inner label={l`Embed post`} style={[{maxWidth: 500}]}>
|
||||||
<View style={[a.gap_lg]}>
|
<View style={[a.gap_lg]}>
|
||||||
<View style={[a.gap_sm]}>
|
<View style={[a.gap_sm]}>
|
||||||
<Text style={[a.text_2xl, a.font_bold]}>
|
<Text style={[a.text_2xl, a.font_bold]}>
|
||||||
@@ -121,8 +120,8 @@ function EmbedDialogInner({
|
|||||||
<Button
|
<Button
|
||||||
label={
|
label={
|
||||||
showCustomisation
|
showCustomisation
|
||||||
? _(msg`Hide customization options`)
|
? l`Hide customization options`
|
||||||
: _(msg`Show customization options`)
|
: l`Show customization options`
|
||||||
}
|
}
|
||||||
color="secondary"
|
color="secondary"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@@ -147,21 +146,21 @@ function EmbedDialogInner({
|
|||||||
<Trans>Color theme</Trans>
|
<Trans>Color theme</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<SegmentedControl.Root
|
<SegmentedControl.Root
|
||||||
label={_(msg`Color mode`)}
|
label={l`Color mode`}
|
||||||
type="radio"
|
type="radio"
|
||||||
value={colorMode}
|
value={colorMode}
|
||||||
onChange={setColorMode}>
|
onChange={setColorMode}>
|
||||||
<SegmentedControl.Item value="system" label={_(msg`System`)}>
|
<SegmentedControl.Item value="system" label={l`System`}>
|
||||||
<SegmentedControl.ItemText>
|
<SegmentedControl.ItemText>
|
||||||
<Trans>System</Trans>
|
<Trans>System</Trans>
|
||||||
</SegmentedControl.ItemText>
|
</SegmentedControl.ItemText>
|
||||||
</SegmentedControl.Item>
|
</SegmentedControl.Item>
|
||||||
<SegmentedControl.Item value="light" label={_(msg`Light`)}>
|
<SegmentedControl.Item value="light" label={l`Light`}>
|
||||||
<SegmentedControl.ItemText>
|
<SegmentedControl.ItemText>
|
||||||
<Trans>Light</Trans>
|
<Trans>Light</Trans>
|
||||||
</SegmentedControl.ItemText>
|
</SegmentedControl.ItemText>
|
||||||
</SegmentedControl.Item>
|
</SegmentedControl.Item>
|
||||||
<SegmentedControl.Item value="dark" label={_(msg`Dark`)}>
|
<SegmentedControl.Item value="dark" label={l`Dark`}>
|
||||||
<SegmentedControl.ItemText>
|
<SegmentedControl.ItemText>
|
||||||
<Trans>Dark</Trans>
|
<Trans>Dark</Trans>
|
||||||
</SegmentedControl.ItemText>
|
</SegmentedControl.ItemText>
|
||||||
@@ -175,7 +174,7 @@ function EmbedDialogInner({
|
|||||||
<TextField.Root>
|
<TextField.Root>
|
||||||
<TextField.Icon icon={CodeBracketsIcon} />
|
<TextField.Icon icon={CodeBracketsIcon} />
|
||||||
<TextField.Input
|
<TextField.Input
|
||||||
label={_(msg`Embed HTML code`)}
|
label={l`Embed HTML code`}
|
||||||
editable={false}
|
editable={false}
|
||||||
selection={{start: 0, end: snippet.length}}
|
selection={{start: 0, end: snippet.length}}
|
||||||
value={snippet}
|
value={snippet}
|
||||||
@@ -183,7 +182,7 @@ function EmbedDialogInner({
|
|||||||
</TextField.Root>
|
</TextField.Root>
|
||||||
</View>
|
</View>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Copy code`)}
|
label={l`Copy code`}
|
||||||
color="primary"
|
color="primary"
|
||||||
size="large"
|
size="large"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {useCallback} from 'react'
|
import {useCallback} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -25,7 +24,7 @@ export function EmbedConsentDialog({
|
|||||||
source: EmbedPlayerSource
|
source: EmbedPlayerSource
|
||||||
onAccept: () => void
|
onAccept: () => void
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const setExternalEmbedPref = useSetExternalEmbedPref()
|
const setExternalEmbedPref = useSetExternalEmbedPref()
|
||||||
|
|
||||||
const onShowAllPress = useCallback(() => {
|
const onShowAllPress = useCallback(() => {
|
||||||
@@ -51,7 +50,7 @@ export function EmbedConsentDialog({
|
|||||||
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
|
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
label={_(msg`External Media`)}
|
label={l`External Media`}
|
||||||
style={web({maxWidth: 400})}>
|
style={web({maxWidth: 400})}>
|
||||||
<View style={a.gap_sm}>
|
<View style={a.gap_sm}>
|
||||||
<Text style={[a.text_2xl, a.font_bold]}>
|
<Text style={[a.text_2xl, a.font_bold]}>
|
||||||
@@ -77,7 +76,7 @@ export function EmbedConsentDialog({
|
|||||||
</View>
|
</View>
|
||||||
<View style={a.gap_md}>
|
<View style={a.gap_md}>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Enable external media`)}
|
label={l`Enable external media`}
|
||||||
onPress={onShowAllPress}
|
onPress={onShowAllPress}
|
||||||
onAccessibilityEscape={control.close}
|
onAccessibilityEscape={control.close}
|
||||||
color="primary"
|
color="primary"
|
||||||
@@ -87,7 +86,7 @@ export function EmbedConsentDialog({
|
|||||||
</ButtonText>
|
</ButtonText>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Enable this source only`)}
|
label={l`Enable this source only`}
|
||||||
onPress={onShowPress}
|
onPress={onShowPress}
|
||||||
onAccessibilityEscape={control.close}
|
onAccessibilityEscape={control.close}
|
||||||
color="secondary"
|
color="secondary"
|
||||||
@@ -97,7 +96,7 @@ export function EmbedConsentDialog({
|
|||||||
</ButtonText>
|
</ButtonText>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`No thanks`)}
|
label={l`No thanks`}
|
||||||
onAccessibilityEscape={control.close}
|
onAccessibilityEscape={control.close}
|
||||||
onPress={onHidePress}
|
onPress={onHidePress}
|
||||||
color="secondary"
|
color="secondary"
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {useCallback} from 'react'
|
import {useCallback} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {useOpenLink} from '#/lib/hooks/useOpenLink'
|
import {useOpenLink} from '#/lib/hooks/useOpenLink'
|
||||||
@@ -32,7 +31,7 @@ export function InAppBrowserConsentDialog() {
|
|||||||
|
|
||||||
function InAppBrowserConsentInner({href}: {href?: string}) {
|
function InAppBrowserConsentInner({href}: {href?: string}) {
|
||||||
const control = Dialog.useDialogContext()
|
const control = Dialog.useDialogContext()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const setInAppBrowser = useSetInAppBrowser()
|
const setInAppBrowser = useSetInAppBrowser()
|
||||||
const openLink = useOpenLink()
|
const openLink = useOpenLink()
|
||||||
@@ -60,7 +59,7 @@ function InAppBrowserConsentInner({href}: {href?: string}) {
|
|||||||
}, [control])
|
}, [control])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.ScrollableInner label={_(msg`How should we open this link?`)}>
|
<Dialog.ScrollableInner label={l`How should we open this link?`}>
|
||||||
<View style={[a.gap_2xl]}>
|
<View style={[a.gap_2xl]}>
|
||||||
<View style={[a.gap_sm]}>
|
<View style={[a.gap_sm]}>
|
||||||
<Text style={[a.font_bold, a.text_2xl]}>
|
<Text style={[a.font_bold, a.text_2xl]}>
|
||||||
@@ -75,7 +74,7 @@ function InAppBrowserConsentInner({href}: {href?: string}) {
|
|||||||
</View>
|
</View>
|
||||||
<View style={[a.gap_sm]}>
|
<View style={[a.gap_sm]}>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Use in-app browser`)}
|
label={l`Use in-app browser`}
|
||||||
onPress={onUseIAB}
|
onPress={onUseIAB}
|
||||||
size="large"
|
size="large"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
@@ -85,7 +84,7 @@ function InAppBrowserConsentInner({href}: {href?: string}) {
|
|||||||
</ButtonText>
|
</ButtonText>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Use my default browser`)}
|
label={l`Use my default browser`}
|
||||||
onPress={onUseLinking}
|
onPress={onUseLinking}
|
||||||
size="large"
|
size="large"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
@@ -96,7 +95,7 @@ function InAppBrowserConsentInner({href}: {href?: string}) {
|
|||||||
<ButtonIcon position="right" icon={External} />
|
<ButtonIcon position="right" icon={External} />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Cancel`)}
|
label={l`Cancel`}
|
||||||
onPress={onCancel}
|
onPress={onCancel}
|
||||||
size="large"
|
size="large"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {useCallback, useMemo, useState} 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 {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {languageName} from '#/locale/helpers'
|
import {languageName} from '#/locale/helpers'
|
||||||
@@ -107,7 +106,7 @@ export function DialogInner({
|
|||||||
const [search, setSearch] = useState('')
|
const [search, setSearch] = useState('')
|
||||||
|
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
control.close(() => {
|
control.close(() => {
|
||||||
@@ -218,7 +217,7 @@ export function DialogInner({
|
|||||||
size="small"
|
size="small"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
shape="round"
|
shape="round"
|
||||||
label={_(msg`Close dialog`)}
|
label={l`Close dialog`}
|
||||||
onPress={handleClose}>
|
onPress={handleClose}>
|
||||||
<ButtonIcon icon={XIcon} />
|
<ButtonIcon icon={XIcon} />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -229,8 +228,8 @@ export function DialogInner({
|
|||||||
<SearchInput
|
<SearchInput
|
||||||
value={search}
|
value={search}
|
||||||
onChangeText={setSearch}
|
onChangeText={setSearch}
|
||||||
placeholder={_(msg`Search languages`)}
|
placeholder={l`Search languages`}
|
||||||
label={_(msg`Search languages`)}
|
label={l`Search languages`}
|
||||||
maxLength={50}
|
maxLength={50}
|
||||||
onClearText={() => setSearch('')}
|
onClearText={() => setSearch('')}
|
||||||
/>
|
/>
|
||||||
@@ -246,13 +245,13 @@ export function DialogInner({
|
|||||||
|
|
||||||
const flatListData = [
|
const flatListData = [
|
||||||
...(isCheckedRecentEmpty
|
...(isCheckedRecentEmpty
|
||||||
? [{type: 'header', label: _(msg`Recently used`)}]
|
? [{type: 'header', label: l`Recently used`}]
|
||||||
: []),
|
: []),
|
||||||
...displayedLanguages.checkedRecent.map(lang => ({type: 'item', lang})),
|
...displayedLanguages.checkedRecent.map(lang => ({type: 'item', lang})),
|
||||||
...displayedLanguages.uncheckedRecent.map(lang => ({type: 'item', lang})),
|
...displayedLanguages.uncheckedRecent.map(lang => ({type: 'item', lang})),
|
||||||
...(isDisplayedLanguagesEmpty
|
...(isDisplayedLanguagesEmpty
|
||||||
? []
|
? []
|
||||||
: [{type: 'header', label: _(msg`All languages`)}]),
|
: [{type: 'header', label: l`All languages`}]),
|
||||||
...displayedLanguages.all.map(lang => ({type: 'item', lang})),
|
...displayedLanguages.all.map(lang => ({type: 'item', lang})),
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -264,7 +263,7 @@ export function DialogInner({
|
|||||||
onChange={setCheckedLanguagesCode2}
|
onChange={setCheckedLanguagesCode2}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
maxSelections={maxLanguages}
|
maxSelections={maxLanguages}
|
||||||
label={_(msg`Select languages`)}
|
label={l`Select languages`}
|
||||||
style={web([a.contents])}>
|
style={web([a.contents])}>
|
||||||
<Dialog.InnerFlatList
|
<Dialog.InnerFlatList
|
||||||
data={flatListData}
|
data={flatListData}
|
||||||
@@ -319,7 +318,7 @@ export function DialogInner({
|
|||||||
<Dialog.FlatListFooter
|
<Dialog.FlatListFooter
|
||||||
onLayout={evt => setFooterHeight(evt.nativeEvent.layout.height)}>
|
onLayout={evt => setFooterHeight(evt.nativeEvent.layout.height)}>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Close dialog`)}
|
label={l`Close dialog`}
|
||||||
onPress={handleClose}
|
onPress={handleClose}
|
||||||
color="primary"
|
color="primary"
|
||||||
size="large">
|
size="large">
|
||||||
@@ -335,23 +334,19 @@ export function DialogInner({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function DialogError({details}: {details?: string}) {
|
function DialogError({details}: {details?: string}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const control = Dialog.useDialogContext()
|
const control = Dialog.useDialogContext()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner style={a.gap_md} label={l`An error has occurred`}>
|
||||||
style={a.gap_md}
|
|
||||||
label={_(msg`An error has occurred`)}>
|
|
||||||
<Dialog.Close />
|
<Dialog.Close />
|
||||||
<ErrorScreen
|
<ErrorScreen
|
||||||
title={_(msg`Oh no!`)}
|
title={l`Oh no!`}
|
||||||
message={_(
|
message={l`There was an unexpected issue in the application. Please let us know if this happened to you!`}
|
||||||
msg`There was an unexpected issue in the application. Please let us know if this happened to you!`,
|
|
||||||
)}
|
|
||||||
details={details}
|
details={details}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Close dialog`)}
|
label={l`Close dialog`}
|
||||||
onPress={() => control.close()}
|
onPress={() => control.close()}
|
||||||
color="primary"
|
color="primary"
|
||||||
size="large">
|
size="large">
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {useCallback, useMemo} from 'react'
|
import {useCallback, useMemo} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {useOpenLink} from '#/lib/hooks/useOpenLink'
|
import {useOpenLink} from '#/lib/hooks/useOpenLink'
|
||||||
@@ -49,7 +48,7 @@ function LinkWarningDialogInner({
|
|||||||
link?: {href: string; displayText: string; share?: boolean}
|
link?: {href: string; displayText: string; share?: boolean}
|
||||||
}) {
|
}) {
|
||||||
const control = Dialog.useDialogContext()
|
const control = Dialog.useDialogContext()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const openLink = useOpenLink()
|
const openLink = useOpenLink()
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
@@ -79,8 +78,8 @@ function LinkWarningDialogInner({
|
|||||||
style={web({maxWidth: 450})}
|
style={web({maxWidth: 450})}
|
||||||
label={
|
label={
|
||||||
potentiallyMisleading
|
potentiallyMisleading
|
||||||
? _(msg`Potentially misleading link warning`)
|
? l`Potentially misleading link warning`
|
||||||
: _(msg`Leaving Bluesky`)
|
: l`Leaving Bluesky`
|
||||||
}>
|
}>
|
||||||
<View style={[a.gap_2xl]}>
|
<View style={[a.gap_2xl]}>
|
||||||
<View style={[a.gap_sm]}>
|
<View style={[a.gap_sm]}>
|
||||||
@@ -109,8 +108,8 @@ function LinkWarningDialogInner({
|
|||||||
gtMobile && [a.flex_row_reverse, a.justify_start],
|
gtMobile && [a.flex_row_reverse, a.justify_start],
|
||||||
]}>
|
]}>
|
||||||
<Button
|
<Button
|
||||||
label={link?.share ? _(msg`Share link`) : _(msg`Visit site`)}
|
label={link?.share ? l`Share link` : l`Visit site`}
|
||||||
accessibilityHint={_(msg`Opens link ${link?.href ?? ''}`)}
|
accessibilityHint={l`Opens link ${link?.href ?? ''}`}
|
||||||
onPress={onPressVisit}
|
onPress={onPressVisit}
|
||||||
size="large"
|
size="large"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
@@ -124,7 +123,7 @@ function LinkWarningDialogInner({
|
|||||||
</ButtonText>
|
</ButtonText>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Go back`)}
|
label={l`Go back`}
|
||||||
onPress={onCancel}
|
onPress={onCancel}
|
||||||
size="large"
|
size="large"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {useCallback, useState} from 'react'
|
import {useCallback, useState} 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 {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
@@ -50,7 +49,7 @@ export function MutedWordsDialog() {
|
|||||||
|
|
||||||
function MutedWordsInner() {
|
function MutedWordsInner() {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
const {
|
const {
|
||||||
isLoading: isPreferencesLoading,
|
isLoading: isPreferencesLoading,
|
||||||
@@ -86,7 +85,7 @@ function MutedWordsInner() {
|
|||||||
|
|
||||||
if (!sanitizedValue || !surfaces.length) {
|
if (!sanitizedValue || !surfaces.length) {
|
||||||
setField('')
|
setField('')
|
||||||
setError(_(msg`Please enter a valid word, tag, or phrase to mute`))
|
setError(l`Please enter a valid word, tag, or phrase to mute`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,10 +104,10 @@ function MutedWordsInner() {
|
|||||||
logger.error(`Failed to save muted word`, {message: e.message})
|
logger.error(`Failed to save muted word`, {message: e.message})
|
||||||
setError(e.message)
|
setError(e.message)
|
||||||
}
|
}
|
||||||
}, [_, field, targets, addMutedWord, setField, durations, excludeFollowing])
|
}, [l, field, targets, addMutedWord, setField, durations, excludeFollowing])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.ScrollableInner label={_(msg`Manage your muted words and tags`)}>
|
<Dialog.ScrollableInner label={l`Manage your muted words and tags`}>
|
||||||
<View>
|
<View>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
@@ -133,8 +132,8 @@ function MutedWordsInner() {
|
|||||||
autoCapitalize="none"
|
autoCapitalize="none"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
returnKeyType="done"
|
returnKeyType="done"
|
||||||
label={_(msg`Enter a word or tag`)}
|
label={l`Enter a word or tag`}
|
||||||
placeholder={_(msg`Enter a word or tag`)}
|
placeholder={l`Enter a word or tag`}
|
||||||
value={field}
|
value={field}
|
||||||
onChangeText={value => {
|
onChangeText={value => {
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -148,7 +147,7 @@ function MutedWordsInner() {
|
|||||||
|
|
||||||
<View style={[a.pb_xl, a.gap_sm]}>
|
<View style={[a.pb_xl, a.gap_sm]}>
|
||||||
<Toggle.Group
|
<Toggle.Group
|
||||||
label={_(msg`Select how long to mute this word for.`)}
|
label={l`Select how long to mute this word for.`}
|
||||||
type="radio"
|
type="radio"
|
||||||
values={durations}
|
values={durations}
|
||||||
onChange={setDurations}>
|
onChange={setDurations}>
|
||||||
@@ -176,7 +175,7 @@ function MutedWordsInner() {
|
|||||||
a.gap_sm,
|
a.gap_sm,
|
||||||
]}>
|
]}>
|
||||||
<Toggle.Item
|
<Toggle.Item
|
||||||
label={_(msg`Mute this word until you unmute it`)}
|
label={l`Mute this word until you unmute it`}
|
||||||
name="forever"
|
name="forever"
|
||||||
style={[a.flex_1]}>
|
style={[a.flex_1]}>
|
||||||
<TargetToggle>
|
<TargetToggle>
|
||||||
@@ -191,7 +190,7 @@ function MutedWordsInner() {
|
|||||||
</Toggle.Item>
|
</Toggle.Item>
|
||||||
|
|
||||||
<Toggle.Item
|
<Toggle.Item
|
||||||
label={_(msg`Mute this word for 24 hours`)}
|
label={l`Mute this word for 24 hours`}
|
||||||
name="24_hours"
|
name="24_hours"
|
||||||
style={[a.flex_1]}>
|
style={[a.flex_1]}>
|
||||||
<TargetToggle>
|
<TargetToggle>
|
||||||
@@ -215,7 +214,7 @@ function MutedWordsInner() {
|
|||||||
a.gap_sm,
|
a.gap_sm,
|
||||||
]}>
|
]}>
|
||||||
<Toggle.Item
|
<Toggle.Item
|
||||||
label={_(msg`Mute this word for 7 days`)}
|
label={l`Mute this word for 7 days`}
|
||||||
name="7_days"
|
name="7_days"
|
||||||
style={[a.flex_1]}>
|
style={[a.flex_1]}>
|
||||||
<TargetToggle>
|
<TargetToggle>
|
||||||
@@ -230,7 +229,7 @@ function MutedWordsInner() {
|
|||||||
</Toggle.Item>
|
</Toggle.Item>
|
||||||
|
|
||||||
<Toggle.Item
|
<Toggle.Item
|
||||||
label={_(msg`Mute this word for 30 days`)}
|
label={l`Mute this word for 30 days`}
|
||||||
name="30_days"
|
name="30_days"
|
||||||
style={[a.flex_1]}>
|
style={[a.flex_1]}>
|
||||||
<TargetToggle>
|
<TargetToggle>
|
||||||
@@ -248,7 +247,7 @@ function MutedWordsInner() {
|
|||||||
</Toggle.Group>
|
</Toggle.Group>
|
||||||
|
|
||||||
<Toggle.Group
|
<Toggle.Group
|
||||||
label={_(msg`Select what content this mute word should apply to.`)}
|
label={l`Select what content this mute word should apply to.`}
|
||||||
type="radio"
|
type="radio"
|
||||||
values={targets}
|
values={targets}
|
||||||
onChange={setTargets}>
|
onChange={setTargets}>
|
||||||
@@ -264,7 +263,7 @@ function MutedWordsInner() {
|
|||||||
|
|
||||||
<View style={[a.flex_row, a.align_center, a.gap_sm, a.flex_wrap]}>
|
<View style={[a.flex_row, a.align_center, a.gap_sm, a.flex_wrap]}>
|
||||||
<Toggle.Item
|
<Toggle.Item
|
||||||
label={_(msg`Mute this word in post text and tags`)}
|
label={l`Mute this word in post text and tags`}
|
||||||
name="content"
|
name="content"
|
||||||
style={[a.flex_1]}>
|
style={[a.flex_1]}>
|
||||||
<TargetToggle>
|
<TargetToggle>
|
||||||
@@ -280,7 +279,7 @@ function MutedWordsInner() {
|
|||||||
</Toggle.Item>
|
</Toggle.Item>
|
||||||
|
|
||||||
<Toggle.Item
|
<Toggle.Item
|
||||||
label={_(msg`Mute this word in tags only`)}
|
label={l`Mute this word in tags only`}
|
||||||
name="tag"
|
name="tag"
|
||||||
style={[a.flex_1]}>
|
style={[a.flex_1]}>
|
||||||
<TargetToggle>
|
<TargetToggle>
|
||||||
@@ -308,7 +307,7 @@ function MutedWordsInner() {
|
|||||||
<Trans>Options:</Trans>
|
<Trans>Options:</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<Toggle.Item
|
<Toggle.Item
|
||||||
label={_(msg`Do not apply this mute word to users you follow`)}
|
label={l`Do not apply this mute word to users you follow`}
|
||||||
name="exclude_following"
|
name="exclude_following"
|
||||||
style={[a.flex_row, a.justify_between]}
|
style={[a.flex_row, a.justify_between]}
|
||||||
value={excludeFollowing}
|
value={excludeFollowing}
|
||||||
@@ -327,7 +326,7 @@ function MutedWordsInner() {
|
|||||||
<View style={[a.pt_xs]}>
|
<View style={[a.pt_xs]}>
|
||||||
<Button
|
<Button
|
||||||
disabled={isPending || !field}
|
disabled={isPending || !field}
|
||||||
label={_(msg`Add mute word with chosen settings`)}
|
label={l`Add mute word with chosen settings`}
|
||||||
size="large"
|
size="large"
|
||||||
color="primary"
|
color="primary"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
@@ -412,7 +411,6 @@ function MutedWordsInner() {
|
|||||||
|
|
||||||
{IS_NATIVE && <View style={{height: 20}} />}
|
{IS_NATIVE && <View style={{height: 20}} />}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Dialog.Close />
|
<Dialog.Close />
|
||||||
</Dialog.ScrollableInner>
|
</Dialog.ScrollableInner>
|
||||||
)
|
)
|
||||||
@@ -423,7 +421,7 @@ function MutedWordRow({
|
|||||||
word,
|
word,
|
||||||
}: ViewStyleProp & {word: AppBskyActorDefs.MutedWord}) {
|
}: ViewStyleProp & {word: AppBskyActorDefs.MutedWord}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {isPending, mutateAsync: removeMutedWord} = useRemoveMutedWordMutation()
|
const {isPending, mutateAsync: removeMutedWord} = useRemoveMutedWordMutation()
|
||||||
const {mutateAsync: updateMutedWord} = useUpdateMutedWordMutation()
|
const {mutateAsync: updateMutedWord} = useUpdateMutedWordMutation()
|
||||||
const control = Prompt.usePromptControl()
|
const control = Prompt.usePromptControl()
|
||||||
@@ -449,15 +447,12 @@ function MutedWordRow({
|
|||||||
<>
|
<>
|
||||||
<Prompt.Basic
|
<Prompt.Basic
|
||||||
control={control}
|
control={control}
|
||||||
title={_(msg`Are you sure?`)}
|
title={l`Are you sure?`}
|
||||||
description={_(
|
description={l`This will delete "${word.value}" from your muted words. You can always add it back later.`}
|
||||||
msg`This will delete "${word.value}" from your muted words. You can always add it back later.`,
|
|
||||||
)}
|
|
||||||
onConfirm={remove}
|
onConfirm={remove}
|
||||||
confirmButtonCta={_(msg`Remove`)}
|
confirmButtonCta={l`Remove`}
|
||||||
confirmButtonColor="negative"
|
confirmButtonColor="negative"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
a.flex_row,
|
a.flex_row,
|
||||||
@@ -528,7 +523,7 @@ function MutedWordRow({
|
|||||||
{' · '}
|
{' · '}
|
||||||
</Text>
|
</Text>
|
||||||
<Menu.Root>
|
<Menu.Root>
|
||||||
<Menu.Trigger label={_(msg`Renew mute word`)}>
|
<Menu.Trigger label={l`Renew mute word`}>
|
||||||
{({props}) => (
|
{({props}) => (
|
||||||
<Text
|
<Text
|
||||||
{...props}
|
{...props}
|
||||||
@@ -548,29 +543,25 @@ function MutedWordRow({
|
|||||||
</Menu.LabelText>
|
</Menu.LabelText>
|
||||||
<Menu.Group>
|
<Menu.Group>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
label={_(msg`24 hours`)}
|
label={l`24 hours`}
|
||||||
onPress={() => renew(1)}>
|
onPress={() => renew(1)}>
|
||||||
<Menu.ItemText>
|
<Menu.ItemText>
|
||||||
<Trans>24 hours</Trans>
|
<Trans>24 hours</Trans>
|
||||||
</Menu.ItemText>
|
</Menu.ItemText>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item label={l`7 days`} onPress={() => renew(7)}>
|
||||||
label={_(msg`7 days`)}
|
|
||||||
onPress={() => renew(7)}>
|
|
||||||
<Menu.ItemText>
|
<Menu.ItemText>
|
||||||
<Trans>7 days</Trans>
|
<Trans>7 days</Trans>
|
||||||
</Menu.ItemText>
|
</Menu.ItemText>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
label={_(msg`30 days`)}
|
label={l`30 days`}
|
||||||
onPress={() => renew(30)}>
|
onPress={() => renew(30)}>
|
||||||
<Menu.ItemText>
|
<Menu.ItemText>
|
||||||
<Trans>30 days</Trans>
|
<Trans>30 days</Trans>
|
||||||
</Menu.ItemText>
|
</Menu.ItemText>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item label={l`Forever`} onPress={() => renew()}>
|
||||||
label={_(msg`Forever`)}
|
|
||||||
onPress={() => renew()}>
|
|
||||||
<Menu.ItemText>
|
<Menu.ItemText>
|
||||||
<Trans>Forever</Trans>
|
<Trans>Forever</Trans>
|
||||||
</Menu.ItemText>
|
</Menu.ItemText>
|
||||||
@@ -610,7 +601,7 @@ function MutedWordRow({
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Remove mute word from your list`)}
|
label={l`Remove mute word from your list`}
|
||||||
size="tiny"
|
size="tiny"
|
||||||
shape="round"
|
shape="round"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ import {
|
|||||||
type AppBskyFeedPostgate,
|
type AppBskyFeedPostgate,
|
||||||
AtUri,
|
AtUri,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Plural, Trans} from '@lingui/react/macro'
|
import {Plural, Trans} from '@lingui/react/macro'
|
||||||
import {useQueryClient} from '@tanstack/react-query'
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
@@ -109,11 +108,11 @@ export function PostInteractionSettingsControlledDialog({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function DialogInner(props: Omit<PostInteractionSettingsFormProps, 'control'>) {
|
function DialogInner(props: Omit<PostInteractionSettingsFormProps, 'control'>) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
label={_(msg`Edit post interaction settings`)}
|
label={l`Edit post interaction settings`}
|
||||||
style={[web({maxWidth: 400}), a.w_full]}>
|
style={[web({maxWidth: 400}), a.w_full]}>
|
||||||
<Header />
|
<Header />
|
||||||
<PostInteractionSettingsForm {...props} />
|
<PostInteractionSettingsForm {...props} />
|
||||||
@@ -164,7 +163,7 @@ export function PostInteractionSettingsDialogControlledInner(
|
|||||||
props: PostInteractionSettingsDialogProps,
|
props: PostInteractionSettingsDialogProps,
|
||||||
) {
|
) {
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const [isSaving, setIsSaving] = useState(false)
|
const [isSaving, setIsSaving] = useState(false)
|
||||||
|
|
||||||
@@ -237,9 +236,7 @@ export function PostInteractionSettingsDialogControlledInner(
|
|||||||
safeMessage: e.message,
|
safeMessage: e.message,
|
||||||
})
|
})
|
||||||
Toast.show(
|
Toast.show(
|
||||||
_(
|
l`There was an issue. Please check your internet connection and try again.`,
|
||||||
msg`There was an issue. Please check your internet connection and try again.`,
|
|
||||||
),
|
|
||||||
{
|
{
|
||||||
type: 'error',
|
type: 'error',
|
||||||
},
|
},
|
||||||
@@ -248,7 +245,7 @@ export function PostInteractionSettingsDialogControlledInner(
|
|||||||
setIsSaving(false)
|
setIsSaving(false)
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
_,
|
l,
|
||||||
ax,
|
ax,
|
||||||
props.postUri,
|
props.postUri,
|
||||||
props.rootPostUri,
|
props.rootPostUri,
|
||||||
@@ -263,7 +260,7 @@ export function PostInteractionSettingsDialogControlledInner(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
label={_(msg`Edit post interaction settings`)}
|
label={l`Edit post interaction settings`}
|
||||||
style={[web({maxWidth: 400}), a.w_full]}>
|
style={[web({maxWidth: 400}), a.w_full]}>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<View
|
<View
|
||||||
@@ -312,7 +309,7 @@ export function PostInteractionSettingsForm({
|
|||||||
onChangePersist,
|
onChangePersist,
|
||||||
}: PostInteractionSettingsFormProps) {
|
}: PostInteractionSettingsFormProps) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const playHaptic = useHaptics()
|
const playHaptic = useHaptics()
|
||||||
const [showLists, setShowLists] = useState(false)
|
const [showLists, setShowLists] = useState(false)
|
||||||
const {
|
const {
|
||||||
@@ -428,7 +425,7 @@ export function PostInteractionSettingsForm({
|
|||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Toggle.Group
|
<Toggle.Group
|
||||||
label={_(msg`Set who can reply to your post`)}
|
label={l`Set who can reply to your post`}
|
||||||
type="radio"
|
type="radio"
|
||||||
maxSelections={1}
|
maxSelections={1}
|
||||||
disabled={replySettingsDisabled}
|
disabled={replySettingsDisabled}
|
||||||
@@ -448,7 +445,7 @@ export function PostInteractionSettingsForm({
|
|||||||
<Toggle.Item
|
<Toggle.Item
|
||||||
name="everyone"
|
name="everyone"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
label={_(msg`Allow anyone to reply`)}
|
label={l`Allow anyone to reply`}
|
||||||
style={[a.flex_1]}>
|
style={[a.flex_1]}>
|
||||||
{({selected}) => (
|
{({selected}) => (
|
||||||
<Toggle.Panel active={selected}>
|
<Toggle.Panel active={selected}>
|
||||||
@@ -462,7 +459,7 @@ export function PostInteractionSettingsForm({
|
|||||||
<Toggle.Item
|
<Toggle.Item
|
||||||
name="nobody"
|
name="nobody"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
label={_(msg`Disable replies entirely`)}
|
label={l`Disable replies entirely`}
|
||||||
style={[a.flex_1]}>
|
style={[a.flex_1]}>
|
||||||
{({selected}) => (
|
{({selected}) => (
|
||||||
<Toggle.Panel active={selected}>
|
<Toggle.Panel active={selected}>
|
||||||
@@ -477,9 +474,7 @@ export function PostInteractionSettingsForm({
|
|||||||
</Toggle.Group>
|
</Toggle.Group>
|
||||||
|
|
||||||
<Toggle.Group
|
<Toggle.Group
|
||||||
label={_(
|
label={l`Set precisely which groups of people can reply to your post`}
|
||||||
msg`Set precisely which groups of people can reply to your post`,
|
|
||||||
)}
|
|
||||||
values={toggleGroupValues}
|
values={toggleGroupValues}
|
||||||
onChange={toggleGroupOnChange}
|
onChange={toggleGroupOnChange}
|
||||||
disabled={replySettingsDisabled}>
|
disabled={replySettingsDisabled}>
|
||||||
@@ -487,7 +482,7 @@ export function PostInteractionSettingsForm({
|
|||||||
<Toggle.Item
|
<Toggle.Item
|
||||||
name="followers"
|
name="followers"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
label={_(msg`Allow your followers to reply`)}
|
label={l`Allow your followers to reply`}
|
||||||
hitSlop={0}>
|
hitSlop={0}>
|
||||||
{({selected}) => (
|
{({selected}) => (
|
||||||
<Toggle.Panel active={selected} adjacent="trailing">
|
<Toggle.Panel active={selected} adjacent="trailing">
|
||||||
@@ -501,7 +496,7 @@ export function PostInteractionSettingsForm({
|
|||||||
<Toggle.Item
|
<Toggle.Item
|
||||||
name="following"
|
name="following"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
label={_(msg`Allow people you follow to reply`)}
|
label={l`Allow people you follow to reply`}
|
||||||
hitSlop={0}>
|
hitSlop={0}>
|
||||||
{({selected}) => (
|
{({selected}) => (
|
||||||
<Toggle.Panel active={selected} adjacent="both">
|
<Toggle.Panel active={selected} adjacent="both">
|
||||||
@@ -515,7 +510,7 @@ export function PostInteractionSettingsForm({
|
|||||||
<Toggle.Item
|
<Toggle.Item
|
||||||
name="mention"
|
name="mention"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
label={_(msg`Allow people you mention to reply`)}
|
label={l`Allow people you mention to reply`}
|
||||||
hitSlop={0}>
|
hitSlop={0}>
|
||||||
{({selected}) => (
|
{({selected}) => (
|
||||||
<Toggle.Panel active={selected} adjacent="both">
|
<Toggle.Panel active={selected} adjacent="both">
|
||||||
@@ -530,8 +525,8 @@ export function PostInteractionSettingsForm({
|
|||||||
<Button
|
<Button
|
||||||
label={
|
label={
|
||||||
showLists
|
showLists
|
||||||
? _(msg`Hide lists`)
|
? l`Hide lists`
|
||||||
: _(msg`Show lists of users to select from`)
|
: l`Show lists of users to select from`
|
||||||
}
|
}
|
||||||
accessibilityRole="togglebutton"
|
accessibilityRole="togglebutton"
|
||||||
hitSlop={0}
|
hitSlop={0}
|
||||||
@@ -595,7 +590,7 @@ export function PostInteractionSettingsForm({
|
|||||||
key={list.uri}
|
key={list.uri}
|
||||||
name={`list:${list.uri}`}
|
name={`list:${list.uri}`}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
label={_(msg`Allow users in ${list.name} to reply`)}
|
label={l`Allow users in ${list.name} to reply`}
|
||||||
hitSlop={0}>
|
hitSlop={0}>
|
||||||
{({selected}) => (
|
{({selected}) => (
|
||||||
<Toggle.Panel
|
<Toggle.Panel
|
||||||
@@ -619,14 +614,13 @@ export function PostInteractionSettingsForm({
|
|||||||
</Toggle.Group>
|
</Toggle.Group>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Toggle.Item
|
<Toggle.Item
|
||||||
name="quoteposts"
|
name="quoteposts"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
label={
|
label={
|
||||||
quotesEnabled
|
quotesEnabled
|
||||||
? _(msg`Disable quote posts of this post`)
|
? l`Disable quote posts of this post`
|
||||||
: _(msg`Enable quote posts of this post`)
|
: l`Enable quote posts of this post`
|
||||||
}
|
}
|
||||||
value={quotesEnabled}
|
value={quotesEnabled}
|
||||||
onChange={onChangeQuotesEnabled}>
|
onChange={onChangeQuotesEnabled}>
|
||||||
@@ -639,14 +633,13 @@ export function PostInteractionSettingsForm({
|
|||||||
</Toggle.Panel>
|
</Toggle.Panel>
|
||||||
)}
|
)}
|
||||||
</Toggle.Item>
|
</Toggle.Item>
|
||||||
|
|
||||||
{typeof persist !== 'undefined' && (
|
{typeof persist !== 'undefined' && (
|
||||||
<View style={[{minHeight: 24}, a.justify_center]}>
|
<View style={[{minHeight: 24}, a.justify_center]}>
|
||||||
{isDirty ? (
|
{isDirty ? (
|
||||||
<Toggle.Item
|
<Toggle.Item
|
||||||
name="persist"
|
name="persist"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
label={_(msg`Save these options for next time`)}
|
label={l`Save these options for next time`}
|
||||||
value={persist}
|
value={persist}
|
||||||
onChange={() => onChangePersist?.(!persist)}>
|
onChange={() => onChangePersist?.(!persist)}>
|
||||||
<Toggle.Checkbox />
|
<Toggle.Checkbox />
|
||||||
@@ -662,10 +655,9 @@ export function PostInteractionSettingsForm({
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
disabled={!canSave || isSaving}
|
disabled={!canSave || isSaving}
|
||||||
label={_(msg`Save`)}
|
label={l`Save`}
|
||||||
onPress={onSave}
|
onPress={onSave}
|
||||||
color="primary"
|
color="primary"
|
||||||
size="large">
|
size="large">
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {useCallback, useImperativeHandle, useRef, useState} from 'react'
|
import {useCallback, useImperativeHandle, useRef, useState} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {BSKY_SERVICE} from '#/lib/constants'
|
import {BSKY_SERVICE} from '#/lib/constants'
|
||||||
@@ -78,7 +77,7 @@ function DialogInner({
|
|||||||
initialCustomAddress: string
|
initialCustomAddress: string
|
||||||
}) {
|
}) {
|
||||||
const control = Dialog.useDialogContext()
|
const control = Dialog.useDialogContext()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {accounts} = useSession()
|
const {accounts} = useSession()
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
@@ -135,24 +134,20 @@ function DialogInner({
|
|||||||
</Text>
|
</Text>
|
||||||
<SegmentedControl.Root
|
<SegmentedControl.Root
|
||||||
type="tabs"
|
type="tabs"
|
||||||
label={_(msg`Hosting provider`)}
|
label={l`Hosting provider`}
|
||||||
value={fixedOption}
|
value={fixedOption}
|
||||||
onChange={setFixedOption}>
|
onChange={setFixedOption}>
|
||||||
<SegmentedControl.Item
|
<SegmentedControl.Item
|
||||||
testID="bskyServiceSelectBtn"
|
testID="bskyServiceSelectBtn"
|
||||||
value={BSKY_SERVICE}
|
value={BSKY_SERVICE}
|
||||||
label={_(msg`Bluesky`)}>
|
label={l`Bluesky`}>
|
||||||
<SegmentedControl.ItemText>
|
<SegmentedControl.ItemText>{l`Bluesky`}</SegmentedControl.ItemText>
|
||||||
{_(msg`Bluesky`)}
|
|
||||||
</SegmentedControl.ItemText>
|
|
||||||
</SegmentedControl.Item>
|
</SegmentedControl.Item>
|
||||||
<SegmentedControl.Item
|
<SegmentedControl.Item
|
||||||
testID="customSelectBtn"
|
testID="customSelectBtn"
|
||||||
value="custom"
|
value="custom"
|
||||||
label={_(msg`Custom`)}>
|
label={l`Custom`}>
|
||||||
<SegmentedControl.ItemText>
|
<SegmentedControl.ItemText>{l`Custom`}</SegmentedControl.ItemText>
|
||||||
{_(msg`Custom`)}
|
|
||||||
</SegmentedControl.ItemText>
|
|
||||||
</SegmentedControl.Item>
|
</SegmentedControl.Item>
|
||||||
</SegmentedControl.Root>
|
</SegmentedControl.Root>
|
||||||
|
|
||||||
@@ -217,7 +212,7 @@ function DialogInner({
|
|||||||
</Trans>
|
</Trans>
|
||||||
)}{' '}
|
)}{' '}
|
||||||
<InlineLinkText
|
<InlineLinkText
|
||||||
label={_(msg`Learn more about self hosting your PDS.`)}
|
label={l`Learn more about self hosting your PDS.`}
|
||||||
to="https://atproto.com/guides/self-hosting">
|
to="https://atproto.com/guides/self-hosting">
|
||||||
<Trans>Learn more.</Trans>
|
<Trans>Learn more.</Trans>
|
||||||
</InlineLinkText>
|
</InlineLinkText>
|
||||||
@@ -234,7 +229,7 @@ function DialogInner({
|
|||||||
web: 'small',
|
web: 'small',
|
||||||
})}
|
})}
|
||||||
onPress={() => control.close()}
|
onPress={() => control.close()}
|
||||||
label={_(msg`Done`)}>
|
label={l`Done`}>
|
||||||
<ButtonText>
|
<ButtonText>
|
||||||
<Trans>Done</Trans>
|
<Trans>Done</Trans>
|
||||||
</ButtonText>
|
</ButtonText>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {useCallback} from 'react'
|
import {useCallback} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
|
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
|
||||||
@@ -27,7 +26,7 @@ export function SigninDialog() {
|
|||||||
|
|
||||||
function SigninDialogInner({}: {control: Dialog.DialogOuterProps['control']}) {
|
function SigninDialogInner({}: {control: Dialog.DialogOuterProps['control']}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
const {requestSwitchToAccount} = useLoggedOutViewControls()
|
const {requestSwitchToAccount} = useLoggedOutViewControls()
|
||||||
const closeAllActiveElements = useCloseAllActiveElements()
|
const closeAllActiveElements = useCloseAllActiveElements()
|
||||||
@@ -44,7 +43,7 @@ function SigninDialogInner({}: {control: Dialog.DialogOuterProps['control']}) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
label={_(msg`Sign in to Bluesky or create a new account`)}
|
label={l`Sign in to Bluesky or create a new account`}
|
||||||
style={[gtMobile ? {width: 'auto', maxWidth: 420} : a.w_full]}>
|
style={[gtMobile ? {width: 'auto', maxWidth: 420} : a.w_full]}>
|
||||||
<View style={[!IS_NATIVE && a.p_2xl]}>
|
<View style={[!IS_NATIVE && a.p_2xl]}>
|
||||||
<View
|
<View
|
||||||
@@ -84,7 +83,7 @@ function SigninDialogInner({}: {control: Dialog.DialogOuterProps['control']}) {
|
|||||||
color="primary"
|
color="primary"
|
||||||
size="large"
|
size="large"
|
||||||
onPress={showCreateAccount}
|
onPress={showCreateAccount}
|
||||||
label={_(msg`Create an account`)}>
|
label={l`Create an account`}>
|
||||||
<ButtonText>
|
<ButtonText>
|
||||||
<Trans>Create an account</Trans>
|
<Trans>Create an account</Trans>
|
||||||
</ButtonText>
|
</ButtonText>
|
||||||
@@ -95,7 +94,7 @@ function SigninDialogInner({}: {control: Dialog.DialogOuterProps['control']}) {
|
|||||||
color="secondary"
|
color="secondary"
|
||||||
size="large"
|
size="large"
|
||||||
onPress={showSignIn}
|
onPress={showSignIn}
|
||||||
label={_(msg`Sign in`)}>
|
label={l`Sign in`}>
|
||||||
<ButtonText>
|
<ButtonText>
|
||||||
<Trans>Sign in</Trans>
|
<Trans>Sign in</Trans>
|
||||||
</ButtonText>
|
</ButtonText>
|
||||||
@@ -104,7 +103,6 @@ function SigninDialogInner({}: {control: Dialog.DialogOuterProps['control']}) {
|
|||||||
|
|
||||||
{IS_NATIVE && <View style={{height: 10}} />}
|
{IS_NATIVE && <View style={{height: 10}} />}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Dialog.Close />
|
<Dialog.Close />
|
||||||
</Dialog.ScrollableInner>
|
</Dialog.ScrollableInner>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ import {
|
|||||||
type AppBskyGraphGetStarterPacksWithMembership,
|
type AppBskyGraphGetStarterPacksWithMembership,
|
||||||
AppBskyGraphStarterpack,
|
AppBskyGraphStarterpack,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Plural, Trans} from '@lingui/react/macro'
|
import {Plural, Trans} from '@lingui/react/macro'
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
@@ -88,7 +87,7 @@ export function StarterPackDialog({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Empty({onStartWizard}: {onStartWizard: () => void}) {
|
function Empty({onStartWizard}: {onStartWizard: () => void}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -102,10 +101,9 @@ function Empty({onStartWizard}: {onStartWizard: () => void}) {
|
|||||||
<Trans>You have no starter packs.</Trans>
|
<Trans>You have no starter packs.</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={[a.align_center]}>
|
<View style={[a.align_center]}>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Create starter pack`)}
|
label={l`Create starter pack`}
|
||||||
color="secondary_inverted"
|
color="secondary_inverted"
|
||||||
size="small"
|
size="small"
|
||||||
onPress={onStartWizard}>
|
onPress={onStartWizard}>
|
||||||
@@ -131,7 +129,7 @@ function StarterPackList({
|
|||||||
enabled?: boolean
|
enabled?: boolean
|
||||||
}) {
|
}) {
|
||||||
const control = Dialog.useDialogContext()
|
const control = Dialog.useDialogContext()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {data: subject} = useProfileQuery({did: targetDid})
|
const {data: subject} = useProfileQuery({did: targetDid})
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -184,7 +182,7 @@ function StarterPackList({
|
|||||||
<Trans>Add to starter packs</Trans>
|
<Trans>Add to starter packs</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Close`)}
|
label={l`Close`}
|
||||||
onPress={onClose}
|
onPress={onClose}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
@@ -202,7 +200,7 @@ function StarterPackList({
|
|||||||
<Trans>New starter pack</Trans>
|
<Trans>New starter pack</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Create starter pack`)}
|
label={l`Create starter pack`}
|
||||||
color="secondary_inverted"
|
color="secondary_inverted"
|
||||||
size="small"
|
size="small"
|
||||||
onPress={onStartWizard}>
|
onPress={onStartWizard}>
|
||||||
@@ -260,7 +258,7 @@ function StarterPackItem({
|
|||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const isSelf = subject?.did === currentAccount?.did
|
const isSelf = subject?.did === currentAccount?.did
|
||||||
|
|
||||||
@@ -271,26 +269,26 @@ function StarterPackItem({
|
|||||||
useListMembershipAddMutation({
|
useListMembershipAddMutation({
|
||||||
subject,
|
subject,
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
Toast.show(_(msg`Added to starter pack`))
|
Toast.show(l`Added to starter pack`)
|
||||||
},
|
},
|
||||||
onError: err => {
|
onError: err => {
|
||||||
if (!isNetworkError(err)) {
|
if (!isNetworkError(err)) {
|
||||||
logger.error('Failed to add to starter pack', {safeMessage: err})
|
logger.error('Failed to add to starter pack', {safeMessage: err})
|
||||||
}
|
}
|
||||||
Toast.show(_(msg`Failed to add to starter pack`), {type: 'error'})
|
Toast.show(l`Failed to add to starter pack`, {type: 'error'})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const {mutate: removeMembership, isPending: isPendingRemove} =
|
const {mutate: removeMembership, isPending: isPendingRemove} =
|
||||||
useListMembershipRemoveMutation({
|
useListMembershipRemoveMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
Toast.show(_(msg`Removed from starter pack`))
|
Toast.show(l`Removed from starter pack`)
|
||||||
},
|
},
|
||||||
onError: err => {
|
onError: err => {
|
||||||
if (!isNetworkError(err)) {
|
if (!isNetworkError(err)) {
|
||||||
logger.error('Failed to remove from starter pack', {safeMessage: err})
|
logger.error('Failed to remove from starter pack', {safeMessage: err})
|
||||||
}
|
}
|
||||||
Toast.show(_(msg`Failed to remove from starter pack`), {type: 'error'})
|
Toast.show(l`Failed to remove from starter pack`, {type: 'error'})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -371,9 +369,8 @@ function StarterPackItem({
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
label={isInPack ? _(msg`Remove`) : _(msg`Add`)}
|
label={isInPack ? l`Remove` : l`Add`}
|
||||||
color={isInPack ? 'secondary' : 'primary_subtle'}
|
color={isInPack ? 'secondary' : 'primary_subtle'}
|
||||||
size="tiny"
|
size="tiny"
|
||||||
disabled={isPending || isSelf}
|
disabled={isPending || isSelf}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {useCallback} from 'react'
|
import {useCallback} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher'
|
import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher'
|
||||||
@@ -17,7 +16,7 @@ export function SwitchAccountDialog({
|
|||||||
}: {
|
}: {
|
||||||
control: Dialog.DialogControlProps
|
control: Dialog.DialogControlProps
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const {onPressSwitchAccount, pendingDid} = useAccountSwitcher()
|
const {onPressSwitchAccount, pendingDid} = useAccountSwitcher()
|
||||||
const {setShowLoggedOut} = useLoggedOutViewControls()
|
const {setShowLoggedOut} = useLoggedOutViewControls()
|
||||||
@@ -44,7 +43,7 @@ export function SwitchAccountDialog({
|
|||||||
return (
|
return (
|
||||||
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
|
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
<Dialog.ScrollableInner label={_(msg`Switch account`)}>
|
<Dialog.ScrollableInner label={l`Switch account`}>
|
||||||
<View style={[a.gap_lg]}>
|
<View style={[a.gap_lg]}>
|
||||||
<Text style={[a.text_2xl, a.font_semi_bold]}>
|
<Text style={[a.text_2xl, a.font_semi_bold]}>
|
||||||
<Trans>Switch account</Trans>
|
<Trans>Switch account</Trans>
|
||||||
@@ -53,7 +52,7 @@ export function SwitchAccountDialog({
|
|||||||
<AccountList
|
<AccountList
|
||||||
onSelectAccount={onSelectAccount}
|
onSelectAccount={onSelectAccount}
|
||||||
onSelectOther={onPressAddAccount}
|
onSelectOther={onPressAddAccount}
|
||||||
otherLabel={_(msg`Add account`)}
|
otherLabel={l`Add account`}
|
||||||
pendingDid={pendingDid}
|
pendingDid={pendingDid}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ import {
|
|||||||
type ComAtprotoRepoApplyWrites,
|
type ComAtprotoRepoApplyWrites,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {TID} from '@atproto/common-web'
|
import {TID} from '@atproto/common-web'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
import {useQueryClient} from '@tanstack/react-query'
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
@@ -38,7 +37,7 @@ export function CreateListFromStarterPackDialog({
|
|||||||
control: Dialog.DialogControlProps
|
control: Dialog.DialogControlProps
|
||||||
starterPack: AppBskyGraphDefs.StarterPackView
|
starterPack: AppBskyGraphDefs.StarterPackView
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
@@ -124,7 +123,7 @@ export function CreateListFromStarterPackDialog({
|
|||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Failed to add members to list', {safeMessage: e})
|
logger.error('Failed to add members to list', {safeMessage: e})
|
||||||
Toast.show(_(msg`List created, but failed to add some members`), {
|
Toast.show(l`List created, but failed to add some members`, {
|
||||||
type: 'error',
|
type: 'error',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -145,7 +144,7 @@ export function CreateListFromStarterPackDialog({
|
|||||||
nativeOptions={{preventExpansion: true}}>
|
nativeOptions={{preventExpansion: true}}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
label={_(msg`Create list from starter pack`)}
|
label={l`Create list from starter pack`}
|
||||||
style={web({maxWidth: 400})}>
|
style={web({maxWidth: 400})}>
|
||||||
<View style={[a.gap_lg]}>
|
<View style={[a.gap_lg]}>
|
||||||
<Text style={[a.text_xl, a.font_bold]}>
|
<Text style={[a.text_xl, a.font_bold]}>
|
||||||
@@ -177,7 +176,7 @@ export function CreateListFromStarterPackDialog({
|
|||||||
a.pt_sm,
|
a.pt_sm,
|
||||||
]}>
|
]}>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Create list`)}
|
label={l`Create list`}
|
||||||
onPress={onPressCreate}
|
onPress={onPressCreate}
|
||||||
size={platform({
|
size={platform({
|
||||||
web: 'small',
|
web: 'small',
|
||||||
@@ -189,7 +188,7 @@ export function CreateListFromStarterPackDialog({
|
|||||||
</ButtonText>
|
</ButtonText>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Cancel`)}
|
label={l`Cancel`}
|
||||||
onPress={() => control.close()}
|
onPress={() => control.close()}
|
||||||
size={platform({
|
size={platform({
|
||||||
web: 'small',
|
web: 'small',
|
||||||
@@ -205,7 +204,6 @@ export function CreateListFromStarterPackDialog({
|
|||||||
<Dialog.Close />
|
<Dialog.Close />
|
||||||
</Dialog.ScrollableInner>
|
</Dialog.ScrollableInner>
|
||||||
</Dialog.Outer>
|
</Dialog.Outer>
|
||||||
|
|
||||||
<CreateOrEditListDialog
|
<CreateOrEditListDialog
|
||||||
control={createDialogControl}
|
control={createDialogControl}
|
||||||
purpose="app.bsky.graph.defs#curatelist"
|
purpose="app.bsky.graph.defs#curatelist"
|
||||||
@@ -216,13 +214,12 @@ export function CreateListFromStarterPackDialog({
|
|||||||
avatar: starterPack.list?.avatar,
|
avatar: starterPack.list?.avatar,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Dialog.Outer
|
<Dialog.Outer
|
||||||
control={loadingDialogControl}
|
control={loadingDialogControl}
|
||||||
nativeOptions={{preventDismiss: true}}>
|
nativeOptions={{preventDismiss: true}}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
label={_(msg`Adding members to list...`)}
|
label={l`Adding members to list...`}
|
||||||
style={web({maxWidth: 400})}>
|
style={web({maxWidth: 400})}>
|
||||||
<View style={[a.align_center, a.gap_lg, a.py_5xl]}>
|
<View style={[a.align_center, a.gap_lg, a.py_5xl]}>
|
||||||
<Loader size="xl" />
|
<Loader size="xl" />
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {useCallback, useEffect, useMemo, useState} from 'react'
|
import {useCallback, useEffect, useMemo, useState} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {type AppBskyGraphDefs, RichText as RichTextAPI} from '@atproto/api'
|
import {type AppBskyGraphDefs, RichText as RichTextAPI} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Plural, Trans} from '@lingui/react/macro'
|
import {Plural, Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {cleanError} from '#/lib/strings/errors'
|
import {cleanError} from '#/lib/strings/errors'
|
||||||
@@ -50,7 +49,7 @@ export function CreateOrEditListDialog({
|
|||||||
onSave?: (uri: string) => void
|
onSave?: (uri: string) => void
|
||||||
initialValues?: InitialListValues
|
initialValues?: InitialListValues
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const cancelControl = Dialog.useDialogControl()
|
const cancelControl = Dialog.useDialogControl()
|
||||||
const [dirty, setDirty] = useState(false)
|
const [dirty, setDirty] = useState(false)
|
||||||
|
|
||||||
@@ -92,13 +91,12 @@ export function CreateOrEditListDialog({
|
|||||||
onPressCancel={onPressCancel}
|
onPressCancel={onPressCancel}
|
||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Prompt.Basic
|
<Prompt.Basic
|
||||||
control={cancelControl}
|
control={cancelControl}
|
||||||
title={_(msg`Discard changes?`)}
|
title={l`Discard changes?`}
|
||||||
description={_(msg`Are you sure you want to discard your changes?`)}
|
description={l`Are you sure you want to discard your changes?`}
|
||||||
onConfirm={() => control.close()}
|
onConfirm={() => control.close()}
|
||||||
confirmButtonCta={_(msg`Discard`)}
|
confirmButtonCta={l`Discard`}
|
||||||
confirmButtonColor="negative"
|
confirmButtonColor="negative"
|
||||||
/>
|
/>
|
||||||
</Dialog.Outer>
|
</Dialog.Outer>
|
||||||
@@ -131,7 +129,7 @@ function DialogInner({
|
|||||||
}, [list, purpose])
|
}, [list, purpose])
|
||||||
const isCurateList = activePurpose === 'app.bsky.graph.defs#curatelist'
|
const isCurateList = activePurpose === 'app.bsky.graph.defs#curatelist'
|
||||||
|
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
const control = Dialog.useDialogContext()
|
const control = Dialog.useDialogContext()
|
||||||
@@ -238,8 +236,8 @@ function DialogInner({
|
|||||||
})
|
})
|
||||||
Toast.show(
|
Toast.show(
|
||||||
isCurateList
|
isCurateList
|
||||||
? _(msg({message: 'User list updated', context: 'toast'}))
|
? l({message: 'User list updated', context: 'toast'})
|
||||||
: _(msg({message: 'Moderation list updated', context: 'toast'})),
|
: l({message: 'Moderation list updated', context: 'toast'}),
|
||||||
)
|
)
|
||||||
control.close(() => onSave?.(list.uri))
|
control.close(() => onSave?.(list.uri))
|
||||||
} else {
|
} else {
|
||||||
@@ -252,8 +250,8 @@ function DialogInner({
|
|||||||
})
|
})
|
||||||
Toast.show(
|
Toast.show(
|
||||||
isCurateList
|
isCurateList
|
||||||
? _(msg({message: 'User list created', context: 'toast'}))
|
? l({message: 'User list created', context: 'toast'})
|
||||||
: _(msg({message: 'Moderation list created', context: 'toast'})),
|
: l({message: 'Moderation list created', context: 'toast'}),
|
||||||
)
|
)
|
||||||
control.close(() => onSave?.(uri))
|
control.close(() => onSave?.(uri))
|
||||||
}
|
}
|
||||||
@@ -273,7 +271,7 @@ function DialogInner({
|
|||||||
activePurpose,
|
activePurpose,
|
||||||
isCurateList,
|
isCurateList,
|
||||||
agent,
|
agent,
|
||||||
_,
|
l,
|
||||||
])
|
])
|
||||||
|
|
||||||
const displayNameTooLong = isOverMaxGraphemeCount({
|
const displayNameTooLong = isOverMaxGraphemeCount({
|
||||||
@@ -288,7 +286,7 @@ function DialogInner({
|
|||||||
const cancelButton = useCallback(
|
const cancelButton = useCallback(
|
||||||
() => (
|
() => (
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Cancel`)}
|
label={l`Cancel`}
|
||||||
onPress={onPressCancel}
|
onPress={onPressCancel}
|
||||||
size="small"
|
size="small"
|
||||||
color="primary"
|
color="primary"
|
||||||
@@ -300,13 +298,13 @@ function DialogInner({
|
|||||||
</ButtonText>
|
</ButtonText>
|
||||||
</Button>
|
</Button>
|
||||||
),
|
),
|
||||||
[onPressCancel, _],
|
[onPressCancel, l],
|
||||||
)
|
)
|
||||||
|
|
||||||
const saveButton = useCallback(
|
const saveButton = useCallback(
|
||||||
() => (
|
() => (
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Save`)}
|
label={l`Save`}
|
||||||
onPress={onPressSave}
|
onPress={onPressSave}
|
||||||
disabled={
|
disabled={
|
||||||
!dirty ||
|
!dirty ||
|
||||||
@@ -327,7 +325,7 @@ function DialogInner({
|
|||||||
</Button>
|
</Button>
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
_,
|
l,
|
||||||
t,
|
t,
|
||||||
dirty,
|
dirty,
|
||||||
onPressSave,
|
onPressSave,
|
||||||
@@ -360,19 +358,19 @@ function DialogInner({
|
|||||||
|
|
||||||
const title = list
|
const title = list
|
||||||
? isCurateList
|
? isCurateList
|
||||||
? _(msg`Edit user list`)
|
? l`Edit user list`
|
||||||
: _(msg`Edit moderation list`)
|
: l`Edit moderation list`
|
||||||
: isCurateList
|
: isCurateList
|
||||||
? _(msg`Create user list`)
|
? l`Create user list`
|
||||||
: _(msg`Create moderation list`)
|
: l`Create moderation list`
|
||||||
|
|
||||||
const displayNamePlaceholder = isCurateList
|
const displayNamePlaceholder = isCurateList
|
||||||
? _(msg`e.g. Great Posters`)
|
? l`e.g. Great Posters`
|
||||||
: _(msg`e.g. Spammers`)
|
: l`e.g. Spammers`
|
||||||
|
|
||||||
const descriptionPlaceholder = isCurateList
|
const descriptionPlaceholder = isCurateList
|
||||||
? _(msg`e.g. The posters who never miss.`)
|
? l`e.g. The posters who never miss.`
|
||||||
: _(msg`e.g. Users that repeatedly reply with ads.`)
|
: l`e.g. Users that repeatedly reply with ads.`
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
@@ -413,7 +411,7 @@ function DialogInner({
|
|||||||
<Dialog.Input
|
<Dialog.Input
|
||||||
defaultValue={displayName}
|
defaultValue={displayName}
|
||||||
onChangeText={onChangeDisplayName}
|
onChangeText={onChangeDisplayName}
|
||||||
label={_(msg`Name`)}
|
label={l`Name`}
|
||||||
placeholder={displayNamePlaceholder}
|
placeholder={displayNamePlaceholder}
|
||||||
testID="editListNameInput"
|
testID="editListNameInput"
|
||||||
/>
|
/>
|
||||||
@@ -450,7 +448,7 @@ function DialogInner({
|
|||||||
defaultValue={descriptionRt.text}
|
defaultValue={descriptionRt.text}
|
||||||
onChangeText={onChangeDescription}
|
onChangeText={onChangeDescription}
|
||||||
multiline
|
multiline
|
||||||
label={_(msg`Description`)}
|
label={l`Description`}
|
||||||
placeholder={descriptionPlaceholder}
|
placeholder={descriptionPlaceholder}
|
||||||
testID="editListDescriptionInput"
|
testID="editListDescriptionInput"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {useCallback, useMemo} from 'react'
|
import {useCallback, useMemo} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {type AppBskyGraphDefs, type ModerationOpts} from '@atproto/api'
|
import {type AppBskyGraphDefs, type ModerationOpts} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {cleanError} from '#/lib/strings/errors'
|
import {cleanError} from '#/lib/strings/errors'
|
||||||
@@ -57,7 +56,7 @@ function DialogInner({
|
|||||||
profile: bsky.profile.AnyProfileView,
|
profile: bsky.profile.AnyProfileView,
|
||||||
) => void | undefined
|
) => void | undefined
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const moderationOpts = useModerationOpts()
|
const moderationOpts = useModerationOpts()
|
||||||
const {data: listMembers} = useAllListMembersQuery(list.uri)
|
const {data: listMembers} = useAllListMembersQuery(list.uri)
|
||||||
|
|
||||||
@@ -78,7 +77,7 @@ function DialogInner({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SearchablePeopleList
|
<SearchablePeopleList
|
||||||
title={_(msg`Add people to list`)}
|
title={l`Add people to list`}
|
||||||
renderProfileCard={renderProfileCard}
|
renderProfileCard={renderProfileCard}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@@ -114,7 +113,7 @@ function UserResult({
|
|||||||
) => void | undefined
|
) => void | undefined
|
||||||
moderationOpts?: ModerationOpts
|
moderationOpts?: ModerationOpts
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const membership = useMemo(
|
const membership = useMemo(
|
||||||
() => getMembership(listMembers, profile.did),
|
() => getMembership(listMembers, profile.did),
|
||||||
[listMembers, profile.did],
|
[listMembers, profile.did],
|
||||||
@@ -122,7 +121,7 @@ function UserResult({
|
|||||||
const {mutate: listMembershipAdd, isPending: isAddingPending} =
|
const {mutate: listMembershipAdd, isPending: isAddingPending} =
|
||||||
useListMembershipAddMutation({
|
useListMembershipAddMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
Toast.show(_(msg`Added to list`))
|
Toast.show(l`Added to list`)
|
||||||
onChange?.('add', profile)
|
onChange?.('add', profile)
|
||||||
},
|
},
|
||||||
onError: e =>
|
onError: e =>
|
||||||
@@ -133,7 +132,7 @@ function UserResult({
|
|||||||
const {mutate: listMembershipRemove, isPending: isRemovingPending} =
|
const {mutate: listMembershipRemove, isPending: isRemovingPending} =
|
||||||
useListMembershipRemoveMutation({
|
useListMembershipRemoveMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
Toast.show(_(msg`Removed from list`))
|
Toast.show(l`Removed from list`)
|
||||||
onChange?.('remove', profile)
|
onChange?.('remove', profile)
|
||||||
},
|
},
|
||||||
onError: e =>
|
onError: e =>
|
||||||
@@ -175,8 +174,8 @@ function UserResult({
|
|||||||
<Button
|
<Button
|
||||||
label={
|
label={
|
||||||
membership === false
|
membership === false
|
||||||
? _(msg`Add user to list`)
|
? l`Add user to list`
|
||||||
: _(msg`Remove user from list`)
|
: l`Remove user from list`
|
||||||
}
|
}
|
||||||
onPress={onToggleMembership}
|
onPress={onToggleMembership}
|
||||||
disabled={isMutating}
|
disabled={isMutating}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {useCallback} from 'react'
|
import {useCallback} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {Image} from 'expo-image'
|
import {Image} from 'expo-image'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {atoms as a, useTheme, web} from '#/alf'
|
import {atoms as a, useTheme, web} from '#/alf'
|
||||||
@@ -15,7 +14,7 @@ import {IS_WEB} from '#/env'
|
|||||||
|
|
||||||
export function ActivitySubscriptionsNUX() {
|
export function ActivitySubscriptionsNUX() {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const nuxDialogs = useNuxDialogContext()
|
const nuxDialogs = useNuxDialogContext()
|
||||||
const control = Dialog.useDialogControl()
|
const control = Dialog.useDialogControl()
|
||||||
|
|
||||||
@@ -28,9 +27,8 @@ export function ActivitySubscriptionsNUX() {
|
|||||||
return (
|
return (
|
||||||
<Dialog.Outer control={control} onClose={onClose}>
|
<Dialog.Outer control={control} onClose={onClose}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
|
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
label={_(msg`Introducing activity notifications`)}
|
label={l`Introducing activity notifications`}
|
||||||
style={[web({maxWidth: 400})]}
|
style={[web({maxWidth: 400})]}
|
||||||
contentContainerStyle={[
|
contentContainerStyle={[
|
||||||
{
|
{
|
||||||
@@ -110,9 +108,7 @@ export function ActivitySubscriptionsNUX() {
|
|||||||
aspectRatio: 398 / 268,
|
aspectRatio: 398 / 268,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
alt={_(
|
alt={l`A screenshot of a profile page with a bell icon next to the follow button, indicating the new activity notifications feature.`}
|
||||||
msg`A screenshot of a profile page with a bell icon next to the follow button, indicating the new activity notifications feature.`,
|
|
||||||
)}
|
|
||||||
useAppleWebpCodec
|
useAppleWebpCodec
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@@ -157,7 +153,7 @@ export function ActivitySubscriptionsNUX() {
|
|||||||
|
|
||||||
{!IS_WEB && (
|
{!IS_WEB && (
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Close`)}
|
label={l`Close`}
|
||||||
size="large"
|
size="large"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import {useCallback} from 'react'
|
|||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {Image} from 'expo-image'
|
import {Image} from 'expo-image'
|
||||||
import {LinearGradient} from 'expo-linear-gradient'
|
import {LinearGradient} from 'expo-linear-gradient'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {atoms as a, useTheme, web} from '#/alf'
|
import {atoms as a, useTheme, web} from '#/alf'
|
||||||
@@ -17,7 +16,7 @@ import {IS_WEB} from '#/env'
|
|||||||
|
|
||||||
export function BookmarksAnnouncement() {
|
export function BookmarksAnnouncement() {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const nuxDialogs = useNuxDialogContext()
|
const nuxDialogs = useNuxDialogContext()
|
||||||
const control = Dialog.useDialogControl()
|
const control = Dialog.useDialogControl()
|
||||||
|
|
||||||
@@ -33,9 +32,8 @@ export function BookmarksAnnouncement() {
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
nativeOptions={{preventExpansion: true}}>
|
nativeOptions={{preventExpansion: true}}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
|
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
label={_(msg`Introducing saved posts AKA bookmarks`)}
|
label={l`Introducing saved posts AKA bookmarks`}
|
||||||
style={[web({maxWidth: 440})]}
|
style={[web({maxWidth: 440})]}
|
||||||
contentContainerStyle={[
|
contentContainerStyle={[
|
||||||
{
|
{
|
||||||
@@ -117,13 +115,11 @@ export function BookmarksAnnouncement() {
|
|||||||
aspectRatio: 333 / 104,
|
aspectRatio: 333 / 104,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
alt={_(
|
alt={l({
|
||||||
msg({
|
message: `A screenshot of a post with a new button next to the share button that allows you to save the post to your bookmarks. The post is from @jcsalterego.bsky.social and reads "inventing a saturday that immediately follows monday".`,
|
||||||
message: `A screenshot of a post with a new button next to the share button that allows you to save the post to your bookmarks. The post is from @jcsalterego.bsky.social and reads "inventing a saturday that immediately follows monday".`,
|
comment:
|
||||||
comment:
|
'Contains a post that originally appeared in English. Consider translating the post text if it makes sense in your language, and noting that the post was translated from English.',
|
||||||
'Contains a post that originally appeared in English. Consider translating the post text if it makes sense in your language, and noting that the post was translated from English.',
|
})}
|
||||||
}),
|
|
||||||
)}
|
|
||||||
useAppleWebpCodec
|
useAppleWebpCodec
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@@ -162,7 +158,7 @@ export function BookmarksAnnouncement() {
|
|||||||
|
|
||||||
{!IS_WEB && (
|
{!IS_WEB && (
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Close`)}
|
label={l`Close`}
|
||||||
size="large"
|
size="large"
|
||||||
color="primary"
|
color="primary"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import {useCallback} from 'react'
|
|||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {Image} from 'expo-image'
|
import {Image} from 'expo-image'
|
||||||
import {LinearGradient} from 'expo-linear-gradient'
|
import {LinearGradient} from 'expo-linear-gradient'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {atoms as a, useTheme, web} from '#/alf'
|
import {atoms as a, useTheme, web} from '#/alf'
|
||||||
@@ -28,7 +27,7 @@ export const enabled = createIsEnabledCheck(props => {
|
|||||||
|
|
||||||
export function DraftsAnnouncement() {
|
export function DraftsAnnouncement() {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const nuxDialogs = useNuxDialogContext()
|
const nuxDialogs = useNuxDialogContext()
|
||||||
const control = Dialog.useDialogControl()
|
const control = Dialog.useDialogControl()
|
||||||
|
|
||||||
@@ -44,9 +43,8 @@ export function DraftsAnnouncement() {
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
nativeOptions={{preventExpansion: true}}>
|
nativeOptions={{preventExpansion: true}}>
|
||||||
<Dialog.Handle fill={t.palette.primary_400} />
|
<Dialog.Handle fill={t.palette.primary_400} />
|
||||||
|
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
label={_(msg`Introducing drafts`)}
|
label={l`Introducing drafts`}
|
||||||
style={[web({maxWidth: 440})]}
|
style={[web({maxWidth: 440})]}
|
||||||
contentContainerStyle={[
|
contentContainerStyle={[
|
||||||
{
|
{
|
||||||
@@ -94,13 +92,11 @@ export function DraftsAnnouncement() {
|
|||||||
aspectRatio: 393 / 226,
|
aspectRatio: 393 / 226,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
alt={_(
|
alt={l({
|
||||||
msg({
|
message: `A screenshot of the post composer with a new button next to the post button that says "Drafts", with a rainbow firework effect. Below, the text in the composer reads "Hey, did you hear the news? Bluesky has drafts now!!!".`,
|
||||||
message: `A screenshot of the post composer with a new button next to the post button that says "Drafts", with a rainbow firework effect. Below, the text in the composer reads "Hey, did you hear the news? Bluesky has drafts now!!!".`,
|
comment:
|
||||||
comment:
|
'Contains a post that originally appeared in English. Consider translating the post text if it makes sense in your language, and noting that the post was translated from English.',
|
||||||
'Contains a post that originally appeared in English. Consider translating the post text if it makes sense in your language, and noting that the post was translated from English.',
|
})}
|
||||||
}),
|
|
||||||
)}
|
|
||||||
useAppleWebpCodec
|
useAppleWebpCodec
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@@ -137,7 +133,7 @@ export function DraftsAnnouncement() {
|
|||||||
|
|
||||||
{!IS_WEB && (
|
{!IS_WEB && (
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Close`)}
|
label={l`Close`}
|
||||||
size="large"
|
size="large"
|
||||||
color="primary"
|
color="primary"
|
||||||
onPress={() => control.close()}
|
onPress={() => control.close()}
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import {useCallback} from 'react'
|
|||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {Image} from 'expo-image'
|
import {Image} from 'expo-image'
|
||||||
import {LinearGradient} from 'expo-linear-gradient'
|
import {LinearGradient} from 'expo-linear-gradient'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {atoms as a, useTheme, web} from '#/alf'
|
import {atoms as a, useTheme, web} from '#/alf'
|
||||||
@@ -34,7 +33,7 @@ export const enabled = createIsEnabledCheck(props => {
|
|||||||
|
|
||||||
export function FindContactsAnnouncement() {
|
export function FindContactsAnnouncement() {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const nuxDialogs = useNuxDialogContext()
|
const nuxDialogs = useNuxDialogContext()
|
||||||
const control = Dialog.useDialogControl()
|
const control = Dialog.useDialogControl()
|
||||||
@@ -51,9 +50,8 @@ export function FindContactsAnnouncement() {
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
nativeOptions={{preventExpansion: true}}>
|
nativeOptions={{preventExpansion: true}}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
|
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
label={_(msg`Introducing finding friends via contacts`)}
|
label={l`Introducing finding friends via contacts`}
|
||||||
style={[web({maxWidth: 440})]}
|
style={[web({maxWidth: 440})]}
|
||||||
contentContainerStyle={[
|
contentContainerStyle={[
|
||||||
{
|
{
|
||||||
@@ -75,9 +73,7 @@ export function FindContactsAnnouncement() {
|
|||||||
accessibilityIgnoresInvertColors
|
accessibilityIgnoresInvertColors
|
||||||
source={require('../../../../assets/images/find_friends_illustration.webp')}
|
source={require('../../../../assets/images/find_friends_illustration.webp')}
|
||||||
style={[a.w_full, {aspectRatio: 1278 / 661}]}
|
style={[a.w_full, {aspectRatio: 1278 / 661}]}
|
||||||
alt={_(
|
alt={l`An illustration depicting user avatars flowing from a contact book into the Bluesky app`}
|
||||||
msg`An illustration depicting user avatars flowing from a contact book into the Bluesky app`,
|
|
||||||
)}
|
|
||||||
useAppleWebpCodec
|
useAppleWebpCodec
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@@ -113,7 +109,7 @@ export function FindContactsAnnouncement() {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Import Contacts`)}
|
label={l`Import Contacts`}
|
||||||
size="large"
|
size="large"
|
||||||
color="primary"
|
color="primary"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {useCallback} from 'react'
|
import {useCallback} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {Image} from 'expo-image'
|
import {Image} from 'expo-image'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {urls} from '#/lib/constants'
|
import {urls} from '#/lib/constants'
|
||||||
@@ -19,7 +18,7 @@ import {IS_NATIVE} from '#/env'
|
|||||||
|
|
||||||
export function InitialVerificationAnnouncement() {
|
export function InitialVerificationAnnouncement() {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
const nuxDialogs = useNuxDialogContext()
|
const nuxDialogs = useNuxDialogContext()
|
||||||
@@ -34,9 +33,8 @@ export function InitialVerificationAnnouncement() {
|
|||||||
return (
|
return (
|
||||||
<Dialog.Outer control={control} onClose={onClose}>
|
<Dialog.Outer control={control} onClose={onClose}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
|
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
label={_(msg`Announcing verification on Bluesky`)}
|
label={l`Announcing verification on Bluesky`}
|
||||||
style={[
|
style={[
|
||||||
gtMobile ? {width: 'auto', maxWidth: 400, minWidth: 200} : a.w_full,
|
gtMobile ? {width: 'auto', maxWidth: 400, minWidth: 200} : a.w_full,
|
||||||
]}>
|
]}>
|
||||||
@@ -82,9 +80,7 @@ export function InitialVerificationAnnouncement() {
|
|||||||
aspectRatio: 353 / 160,
|
aspectRatio: 353 / 160,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
alt={_(
|
alt={l`An illustration showing that Bluesky selects trusted verifiers, and trusted verifiers in turn verify individual user accounts.`}
|
||||||
msg`An illustration showing that Bluesky selects trusted verifiers, and trusted verifiers in turn verify individual user accounts.`,
|
|
||||||
)}
|
|
||||||
useAppleWebpCodec
|
useAppleWebpCodec
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@@ -117,9 +113,7 @@ export function InitialVerificationAnnouncement() {
|
|||||||
aspectRatio: 353 / 196,
|
aspectRatio: 353 / 196,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
alt={_(
|
alt={l`An mockup of a iPhone showing the Bluesky app open to the profile of a verified user with a blue checkmark next to their display name.`}
|
||||||
msg`An mockup of a iPhone showing the Bluesky app open to the profile of a verified user with a blue checkmark next to their display name.`,
|
|
||||||
)}
|
|
||||||
useAppleWebpCodec
|
useAppleWebpCodec
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@@ -159,7 +153,7 @@ export function InitialVerificationAnnouncement() {
|
|||||||
<Link
|
<Link
|
||||||
overridePresentation
|
overridePresentation
|
||||||
to={urls.website.blog.initialVerificationAnnouncement}
|
to={urls.website.blog.initialVerificationAnnouncement}
|
||||||
label={_(msg`Read blog post`)}
|
label={l`Read blog post`}
|
||||||
size="small"
|
size="small"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="primary"
|
color="primary"
|
||||||
@@ -175,7 +169,7 @@ export function InitialVerificationAnnouncement() {
|
|||||||
</Link>
|
</Link>
|
||||||
{IS_NATIVE && (
|
{IS_NATIVE && (
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Close`)}
|
label={l`Close`}
|
||||||
size="small"
|
size="small"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import {useCallback, useMemo} from 'react'
|
|||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {Image} from 'expo-image'
|
import {Image} from 'expo-image'
|
||||||
import {LinearGradient} from 'expo-linear-gradient'
|
import {LinearGradient} from 'expo-linear-gradient'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {atoms as a, select, useTheme, utils, web} from '#/alf'
|
import {atoms as a, select, useTheme, utils, web} from '#/alf'
|
||||||
@@ -31,7 +30,7 @@ export const enabled = createIsEnabledCheck(props => {
|
|||||||
|
|
||||||
export function LiveNowBetaDialog() {
|
export function LiveNowBetaDialog() {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const nuxDialogs = useNuxDialogContext()
|
const nuxDialogs = useNuxDialogContext()
|
||||||
const control = Dialog.useDialogControl()
|
const control = Dialog.useDialogControl()
|
||||||
|
|
||||||
@@ -55,9 +54,8 @@ export function LiveNowBetaDialog() {
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
nativeOptions={{preventExpansion: true}}>
|
nativeOptions={{preventExpansion: true}}>
|
||||||
<Dialog.Handle fill={t.palette.primary_700} />
|
<Dialog.Handle fill={t.palette.primary_700} />
|
||||||
|
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
label={_(msg`Show when you’re live`)}
|
label={l`Show when you’re live`}
|
||||||
style={[web({maxWidth: 440})]}
|
style={[web({maxWidth: 440})]}
|
||||||
contentContainerStyle={[
|
contentContainerStyle={[
|
||||||
{
|
{
|
||||||
@@ -143,13 +141,11 @@ export function LiveNowBetaDialog() {
|
|||||||
aspectRatio: 652 / 211,
|
aspectRatio: 652 / 211,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
alt={_(
|
alt={l({
|
||||||
msg({
|
message: `A screenshot of a post from @esb.lol, showing the user is currently livestreaming content on Twitch. The post reads: "Hello! I'm live on Twitch, and I'm testing Bluesky's latest feature too!"`,
|
||||||
message: `A screenshot of a post from @esb.lol, showing the user is currently livestreaming content on Twitch. The post reads: "Hello! I'm live on Twitch, and I'm testing Bluesky's latest feature too!"`,
|
comment:
|
||||||
comment:
|
'Contains a post that originally appeared in English. Consider translating the post text if it makes sense in your language, and noting that the post was translated from English.',
|
||||||
'Contains a post that originally appeared in English. Consider translating the post text if it makes sense in your language, and noting that the post was translated from English.',
|
})}
|
||||||
}),
|
|
||||||
)}
|
|
||||||
useAppleWebpCodec
|
useAppleWebpCodec
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@@ -189,7 +185,7 @@ export function LiveNowBetaDialog() {
|
|||||||
|
|
||||||
{!IS_WEB && (
|
{!IS_WEB && (
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Close`)}
|
label={l`Close`}
|
||||||
size="large"
|
size="large"
|
||||||
color="primary"
|
color="primary"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {Fragment} 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 {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {listUriToHref} from '#/lib/strings/url-helpers'
|
import {listUriToHref} from '#/lib/strings/url-helpers'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
@@ -19,23 +18,21 @@ export function BlockedByListDialog({
|
|||||||
control: DialogControlProps
|
control: DialogControlProps
|
||||||
listBlocks: ModerationCause[]
|
listBlocks: ModerationCause[]
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
|
||||||
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>{l`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
|
||||||
style={[a.text_md, a.leading_snug, t.atoms.text_contrast_high]}>
|
style={[a.text_md, a.leading_snug, t.atoms.text_contrast_high]}>
|
||||||
{_(
|
{l`This account is blocked by one or more of your moderation lists. To unblock, please visit the lists directly and remove this user.`}{' '}
|
||||||
msg`This account is blocked by one or more of your moderation lists. To unblock, please visit the lists directly and remove this user.`,
|
|
||||||
)}{' '}
|
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Text style={[a.text_md, a.leading_snug, t.atoms.text_contrast_high]}>
|
<Text style={[a.text_md, a.leading_snug, t.atoms.text_contrast_high]}>
|
||||||
{_(msg`Lists blocking this user:`)}{' '}
|
{l`Lists blocking this user:`}{' '}
|
||||||
{listBlocks.map((block, i) =>
|
{listBlocks.map((block, i) =>
|
||||||
block.source.type === 'list' ? (
|
block.source.type === 'list' ? (
|
||||||
<Fragment key={block.source.list.uri}>
|
<Fragment key={block.source.list.uri}>
|
||||||
@@ -52,7 +49,7 @@ export function BlockedByListDialog({
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<Prompt.Actions>
|
<Prompt.Actions>
|
||||||
<Prompt.Action cta={_(msg`I understand`)} onPress={() => {}} />
|
<Prompt.Action cta={l`I understand`} onPress={() => {}} />
|
||||||
</Prompt.Actions>
|
</Prompt.Actions>
|
||||||
<Dialog.Close />
|
<Dialog.Close />
|
||||||
</Prompt.Outer>
|
</Prompt.Outer>
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {useState} from 'react'
|
import {useState} from 'react'
|
||||||
import {Modal, Pressable, View} from 'react-native'
|
import {Modal, Pressable, View} from 'react-native'
|
||||||
import {SafeAreaView} from 'react-native-safe-area-context'
|
import {SafeAreaView} from 'react-native-safe-area-context'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
@@ -19,19 +18,18 @@ export function EmojiPopup({
|
|||||||
onEmojiSelected: (emoji: string) => void
|
onEmojiSelected: (emoji: string) => void
|
||||||
}) {
|
}) {
|
||||||
const [modalVisible, setModalVisible] = useState(false)
|
const [modalVisible, setModalVisible] = useState(false)
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Pressable
|
<Pressable
|
||||||
accessibilityLabel={_(msg`Open full emoji list`)}
|
accessibilityLabel={l`Open full emoji list`}
|
||||||
accessibilityHint=""
|
accessibilityHint=""
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
onPress={() => setModalVisible(true)}>
|
onPress={() => setModalVisible(true)}>
|
||||||
{children}
|
{children}
|
||||||
</Pressable>
|
</Pressable>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
animationType="slide"
|
animationType="slide"
|
||||||
visible={modalVisible}
|
visible={modalVisible}
|
||||||
@@ -56,7 +54,7 @@ export function EmojiPopup({
|
|||||||
<Trans>Add Reaction</Trans>
|
<Trans>Add Reaction</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Close`)}
|
label={l`Close`}
|
||||||
onPress={() => setModalVisible(false)}
|
onPress={() => setModalVisible(false)}
|
||||||
size="small"
|
size="small"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {useMemo, useState} from 'react'
|
import {useMemo, useState} from 'react'
|
||||||
import {useWindowDimensions, View} from 'react-native'
|
import {useWindowDimensions, View} from 'react-native'
|
||||||
import {type ChatBskyConvoDefs} from '@atproto/api'
|
import {type ChatBskyConvoDefs} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {atoms as a, tokens, useTheme} from '#/alf'
|
import {atoms as a, tokens, useTheme} from '#/alf'
|
||||||
@@ -25,7 +24,7 @@ export function EmojiReactionPicker({
|
|||||||
children?: TriggerProps['children']
|
children?: TriggerProps['children']
|
||||||
onEmojiSelect: (emoji: string) => void
|
onEmojiSelect: (emoji: string) => void
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const isFromSelf = message.sender?.did === currentAccount?.did
|
const isFromSelf = message.sender?.did === currentAccount?.did
|
||||||
@@ -77,7 +76,7 @@ export function EmojiReactionPicker({
|
|||||||
return (
|
return (
|
||||||
<ContextMenu.Item
|
<ContextMenu.Item
|
||||||
position={position}
|
position={position}
|
||||||
label={_(msg`React with ${emoji}`)}
|
label={l`React with ${emoji}`}
|
||||||
key={emoji}
|
key={emoji}
|
||||||
onPress={() => onEmojiSelect(emoji)}
|
onPress={() => onEmojiSelect(emoji)}
|
||||||
unstyled
|
unstyled
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {useCallback} 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 {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
@@ -22,7 +21,7 @@ export function MessageProfileButton({
|
|||||||
}: {
|
}: {
|
||||||
profile: AppBskyActorDefs.ProfileViewDetailed
|
profile: AppBskyActorDefs.ProfileViewDetailed
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const navigation = useNavigation<NavigationProp>()
|
const navigation = useNavigation<NavigationProp>()
|
||||||
@@ -35,7 +34,7 @@ export function MessageProfileButton({
|
|||||||
navigation.navigate('MessagesConversation', {conversation: convo.id})
|
navigation.navigate('MessagesConversation', {conversation: convo.id})
|
||||||
},
|
},
|
||||||
onError: () => {
|
onError: () => {
|
||||||
Toast.show(_(msg`Failed to create conversation`))
|
Toast.show(l`Failed to create conversation`)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -96,7 +95,7 @@ export function MessageProfileButton({
|
|||||||
color="secondary"
|
color="secondary"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
shape="round"
|
shape="round"
|
||||||
label={_(msg`Message ${profile.handle}`)}
|
label={l`Message ${profile.handle}`}
|
||||||
style={[a.justify_center]}
|
style={[a.justify_center]}
|
||||||
onPress={wrappedOnPress}>
|
onPress={wrappedOnPress}>
|
||||||
<ButtonIcon icon={Message} size="md" />
|
<ButtonIcon icon={Message} size="md" />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {useCallback, useImperativeHandle, useState} from 'react'
|
import {useCallback, useImperativeHandle, useState} from 'react'
|
||||||
import {Keyboard} from 'react-native'
|
import {Keyboard} from 'react-native'
|
||||||
import DatePicker from 'react-native-date-picker'
|
import DatePicker from 'react-native-date-picker'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {useTheme} from '#/alf'
|
import {useTheme} from '#/alf'
|
||||||
import {type DateFieldProps} from '#/components/forms/DateField/types'
|
import {type DateFieldProps} from '#/components/forms/DateField/types'
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {Pressable, View} from 'react-native'
|
import {Pressable, View} from 'react-native'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {atoms as a, native, useTheme, web} from '#/alf'
|
import {atoms as a, native, useTheme, web} from '#/alf'
|
||||||
import * as TextField from '#/components/forms/TextField'
|
import * as TextField from '#/components/forms/TextField'
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {useCallback, useImperativeHandle, useState} from 'react'
|
import {useCallback, useImperativeHandle, useState} from 'react'
|
||||||
import {Keyboard, View} from 'react-native'
|
import {Keyboard, View} from 'react-native'
|
||||||
import DatePicker from 'react-native-date-picker'
|
import DatePicker from 'react-native-date-picker'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
@@ -37,7 +36,7 @@ export function DateField({
|
|||||||
maximumDate,
|
maximumDate,
|
||||||
minimumDate,
|
minimumDate,
|
||||||
}: DateFieldProps) {
|
}: DateFieldProps) {
|
||||||
const {_, i18n} = useLingui()
|
const {t: l, i18n} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const control = Dialog.useDialogControl()
|
const control = Dialog.useDialogControl()
|
||||||
|
|
||||||
@@ -125,7 +124,7 @@ export function DateField({
|
|||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Done`)}
|
label={l`Done`}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
/*
|
/*
|
||||||
* Commit the currently shown date even if the user never
|
* Commit the currently shown date even if the user never
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {useCallback} 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 {useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {toNiceDomain} from '#/lib/strings/url-helpers'
|
import {toNiceDomain} from '#/lib/strings/url-helpers'
|
||||||
@@ -26,7 +25,7 @@ export function HostingProvider({
|
|||||||
}) {
|
}) {
|
||||||
const serverInputControl = useDialogControl()
|
const serverInputControl = useDialogControl()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
const onPressSelectService = useCallback(() => {
|
const onPressSelectService = useCallback(() => {
|
||||||
Keyboard.dismiss()
|
Keyboard.dismiss()
|
||||||
@@ -47,7 +46,7 @@ export function HostingProvider({
|
|||||||
</Text>
|
</Text>
|
||||||
<Button
|
<Button
|
||||||
label={toNiceDomain(serviceUrl)}
|
label={toNiceDomain(serviceUrl)}
|
||||||
accessibilityHint={_(msg`Changes hosting provider`)}
|
accessibilityHint={l`Changes hosting provider`}
|
||||||
onPress={onPressSelectService}
|
onPress={onPressSelectService}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
@@ -67,7 +66,7 @@ export function HostingProvider({
|
|||||||
<Button
|
<Button
|
||||||
testID="selectServiceButton"
|
testID="selectServiceButton"
|
||||||
label={toNiceDomain(serviceUrl)}
|
label={toNiceDomain(serviceUrl)}
|
||||||
accessibilityHint={_(msg`Changes hosting provider`)}
|
accessibilityHint={l`Changes hosting provider`}
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
style={[
|
style={[
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user