parent
aeeacd10d3
commit
008893b911
|
@ -39,6 +39,8 @@ import {
|
|||
import {Provider as UnreadNotifsProvider} from 'state/queries/notifications/unread'
|
||||
import * as persisted from '#/state/persisted'
|
||||
import {Splash} from '#/Splash'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
SplashScreen.preventAutoHideAsync()
|
||||
|
||||
|
@ -46,17 +48,18 @@ function InnerApp() {
|
|||
const colorMode = useColorMode()
|
||||
const {isInitialLoad, currentAccount} = useSession()
|
||||
const {resumeSession} = useSessionApi()
|
||||
const {_} = useLingui()
|
||||
|
||||
// init
|
||||
useEffect(() => {
|
||||
notifications.init(queryClient)
|
||||
listenSessionDropped(() => {
|
||||
Toast.show('Sorry! Your session expired. Please log in again.')
|
||||
Toast.show(_(msg`Sorry! Your session expired. Please log in again.`))
|
||||
})
|
||||
|
||||
const account = persisted.get('session').currentAccount
|
||||
resumeSession(account)
|
||||
}, [resumeSession])
|
||||
}, [resumeSession, _])
|
||||
|
||||
return (
|
||||
<SafeAreaProvider initialMetrics={initialWindowMetrics}>
|
||||
|
|
|
@ -76,6 +76,8 @@ import {PreferencesHomeFeed} from 'view/screens/PreferencesHomeFeed'
|
|||
import {PreferencesThreads} from 'view/screens/PreferencesThreads'
|
||||
import {PreferencesExternalEmbeds} from '#/view/screens/PreferencesExternalEmbeds'
|
||||
import {createNativeStackNavigatorWithAuth} from './view/shell/createNativeStackNavigatorWithAuth'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {i18n, MessageDescriptor} from '@lingui/core'
|
||||
|
||||
const navigationRef = createNavigationContainerRef<AllNavigatorParams>()
|
||||
|
||||
|
@ -93,55 +95,56 @@ const Tab = createBottomTabNavigator<BottomTabNavigatorParams>()
|
|||
* These "common screens" are reused across stacks.
|
||||
*/
|
||||
function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) {
|
||||
const title = (page: string) => bskyTitle(page, unreadCountLabel)
|
||||
const title = (page: MessageDescriptor) =>
|
||||
bskyTitle(i18n._(page), unreadCountLabel)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
name="NotFound"
|
||||
getComponent={() => NotFoundScreen}
|
||||
options={{title: title('Not Found')}}
|
||||
options={{title: title(msg`Not Found`)}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Lists"
|
||||
component={ListsScreen}
|
||||
options={{title: title('Lists'), requireAuth: true}}
|
||||
options={{title: title(msg`Lists`), requireAuth: true}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Moderation"
|
||||
getComponent={() => ModerationScreen}
|
||||
options={{title: title('Moderation'), requireAuth: true}}
|
||||
options={{title: title(msg`Moderation`), requireAuth: true}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="ModerationModlists"
|
||||
getComponent={() => ModerationModlistsScreen}
|
||||
options={{title: title('Moderation Lists'), requireAuth: true}}
|
||||
options={{title: title(msg`Moderation Lists`), requireAuth: true}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="ModerationMutedAccounts"
|
||||
getComponent={() => ModerationMutedAccounts}
|
||||
options={{title: title('Muted Accounts'), requireAuth: true}}
|
||||
options={{title: title(msg`Muted Accounts`), requireAuth: true}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="ModerationBlockedAccounts"
|
||||
getComponent={() => ModerationBlockedAccounts}
|
||||
options={{title: title('Blocked Accounts'), requireAuth: true}}
|
||||
options={{title: title(msg`Blocked Accounts`), requireAuth: true}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Settings"
|
||||
getComponent={() => SettingsScreen}
|
||||
options={{title: title('Settings'), requireAuth: true}}
|
||||
options={{title: title(msg`Settings`), requireAuth: true}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="LanguageSettings"
|
||||
getComponent={() => LanguageSettingsScreen}
|
||||
options={{title: title('Language Settings'), requireAuth: true}}
|
||||
options={{title: title(msg`Language Settings`), requireAuth: true}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Profile"
|
||||
getComponent={() => ProfileScreen}
|
||||
options={({route}) => ({
|
||||
title: title(`@${route.params.name}`),
|
||||
title: title(msg`@${route.params.name}`),
|
||||
animation: 'none',
|
||||
})}
|
||||
/>
|
||||
|
@ -149,106 +152,112 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) {
|
|||
name="ProfileFollowers"
|
||||
getComponent={() => ProfileFollowersScreen}
|
||||
options={({route}) => ({
|
||||
title: title(`People following @${route.params.name}`),
|
||||
title: title(msg`People following @${route.params.name}`),
|
||||
})}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="ProfileFollows"
|
||||
getComponent={() => ProfileFollowsScreen}
|
||||
options={({route}) => ({
|
||||
title: title(`People followed by @${route.params.name}`),
|
||||
title: title(msg`People followed by @${route.params.name}`),
|
||||
})}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="ProfileList"
|
||||
getComponent={() => ProfileListScreen}
|
||||
options={{title: title('List'), requireAuth: true}}
|
||||
options={{title: title(msg`List`), requireAuth: true}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="PostThread"
|
||||
getComponent={() => PostThreadScreen}
|
||||
options={({route}) => ({title: title(`Post by @${route.params.name}`)})}
|
||||
options={({route}) => ({
|
||||
title: title(msg`Post by @${route.params.name}`),
|
||||
})}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="PostLikedBy"
|
||||
getComponent={() => PostLikedByScreen}
|
||||
options={({route}) => ({title: title(`Post by @${route.params.name}`)})}
|
||||
options={({route}) => ({
|
||||
title: title(msg`Post by @${route.params.name}`),
|
||||
})}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="PostRepostedBy"
|
||||
getComponent={() => PostRepostedByScreen}
|
||||
options={({route}) => ({title: title(`Post by @${route.params.name}`)})}
|
||||
options={({route}) => ({
|
||||
title: title(msg`Post by @${route.params.name}`),
|
||||
})}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="ProfileFeed"
|
||||
getComponent={() => ProfileFeedScreen}
|
||||
options={{title: title('Feed'), requireAuth: true}}
|
||||
options={{title: title(msg`Feed`), requireAuth: true}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="ProfileFeedLikedBy"
|
||||
getComponent={() => ProfileFeedLikedByScreen}
|
||||
options={{title: title('Liked by')}}
|
||||
options={{title: title(msg`Liked by`)}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Debug"
|
||||
getComponent={() => DebugScreen}
|
||||
options={{title: title('Debug'), requireAuth: true}}
|
||||
options={{title: title(msg`Debug`), requireAuth: true}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Log"
|
||||
getComponent={() => LogScreen}
|
||||
options={{title: title('Log'), requireAuth: true}}
|
||||
options={{title: title(msg`Log`), requireAuth: true}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Support"
|
||||
getComponent={() => SupportScreen}
|
||||
options={{title: title('Support')}}
|
||||
options={{title: title(msg`Support`)}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="PrivacyPolicy"
|
||||
getComponent={() => PrivacyPolicyScreen}
|
||||
options={{title: title('Privacy Policy')}}
|
||||
options={{title: title(msg`Privacy Policy`)}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="TermsOfService"
|
||||
getComponent={() => TermsOfServiceScreen}
|
||||
options={{title: title('Terms of Service')}}
|
||||
options={{title: title(msg`Terms of Service`)}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="CommunityGuidelines"
|
||||
getComponent={() => CommunityGuidelinesScreen}
|
||||
options={{title: title('Community Guidelines')}}
|
||||
options={{title: title(msg`Community Guidelines`)}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="CopyrightPolicy"
|
||||
getComponent={() => CopyrightPolicyScreen}
|
||||
options={{title: title('Copyright Policy')}}
|
||||
options={{title: title(msg`Copyright Policy`)}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="AppPasswords"
|
||||
getComponent={() => AppPasswords}
|
||||
options={{title: title('App Passwords'), requireAuth: true}}
|
||||
options={{title: title(msg`App Passwords`), requireAuth: true}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="SavedFeeds"
|
||||
getComponent={() => SavedFeeds}
|
||||
options={{title: title('Edit My Feeds'), requireAuth: true}}
|
||||
options={{title: title(msg`Edit My Feeds`), requireAuth: true}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="PreferencesHomeFeed"
|
||||
getComponent={() => PreferencesHomeFeed}
|
||||
options={{title: title('Home Feed Preferences'), requireAuth: true}}
|
||||
options={{title: title(msg`Home Feed Preferences`), requireAuth: true}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="PreferencesThreads"
|
||||
getComponent={() => PreferencesThreads}
|
||||
options={{title: title('Threads Preferences'), requireAuth: true}}
|
||||
options={{title: title(msg`Threads Preferences`), requireAuth: true}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="PreferencesExternalEmbeds"
|
||||
getComponent={() => PreferencesExternalEmbeds}
|
||||
options={{
|
||||
title: title('External Media Preferences'),
|
||||
title: title(msg`External Media Preferences`),
|
||||
requireAuth: true,
|
||||
}}
|
||||
/>
|
||||
|
@ -407,7 +416,7 @@ const FlatNavigator = () => {
|
|||
const pal = usePalette('default')
|
||||
const numUnread = useUnreadNotifications()
|
||||
|
||||
const title = (page: string) => bskyTitle(page, numUnread)
|
||||
const title = (page: MessageDescriptor) => bskyTitle(i18n._(page), numUnread)
|
||||
return (
|
||||
<Flat.Navigator
|
||||
screenOptions={{
|
||||
|
@ -420,22 +429,22 @@ const FlatNavigator = () => {
|
|||
<Flat.Screen
|
||||
name="Home"
|
||||
getComponent={() => HomeScreen}
|
||||
options={{title: title('Home'), requireAuth: true}}
|
||||
options={{title: title(msg`Home`), requireAuth: true}}
|
||||
/>
|
||||
<Flat.Screen
|
||||
name="Search"
|
||||
getComponent={() => SearchScreen}
|
||||
options={{title: title('Search')}}
|
||||
options={{title: title(msg`Search`)}}
|
||||
/>
|
||||
<Flat.Screen
|
||||
name="Feeds"
|
||||
getComponent={() => FeedsScreen}
|
||||
options={{title: title('Feeds'), requireAuth: true}}
|
||||
options={{title: title(msg`Feeds`), requireAuth: true}}
|
||||
/>
|
||||
<Flat.Screen
|
||||
name="Notifications"
|
||||
getComponent={() => NotificationsScreen}
|
||||
options={{title: title('Notifications'), requireAuth: true}}
|
||||
options={{title: title(msg`Notifications`), requireAuth: true}}
|
||||
/>
|
||||
{commonScreens(Flat as typeof HomeTab, numUnread)}
|
||||
</Flat.Navigator>
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react'
|
|||
import {View, Pressable} from 'react-native'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
|
||||
import {isIOS, isNative} from 'platform/detection'
|
||||
|
@ -119,7 +119,7 @@ export function LoggedOut({onDismiss}: {onDismiss?: () => void}) {
|
|||
}}
|
||||
onPress={onPressSearch}>
|
||||
<Text type="lg-bold" style={[pal.text]}>
|
||||
Search{' '}
|
||||
<Trans>Search</Trans>{' '}
|
||||
</Text>
|
||||
<FontAwesomeIcon
|
||||
icon="search"
|
||||
|
|
|
@ -74,7 +74,7 @@ export const SplashScreen = ({
|
|||
// TODO: web accessibility
|
||||
accessibilityRole="button">
|
||||
<Text style={[s.white, styles.btnLabel]}>
|
||||
Create a new account
|
||||
<Trans>Create a new account</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
|
|
|
@ -77,7 +77,7 @@ export function Step1({
|
|||
value={uiState.serviceUrl}
|
||||
editable
|
||||
onChange={onChangeServiceUrl}
|
||||
accessibilityHint="Input hosting provider address"
|
||||
accessibilityHint={_(msg`Input hosting provider address`)}
|
||||
accessibilityLabel={_(msg`Hosting provider address`)}
|
||||
accessibilityLabelledBy="addressProvider"
|
||||
/>
|
||||
|
@ -125,6 +125,7 @@ function Option({
|
|||
}>) {
|
||||
const theme = useTheme()
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const circleFillStyle = React.useMemo(
|
||||
() => ({
|
||||
backgroundColor: theme.palette.primary.background,
|
||||
|
@ -139,7 +140,7 @@ function Option({
|
|||
testID={testID}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={label}
|
||||
accessibilityHint={`Sets hosting provider to ${label}`}>
|
||||
accessibilityHint={_(msg`Sets hosting provider to ${label}`)}>
|
||||
<View style={styles.optionHeading}>
|
||||
<View style={[styles.circle, pal.border]}>
|
||||
{isSelected ? (
|
||||
|
|
|
@ -60,7 +60,7 @@ export function Step2({
|
|||
{uiState.isInviteCodeRequired && (
|
||||
<View style={s.pb20}>
|
||||
<Text type="md-medium" style={[pal.text, s.mb2]}>
|
||||
Invite code
|
||||
<Trans>Invite code</Trans>
|
||||
</Text>
|
||||
<TextInput
|
||||
testID="inviteCodeInput"
|
||||
|
@ -70,7 +70,7 @@ export function Step2({
|
|||
editable
|
||||
onChange={value => uiDispatch({type: 'set-invite-code', value})}
|
||||
accessibilityLabel={_(msg`Invite code`)}
|
||||
accessibilityHint="Input invite code to proceed"
|
||||
accessibilityHint={_(msg`Input invite code to proceed`)}
|
||||
autoCapitalize="none"
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
|
@ -80,7 +80,7 @@ export function Step2({
|
|||
|
||||
{!uiState.inviteCode && uiState.isInviteCodeRequired ? (
|
||||
<Text style={[s.alignBaseline, pal.text]}>
|
||||
Don't have an invite code?{' '}
|
||||
<Trans>Don't have an invite code?</Trans>{' '}
|
||||
<TouchableWithoutFeedback
|
||||
onPress={onPressWaitlist}
|
||||
accessibilityLabel={_(msg`Join the waitlist.`)}
|
||||
|
@ -106,7 +106,7 @@ export function Step2({
|
|||
editable
|
||||
onChange={value => uiDispatch({type: 'set-email', value})}
|
||||
accessibilityLabel={_(msg`Email`)}
|
||||
accessibilityHint="Input email for Bluesky waitlist"
|
||||
accessibilityHint={_(msg`Input email for Bluesky waitlist`)}
|
||||
accessibilityLabelledBy="email"
|
||||
autoCapitalize="none"
|
||||
autoComplete="off"
|
||||
|
@ -130,7 +130,7 @@ export function Step2({
|
|||
secureTextEntry
|
||||
onChange={value => uiDispatch({type: 'set-password', value})}
|
||||
accessibilityLabel={_(msg`Password`)}
|
||||
accessibilityHint="Set password"
|
||||
accessibilityHint={_(msg`Set password`)}
|
||||
accessibilityLabelledBy="password"
|
||||
autoCapitalize="none"
|
||||
autoComplete="off"
|
||||
|
@ -154,7 +154,7 @@ export function Step2({
|
|||
buttonStyle={[pal.border, styles.dateInputButton]}
|
||||
buttonLabelType="lg"
|
||||
accessibilityLabel={_(msg`Birthday`)}
|
||||
accessibilityHint="Enter your birth date"
|
||||
accessibilityHint={_(msg`Enter your birth date`)}
|
||||
accessibilityLabelledBy="birthDate"
|
||||
/>
|
||||
</View>
|
||||
|
|
|
@ -36,7 +36,7 @@ export function Step3({
|
|||
onChange={value => uiDispatch({type: 'set-handle', value})}
|
||||
// TODO: Add explicit text label
|
||||
accessibilityLabel={_(msg`User handle`)}
|
||||
accessibilityHint="Input your user handle"
|
||||
accessibilityHint={_(msg`Input your user handle`)}
|
||||
/>
|
||||
<Text type="lg" style={[pal.text, s.pl5, s.pt10]}>
|
||||
<Trans>Your full handle will be</Trans>{' '}
|
||||
|
|
|
@ -2,13 +2,18 @@ import React from 'react'
|
|||
import {StyleSheet, View} from 'react-native'
|
||||
import {Text} from 'view/com/util/text/Text'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {Trans} from '@lingui/macro'
|
||||
|
||||
export function StepHeader({step, title}: {step: string; title: string}) {
|
||||
const pal = usePalette('default')
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text type="lg" style={[pal.textLight]}>
|
||||
{step === '3' ? 'Last step!' : <>Step {step} of 3</>}
|
||||
{step === '3' ? (
|
||||
<Trans>Last step!</Trans>
|
||||
) : (
|
||||
<Trans>Step {step} of 3</Trans>
|
||||
)}
|
||||
</Text>
|
||||
<Text style={[pal.text]} type="title-xl">
|
||||
{title}
|
||||
|
|
|
@ -42,7 +42,7 @@ function AccountItem({
|
|||
onPress={onPress}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Sign in as ${account.handle}`)}
|
||||
accessibilityHint="Double tap to sign in">
|
||||
accessibilityHint={_(msg`Double tap to sign in`)}>
|
||||
<View style={[pal.borderDark, styles.groupContent, styles.noTopBorder]}>
|
||||
<View style={s.p10}>
|
||||
<UserAvatar avatar={profile?.avatar} size={30} />
|
||||
|
@ -95,19 +95,19 @@ export const ChooseAccountForm = ({
|
|||
if (account.accessJwt) {
|
||||
if (account.did === currentAccount?.did) {
|
||||
setShowLoggedOut(false)
|
||||
Toast.show(`Already signed in as @${account.handle}`)
|
||||
Toast.show(_(msg`Already signed in as @${account.handle}`))
|
||||
} else {
|
||||
await initSession(account)
|
||||
track('Sign In', {resumedSession: true})
|
||||
setTimeout(() => {
|
||||
Toast.show(`Signed in as @${account.handle}`)
|
||||
Toast.show(_(msg`Signed in as @${account.handle}`))
|
||||
}, 100)
|
||||
}
|
||||
} else {
|
||||
onSelectAccount(account)
|
||||
}
|
||||
},
|
||||
[currentAccount, track, initSession, onSelectAccount, setShowLoggedOut],
|
||||
[currentAccount, track, initSession, onSelectAccount, setShowLoggedOut, _],
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
|
@ -67,7 +67,7 @@ export const ForgotPasswordForm = ({
|
|||
|
||||
const onPressNext = async () => {
|
||||
if (!EmailValidator.validate(email)) {
|
||||
return setError('Your email appears to be invalid.')
|
||||
return setError(_(msg`Your email appears to be invalid.`))
|
||||
}
|
||||
|
||||
setError('')
|
||||
|
@ -83,7 +83,9 @@ export const ForgotPasswordForm = ({
|
|||
setIsProcessing(false)
|
||||
if (isNetworkError(e)) {
|
||||
setError(
|
||||
'Unable to contact your service. Please check your Internet connection.',
|
||||
_(
|
||||
msg`Unable to contact your service. Please check your Internet connection.`,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
setError(cleanError(errMsg))
|
||||
|
@ -112,7 +114,9 @@ export const ForgotPasswordForm = ({
|
|||
onPress={onPressSelectService}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Hosting provider`)}
|
||||
accessibilityHint="Sets hosting provider for password reset">
|
||||
accessibilityHint={_(
|
||||
msg`Sets hosting provider for password reset`,
|
||||
)}>
|
||||
<FontAwesomeIcon
|
||||
icon="globe"
|
||||
style={[pal.textLight, styles.groupContentIcon]}
|
||||
|
@ -136,7 +140,7 @@ export const ForgotPasswordForm = ({
|
|||
<TextInput
|
||||
testID="forgotPasswordEmail"
|
||||
style={[pal.text, styles.textInput]}
|
||||
placeholder="Email address"
|
||||
placeholder={_(msg`Email address`)}
|
||||
placeholderTextColor={pal.colors.textLight}
|
||||
autoCapitalize="none"
|
||||
autoFocus
|
||||
|
@ -146,7 +150,7 @@ export const ForgotPasswordForm = ({
|
|||
onChangeText={setEmail}
|
||||
editable={!isProcessing}
|
||||
accessibilityLabel={_(msg`Email`)}
|
||||
accessibilityHint="Sets email for password reset"
|
||||
accessibilityHint={_(msg`Sets email for password reset`)}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
@ -179,7 +183,7 @@ export const ForgotPasswordForm = ({
|
|||
onPress={onPressNext}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Go to next`)}
|
||||
accessibilityHint="Navigates to the next screen">
|
||||
accessibilityHint={_(msg`Navigates to the next screen`)}>
|
||||
<Text type="xl-bold" style={[pal.link, s.pr5]}>
|
||||
<Trans>Next</Trans>
|
||||
</Text>
|
||||
|
|
|
@ -145,7 +145,7 @@ export const LoginForm = ({
|
|||
onPress={onPressSelectService}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Select service`)}
|
||||
accessibilityHint="Sets server for the Bluesky client">
|
||||
accessibilityHint={_(msg`Sets server for the Bluesky client`)}>
|
||||
<Text type="xl" style={[pal.text, styles.textBtnLabel]}>
|
||||
{toNiceDomain(serviceUrl)}
|
||||
</Text>
|
||||
|
@ -190,7 +190,9 @@ export const LoginForm = ({
|
|||
}
|
||||
editable={!isProcessing}
|
||||
accessibilityLabel={_(msg`Username or email address`)}
|
||||
accessibilityHint="Input the username or email address you used at signup"
|
||||
accessibilityHint={_(
|
||||
msg`Input the username or email address you used at signup`,
|
||||
)}
|
||||
/>
|
||||
</View>
|
||||
<View style={[pal.borderDark, styles.groupContent]}>
|
||||
|
@ -221,8 +223,8 @@ export const LoginForm = ({
|
|||
accessibilityLabel={_(msg`Password`)}
|
||||
accessibilityHint={
|
||||
identifier === ''
|
||||
? 'Input your password'
|
||||
: `Input the password tied to ${identifier}`
|
||||
? _(msg`Input your password`)
|
||||
: _(msg`Input the password tied to ${identifier}`)
|
||||
}
|
||||
/>
|
||||
<TouchableOpacity
|
||||
|
@ -231,7 +233,7 @@ export const LoginForm = ({
|
|||
onPress={onPressForgotPassword}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Forgot password`)}
|
||||
accessibilityHint="Opens password reset form">
|
||||
accessibilityHint={_(msg`Opens password reset form`)}>
|
||||
<Text style={pal.link}>
|
||||
<Trans>Forgot</Trans>
|
||||
</Text>
|
||||
|
@ -261,7 +263,7 @@ export const LoginForm = ({
|
|||
onPress={onPressRetryConnect}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Retry`)}
|
||||
accessibilityHint="Retries login">
|
||||
accessibilityHint={_(msg`Retries login`)}>
|
||||
<Text type="xl-bold" style={[pal.link, s.pr5]}>
|
||||
<Trans>Retry</Trans>
|
||||
</Text>
|
||||
|
@ -281,7 +283,7 @@ export const LoginForm = ({
|
|||
onPress={onPressNext}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Go to next`)}
|
||||
accessibilityHint="Navigates to the next screen">
|
||||
accessibilityHint={_(msg`Navigates to the next screen`)}>
|
||||
<Text type="xl-bold" style={[pal.link, s.pr5]}>
|
||||
<Trans>Next</Trans>
|
||||
</Text>
|
||||
|
|
|
@ -36,7 +36,7 @@ export const PasswordUpdatedForm = ({
|
|||
onPress={onPressNext}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Close alert`)}
|
||||
accessibilityHint="Closes password update alert">
|
||||
accessibilityHint={_(msg`Closes password update alert`)}>
|
||||
<Text type="xl-bold" style={[pal.link, s.pr5]}>
|
||||
<Trans>Okay</Trans>
|
||||
</Text>
|
||||
|
|
|
@ -95,7 +95,7 @@ export const SetNewPasswordForm = ({
|
|||
<TextInput
|
||||
testID="resetCodeInput"
|
||||
style={[pal.text, styles.textInput]}
|
||||
placeholder="Reset code"
|
||||
placeholder={_(msg`Reset code`)}
|
||||
placeholderTextColor={pal.colors.textLight}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
|
@ -106,7 +106,9 @@ export const SetNewPasswordForm = ({
|
|||
editable={!isProcessing}
|
||||
accessible={true}
|
||||
accessibilityLabel={_(msg`Reset code`)}
|
||||
accessibilityHint="Input code sent to your email for password reset"
|
||||
accessibilityHint={_(
|
||||
msg`Input code sent to your email for password reset`,
|
||||
)}
|
||||
/>
|
||||
</View>
|
||||
<View style={[pal.borderDark, styles.groupContent]}>
|
||||
|
@ -117,7 +119,7 @@ export const SetNewPasswordForm = ({
|
|||
<TextInput
|
||||
testID="newPasswordInput"
|
||||
style={[pal.text, styles.textInput]}
|
||||
placeholder="New password"
|
||||
placeholder={_(msg`New password`)}
|
||||
placeholderTextColor={pal.colors.textLight}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
|
@ -128,7 +130,7 @@ export const SetNewPasswordForm = ({
|
|||
editable={!isProcessing}
|
||||
accessible={true}
|
||||
accessibilityLabel={_(msg`Password`)}
|
||||
accessibilityHint="Input new password"
|
||||
accessibilityHint={_(msg`Input new password`)}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
@ -161,7 +163,7 @@ export const SetNewPasswordForm = ({
|
|||
onPress={onPressNext}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Go to next`)}
|
||||
accessibilityHint="Navigates to the next screen">
|
||||
accessibilityHint={_(msg`Navigates to the next screen`)}>
|
||||
<Text type="xl-bold" style={[pal.link, s.pr5]}>
|
||||
<Trans>Next</Trans>
|
||||
</Text>
|
||||
|
|
|
@ -18,6 +18,8 @@ import {
|
|||
} from '#/state/queries/preferences'
|
||||
import {logger} from '#/logger'
|
||||
import {useAnalytics} from '#/lib/analytics/analytics'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
export function RecommendedFeedsItem({
|
||||
item,
|
||||
|
@ -26,6 +28,7 @@ export function RecommendedFeedsItem({
|
|||
}) {
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {data: preferences} = usePreferencesQuery()
|
||||
const {
|
||||
mutateAsync: pinFeed,
|
||||
|
@ -51,7 +54,7 @@ export function RecommendedFeedsItem({
|
|||
await removeFeed({uri: item.uri})
|
||||
resetRemoveFeed()
|
||||
} catch (e) {
|
||||
Toast.show('There was an issue contacting your server')
|
||||
Toast.show(_(msg`There was an issue contacting your server`))
|
||||
logger.error('Failed to unsave feed', {error: e})
|
||||
}
|
||||
} else {
|
||||
|
@ -60,7 +63,7 @@ export function RecommendedFeedsItem({
|
|||
resetPinFeed()
|
||||
track('Onboarding:CustomFeedAdded')
|
||||
} catch (e) {
|
||||
Toast.show('There was an issue contacting your server')
|
||||
Toast.show(_(msg`There was an issue contacting your server`))
|
||||
logger.error('Failed to pin feed', {error: e})
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +97,7 @@ export function RecommendedFeedsItem({
|
|||
</Text>
|
||||
|
||||
<Text style={[pal.textLight, {marginBottom: 8}]} numberOfLines={1}>
|
||||
by {sanitizeHandle(item.creator.handle, '@')}
|
||||
<Trans>by {sanitizeHandle(item.creator.handle, '@')}</Trans>
|
||||
</Text>
|
||||
|
||||
{item.description ? (
|
||||
|
@ -133,7 +136,7 @@ export function RecommendedFeedsItem({
|
|||
color={pal.colors.textInverted}
|
||||
/>
|
||||
<Text type="lg-medium" style={pal.textInverted}>
|
||||
Added
|
||||
<Trans>Added</Trans>
|
||||
</Text>
|
||||
</>
|
||||
) : (
|
||||
|
@ -144,7 +147,7 @@ export function RecommendedFeedsItem({
|
|||
color={pal.colors.textInverted}
|
||||
/>
|
||||
<Text type="lg-medium" style={pal.textInverted}>
|
||||
Add
|
||||
<Trans>Add</Trans>
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -83,7 +83,7 @@ export function RecommendedFollows({next}: Props) {
|
|||
<Text
|
||||
type="2xl-medium"
|
||||
style={{color: '#fff', position: 'relative', top: -1}}>
|
||||
<Trans>Done</Trans>
|
||||
<Trans context="action">Done</Trans>
|
||||
</Text>
|
||||
<FontAwesomeIcon icon="angle-right" color="#fff" size={14} />
|
||||
</View>
|
||||
|
|
|
@ -7,6 +7,7 @@ import {usePalette} from 'lib/hooks/usePalette'
|
|||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {TitleColumnLayout} from 'view/com/util/layouts/TitleColumnLayout'
|
||||
import {Button} from 'view/com/util/forms/Button'
|
||||
import {Trans} from '@lingui/macro'
|
||||
|
||||
type Props = {
|
||||
next: () => void
|
||||
|
@ -17,7 +18,7 @@ export function WelcomeDesktop({next}: Props) {
|
|||
const pal = usePalette('default')
|
||||
const horizontal = useMediaQuery({minWidth: 1300})
|
||||
const title = (
|
||||
<>
|
||||
<Trans>
|
||||
<Text
|
||||
style={[
|
||||
pal.textLight,
|
||||
|
@ -40,7 +41,7 @@ export function WelcomeDesktop({next}: Props) {
|
|||
]}>
|
||||
Bluesky
|
||||
</Text>
|
||||
</>
|
||||
</Trans>
|
||||
)
|
||||
return (
|
||||
<TitleColumnLayout
|
||||
|
@ -52,10 +53,12 @@ export function WelcomeDesktop({next}: Props) {
|
|||
<FontAwesomeIcon icon={'globe'} size={36} color={pal.colors.link} />
|
||||
<View style={[styles.rowText]}>
|
||||
<Text type="xl-bold" style={[pal.text]}>
|
||||
Bluesky is public.
|
||||
<Trans>Bluesky is public.</Trans>
|
||||
</Text>
|
||||
<Text type="xl" style={[pal.text, s.pt2]}>
|
||||
Your posts, likes, and blocks are public. Mutes are private.
|
||||
<Trans>
|
||||
Your posts, likes, and blocks are public. Mutes are private.
|
||||
</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
@ -63,10 +66,10 @@ export function WelcomeDesktop({next}: Props) {
|
|||
<FontAwesomeIcon icon={'at'} size={36} color={pal.colors.link} />
|
||||
<View style={[styles.rowText]}>
|
||||
<Text type="xl-bold" style={[pal.text]}>
|
||||
Bluesky is open.
|
||||
<Trans>Bluesky is open.</Trans>
|
||||
</Text>
|
||||
<Text type="xl" style={[pal.text, s.pt2]}>
|
||||
Never lose access to your followers and data.
|
||||
<Trans>Never lose access to your followers and data.</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
@ -74,10 +77,13 @@ export function WelcomeDesktop({next}: Props) {
|
|||
<FontAwesomeIcon icon={'gear'} size={36} color={pal.colors.link} />
|
||||
<View style={[styles.rowText]}>
|
||||
<Text type="xl-bold" style={[pal.text]}>
|
||||
Bluesky is flexible.
|
||||
<Trans>Bluesky is flexible.</Trans>
|
||||
</Text>
|
||||
<Text type="xl" style={[pal.text, s.pt2]}>
|
||||
Choose the algorithms that power your experience with custom feeds.
|
||||
<Trans>
|
||||
Choose the algorithms that power your experience with custom
|
||||
feeds.
|
||||
</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
@ -94,7 +100,7 @@ export function WelcomeDesktop({next}: Props) {
|
|||
<Text
|
||||
type="2xl-medium"
|
||||
style={{color: '#fff', position: 'relative', top: -1}}>
|
||||
Next
|
||||
<Trans context="action">Next</Trans>
|
||||
</Text>
|
||||
<FontAwesomeIcon icon="angle-right" color="#fff" size={14} />
|
||||
</View>
|
||||
|
|
|
@ -260,7 +260,11 @@ export const ComposePost = observer(function ComposePost({
|
|||
setLangPrefs.savePostLanguageToHistory()
|
||||
onPost?.()
|
||||
onClose()
|
||||
Toast.show(`Your ${replyTo ? 'reply' : 'post'} has been published`)
|
||||
Toast.show(
|
||||
replyTo
|
||||
? _(msg`Your reply has been published`)
|
||||
: _(msg`Your post has been published`),
|
||||
)
|
||||
}
|
||||
|
||||
const canPost = useMemo(
|
||||
|
@ -269,7 +273,9 @@ export const ComposePost = observer(function ComposePost({
|
|||
(!requireAltTextEnabled || !gallery.needsAltText),
|
||||
[graphemeLength, requireAltTextEnabled, gallery.needsAltText],
|
||||
)
|
||||
const selectTextInputPlaceholder = replyTo ? 'Write your reply' : `What's up?`
|
||||
const selectTextInputPlaceholder = replyTo
|
||||
? _(msg`Write your reply`)
|
||||
: _(msg`What's up?`)
|
||||
|
||||
const canSelectImages = useMemo(() => gallery.size < 4, [gallery.size])
|
||||
const hasMedia = gallery.size > 0 || Boolean(extLink)
|
||||
|
@ -291,7 +297,9 @@ export const ComposePost = observer(function ComposePost({
|
|||
onAccessibilityEscape={onPressCancel}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Cancel`)}
|
||||
accessibilityHint="Closes post composer and discards post draft">
|
||||
accessibilityHint={_(
|
||||
msg`Closes post composer and discards post draft`,
|
||||
)}>
|
||||
<Text style={[pal.link, s.f18]}>
|
||||
<Trans>Cancel</Trans>
|
||||
</Text>
|
||||
|
@ -323,7 +331,7 @@ export const ComposePost = observer(function ComposePost({
|
|||
onPress={onPressPublish}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={
|
||||
replyTo ? 'Publish reply' : 'Publish post'
|
||||
replyTo ? _(msg`Publish reply`) : _(msg`Publish post`)
|
||||
}
|
||||
accessibilityHint="">
|
||||
<LinearGradient
|
||||
|
@ -335,14 +343,18 @@ export const ComposePost = observer(function ComposePost({
|
|||
end={{x: 1, y: 1}}
|
||||
style={styles.postBtn}>
|
||||
<Text style={[s.white, s.f16, s.bold]}>
|
||||
{replyTo ? 'Reply' : 'Post'}
|
||||
{replyTo ? (
|
||||
<Trans context="action">Reply</Trans>
|
||||
) : (
|
||||
<Trans context="action">Post</Trans>
|
||||
)}
|
||||
</Text>
|
||||
</LinearGradient>
|
||||
</TouchableOpacity>
|
||||
) : (
|
||||
<View style={[styles.postBtn, pal.btn]}>
|
||||
<Text style={[pal.textLight, s.f16, s.bold]}>
|
||||
<Trans>Post</Trans>
|
||||
<Trans context="action">Post</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
|
@ -400,7 +412,9 @@ export const ComposePost = observer(function ComposePost({
|
|||
onError={setError}
|
||||
accessible={true}
|
||||
accessibilityLabel={_(msg`Write post`)}
|
||||
accessibilityHint={`Compose posts up to ${MAX_GRAPHEME_LENGTH} characters in length`}
|
||||
accessibilityHint={_(
|
||||
msg`Compose posts up to ${MAX_GRAPHEME_LENGTH} characters in length`,
|
||||
)}
|
||||
/>
|
||||
</View>
|
||||
|
||||
|
@ -429,7 +443,9 @@ export const ComposePost = observer(function ComposePost({
|
|||
onPress={() => onPressAddLinkCard(url)}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Add link card`)}
|
||||
accessibilityHint={`Creates a card with a thumbnail. The card links to ${url}`}>
|
||||
accessibilityHint={_(
|
||||
msg`Creates a card with a thumbnail. The card links to ${url}`,
|
||||
)}>
|
||||
<Text style={pal.text}>
|
||||
<Trans>Add link card:</Trans>{' '}
|
||||
<Text style={[pal.link, s.ml5]}>{toShortUrl(url)}</Text>
|
||||
|
|
|
@ -68,7 +68,7 @@ export const ExternalEmbed = ({
|
|||
onPress={onRemove}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Remove image preview`)}
|
||||
accessibilityHint={`Removes default thumbnail from ${link.uri}`}
|
||||
accessibilityHint={_(msg`Removes default thumbnail from ${link.uri}`)}
|
||||
onAccessibilityEscape={onRemove}>
|
||||
<FontAwesomeIcon size={18} icon="xmark" style={s.white} />
|
||||
</TouchableOpacity>
|
||||
|
|
|
@ -22,7 +22,7 @@ export function ComposePrompt({onPressCompose}: {onPressCompose: () => void}) {
|
|||
onPress={() => onPressCompose()}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Compose reply`)}
|
||||
accessibilityHint="Opens composer">
|
||||
accessibilityHint={_(msg`Opens composer`)}>
|
||||
<UserAvatar avatar={profile?.avatar} size={38} />
|
||||
<Text
|
||||
type="xl"
|
||||
|
|
|
@ -58,7 +58,7 @@ export function OpenCameraBtn({gallery}: Props) {
|
|||
hitSlop={HITSLOP_10}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Camera`)}
|
||||
accessibilityHint="Opens camera on device">
|
||||
accessibilityHint={_(msg`Opens camera on device`)}>
|
||||
<FontAwesomeIcon
|
||||
icon="camera"
|
||||
style={pal.link as FontAwesomeIconStyle}
|
||||
|
|
|
@ -41,7 +41,7 @@ export function SelectPhotoBtn({gallery}: Props) {
|
|||
hitSlop={HITSLOP_10}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Gallery`)}
|
||||
accessibilityHint="Opens device photo gallery">
|
||||
accessibilityHint={_(msg`Opens device photo gallery`)}>
|
||||
<FontAwesomeIcon
|
||||
icon={['far', 'image']}
|
||||
style={pal.link as FontAwesomeIconStyle}
|
||||
|
|
|
@ -17,6 +17,7 @@ import {usePalette} from 'lib/hooks/usePalette'
|
|||
import {Text} from 'view/com/util/text/Text'
|
||||
import {UserAvatar} from 'view/com/util/UserAvatar'
|
||||
import {useGrapheme} from '../hooks/useGrapheme'
|
||||
import {Trans} from '@lingui/macro'
|
||||
|
||||
interface MentionListRef {
|
||||
onKeyDown: (props: SuggestionKeyDownProps) => boolean
|
||||
|
@ -187,7 +188,7 @@ const MentionList = forwardRef<MentionListRef, SuggestionProps>(
|
|||
})
|
||||
) : (
|
||||
<Text type="sm" style={[pal.text, styles.noResult]}>
|
||||
No result
|
||||
<Trans>No result</Trans>
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
|
|
|
@ -197,7 +197,7 @@ export function FeedPage({
|
|||
onPress={onPressCompose}
|
||||
icon={<ComposeIcon2 strokeWidth={1.5} size={29} style={s.white} />}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`New post`)}
|
||||
accessibilityLabel={_(msg({message: `New post`, context: 'action'}))}
|
||||
accessibilityHint=""
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -14,7 +14,7 @@ import * as Toast from 'view/com/util/Toast'
|
|||
import {sanitizeHandle} from 'lib/strings/handles'
|
||||
import {logger} from '#/logger'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {
|
||||
usePinFeedMutation,
|
||||
|
@ -108,9 +108,9 @@ export function FeedSourceCardLoaded({
|
|||
try {
|
||||
await removeFeed({uri: feed.uri})
|
||||
// await item.unsave()
|
||||
Toast.show('Removed from my feeds')
|
||||
Toast.show(_(msg`Removed from my feeds`))
|
||||
} catch (e) {
|
||||
Toast.show('There was an issue contacting your server')
|
||||
Toast.show(_(msg`There was an issue contacting your server`))
|
||||
logger.error('Failed to unsave feed', {error: e})
|
||||
}
|
||||
},
|
||||
|
@ -122,9 +122,9 @@ export function FeedSourceCardLoaded({
|
|||
} else {
|
||||
await saveFeed({uri: feed.uri})
|
||||
}
|
||||
Toast.show('Added to my feeds')
|
||||
Toast.show(_(msg`Added to my feeds`))
|
||||
} catch (e) {
|
||||
Toast.show('There was an issue contacting your server')
|
||||
Toast.show(_(msg`There was an issue contacting your server`))
|
||||
logger.error('Failed to save feed', {error: e})
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ export function FeedSourceCardLoaded({
|
|||
testID={`feed-${feedUri}-toggleSave`}
|
||||
disabled={isRemovePending}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={'Remove from my feeds'}
|
||||
accessibilityLabel={_(msg`Remove from my feeds`)}
|
||||
accessibilityHint=""
|
||||
onPress={() => {
|
||||
openModal({
|
||||
|
@ -175,9 +175,11 @@ export function FeedSourceCardLoaded({
|
|||
try {
|
||||
await removeFeed({uri: feedUri})
|
||||
// await item.unsave()
|
||||
Toast.show('Removed from my feeds')
|
||||
Toast.show(_(msg`Removed from my feeds`))
|
||||
} catch (e) {
|
||||
Toast.show('There was an issue contacting your server')
|
||||
Toast.show(
|
||||
_(msg`There was an issue contacting your server`),
|
||||
)
|
||||
logger.error('Failed to unsave feed', {error: e})
|
||||
}
|
||||
},
|
||||
|
@ -223,8 +225,11 @@ export function FeedSourceCardLoaded({
|
|||
{feed.displayName}
|
||||
</Text>
|
||||
<Text style={[pal.textLight]} numberOfLines={3}>
|
||||
{feed.type === 'feed' ? 'Feed' : 'List'} by{' '}
|
||||
{sanitizeHandle(feed.creatorHandle, '@')}
|
||||
{feed.type === 'feed' ? (
|
||||
<Trans>Feed by {sanitizeHandle(feed.creatorHandle, '@')}</Trans>
|
||||
) : (
|
||||
<Trans>List by {sanitizeHandle(feed.creatorHandle, '@')}</Trans>
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
|
@ -235,7 +240,7 @@ export function FeedSourceCardLoaded({
|
|||
disabled={isSavePending || isPinPending || isRemovePending}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={
|
||||
isSaved ? 'Remove from my feeds' : 'Add to my feeds'
|
||||
isSaved ? _(msg`Remove from my feeds`) : _(msg`Add to my feeds`)
|
||||
}
|
||||
accessibilityHint=""
|
||||
onPress={onToggleSaved}
|
||||
|
@ -269,8 +274,10 @@ export function FeedSourceCardLoaded({
|
|||
|
||||
{showLikes && feed.type === 'feed' ? (
|
||||
<Text type="sm-medium" style={[pal.text, pal.textLight]}>
|
||||
Liked by {feed.likeCount || 0}{' '}
|
||||
{pluralize(feed.likeCount || 0, 'user')}
|
||||
<Trans>
|
||||
Liked by {feed.likeCount || 0}{' '}
|
||||
{pluralize(feed.likeCount || 0, 'user')}
|
||||
</Trans>
|
||||
</Text>
|
||||
) : null}
|
||||
</Pressable>
|
||||
|
|
|
@ -9,13 +9,14 @@ import {Text} from '../util/text/Text'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useProfileFeedgensQuery, RQKEY} from '#/state/queries/profile-feedgens'
|
||||
import {logger} from '#/logger'
|
||||
import {Trans} from '@lingui/macro'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {useTheme} from '#/lib/ThemeContext'
|
||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||
import {hydrateFeedGenerator} from '#/state/queries/feed'
|
||||
import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||
import {isNative} from '#/platform/detection'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
const LOADING = {_reactKey: '__loading__'}
|
||||
const EMPTY = {_reactKey: '__empty__'}
|
||||
|
@ -43,6 +44,7 @@ export const ProfileFeedgens = React.forwardRef<
|
|||
ref,
|
||||
) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const theme = useTheme()
|
||||
const [isPTRing, setIsPTRing] = React.useState(false)
|
||||
const opts = React.useMemo(() => ({enabled}), [enabled])
|
||||
|
@ -142,7 +144,9 @@ export const ProfileFeedgens = React.forwardRef<
|
|||
} else if (item === LOAD_MORE_ERROR_ITEM) {
|
||||
return (
|
||||
<LoadMoreRetryBtn
|
||||
label="There was an issue fetching your lists. Tap here to try again."
|
||||
label={_(
|
||||
msg`There was an issue fetching your lists. Tap here to try again.`,
|
||||
)}
|
||||
onPress={onPressRetryLoadMore}
|
||||
/>
|
||||
)
|
||||
|
@ -162,7 +166,7 @@ export const ProfileFeedgens = React.forwardRef<
|
|||
}
|
||||
return null
|
||||
},
|
||||
[error, refetch, onPressRetryLoadMore, pal, preferences],
|
||||
[error, refetch, onPressRetryLoadMore, pal, preferences, _],
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
|
@ -24,7 +24,7 @@ const ImageDefaultHeader = ({onRequestClose}: Props) => (
|
|||
hitSlop={HIT_SLOP}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t`Close image`}
|
||||
accessibilityHint="Closes viewer for header image"
|
||||
accessibilityHint={t`Closes viewer for header image`}
|
||||
onAccessibilityEscape={onRequestClose}>
|
||||
<Text style={styles.closeText}>✕</Text>
|
||||
</TouchableOpacity>
|
||||
|
|
|
@ -15,6 +15,8 @@ import {
|
|||
ProfileImageLightbox,
|
||||
ImagesLightbox,
|
||||
} from '#/state/lightbox'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
export function Lightbox() {
|
||||
const {activeLightbox} = useLightbox()
|
||||
|
@ -53,6 +55,7 @@ export function Lightbox() {
|
|||
}
|
||||
|
||||
function LightboxFooter({imageIndex}: {imageIndex: number}) {
|
||||
const {_} = useLingui()
|
||||
const {activeLightbox} = useLightbox()
|
||||
const [isAltExpanded, setAltExpanded] = React.useState(false)
|
||||
const [permissionResponse, requestPermission] = MediaLibrary.usePermissions()
|
||||
|
@ -60,12 +63,14 @@ function LightboxFooter({imageIndex}: {imageIndex: number}) {
|
|||
const saveImageToAlbumWithToasts = React.useCallback(
|
||||
async (uri: string) => {
|
||||
if (!permissionResponse || permissionResponse.granted === false) {
|
||||
Toast.show('Permission to access camera roll is required.')
|
||||
Toast.show(_(msg`Permission to access camera roll is required.`))
|
||||
if (permissionResponse?.canAskAgain) {
|
||||
requestPermission()
|
||||
} else {
|
||||
Toast.show(
|
||||
'Permission to access camera roll was denied. Please enable it in your system settings.',
|
||||
_(
|
||||
msg`Permission to access camera roll was denied. Please enable it in your system settings.`,
|
||||
),
|
||||
)
|
||||
}
|
||||
return
|
||||
|
@ -78,7 +83,7 @@ function LightboxFooter({imageIndex}: {imageIndex: number}) {
|
|||
Toast.show(`Failed to save image: ${String(e)}`)
|
||||
}
|
||||
},
|
||||
[permissionResponse, requestPermission],
|
||||
[permissionResponse, requestPermission, _],
|
||||
)
|
||||
|
||||
const lightbox = activeLightbox
|
||||
|
@ -117,7 +122,7 @@ function LightboxFooter({imageIndex}: {imageIndex: number}) {
|
|||
onPress={() => saveImageToAlbumWithToasts(uri)}>
|
||||
<FontAwesomeIcon icon={['far', 'floppy-disk']} style={s.white} />
|
||||
<Text type="xl" style={s.white}>
|
||||
Save
|
||||
<Trans context="action">Save</Trans>
|
||||
</Text>
|
||||
</Button>
|
||||
<Button
|
||||
|
@ -126,7 +131,7 @@ function LightboxFooter({imageIndex}: {imageIndex: number}) {
|
|||
onPress={() => shareImageModal({uri})}>
|
||||
<FontAwesomeIcon icon="arrow-up-from-bracket" style={s.white} />
|
||||
<Text type="xl" style={s.white}>
|
||||
Share
|
||||
<Trans context="action">Share</Trans>
|
||||
</Text>
|
||||
</Button>
|
||||
</View>
|
||||
|
|
|
@ -110,7 +110,7 @@ function LightboxInner({
|
|||
onPress={onClose}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Close image viewer`)}
|
||||
accessibilityHint="Exits image view"
|
||||
accessibilityHint={_(msg`Exits image view`)}
|
||||
onAccessibilityEscape={onClose}>
|
||||
<View style={styles.imageCenterer}>
|
||||
<Image
|
||||
|
@ -154,7 +154,9 @@ function LightboxInner({
|
|||
<View style={styles.footer}>
|
||||
<Pressable
|
||||
accessibilityLabel={_(msg`Expand alt text`)}
|
||||
accessibilityHint="If alt text is long, toggles alt text expanded state"
|
||||
accessibilityHint={_(
|
||||
msg`If alt text is long, toggles alt text expanded state`,
|
||||
)}
|
||||
onPress={() => {
|
||||
setAltExpanded(!isAltExpanded)
|
||||
}}>
|
||||
|
|
|
@ -11,6 +11,7 @@ import {useSession} from '#/state/session'
|
|||
import {sanitizeDisplayName} from 'lib/strings/display-names'
|
||||
import {sanitizeHandle} from 'lib/strings/handles'
|
||||
import {makeProfileLink} from 'lib/routes/links'
|
||||
import {Trans} from '@lingui/macro'
|
||||
|
||||
export const ListCard = ({
|
||||
testID,
|
||||
|
@ -76,19 +77,28 @@ export const ListCard = ({
|
|||
{sanitizeDisplayName(list.name)}
|
||||
</Text>
|
||||
<Text type="md" style={[pal.textLight]} numberOfLines={1}>
|
||||
{list.purpose === 'app.bsky.graph.defs#curatelist' && 'User list '}
|
||||
{list.purpose === 'app.bsky.graph.defs#curatelist' &&
|
||||
(list.creator.did === currentAccount?.did ? (
|
||||
<Trans>User list by you</Trans>
|
||||
) : (
|
||||
<Trans>
|
||||
User list by {sanitizeHandle(list.creator.handle, '@')}
|
||||
</Trans>
|
||||
))}
|
||||
{list.purpose === 'app.bsky.graph.defs#modlist' &&
|
||||
'Moderation list '}
|
||||
by{' '}
|
||||
{list.creator.did === currentAccount?.did
|
||||
? 'you'
|
||||
: sanitizeHandle(list.creator.handle, '@')}
|
||||
(list.creator.did === currentAccount?.did ? (
|
||||
<Trans>Moderation list by you</Trans>
|
||||
) : (
|
||||
<Trans>
|
||||
Moderation list by {sanitizeHandle(list.creator.handle, '@')}
|
||||
</Trans>
|
||||
))}
|
||||
</Text>
|
||||
{!!list.viewer?.muted && (
|
||||
<View style={s.flexRow}>
|
||||
<View style={[s.mt5, pal.btn, styles.pill]}>
|
||||
<Text type="xs" style={pal.text}>
|
||||
Subscribed
|
||||
<Trans>Subscribed</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
|
|
@ -20,6 +20,8 @@ import {logger} from '#/logger'
|
|||
import {useModalControls} from '#/state/modals'
|
||||
import {useSession} from '#/state/session'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
|
||||
const LOADING_ITEM = {_reactKey: '__loading__'}
|
||||
const EMPTY_ITEM = {_reactKey: '__empty__'}
|
||||
|
@ -50,6 +52,7 @@ export function ListMembers({
|
|||
desktopFixedHeightOffset?: number
|
||||
}) {
|
||||
const {track} = useAnalytics()
|
||||
const {_} = useLingui()
|
||||
const [isRefreshing, setIsRefreshing] = React.useState(false)
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const {openModal} = useModalControls()
|
||||
|
@ -143,12 +146,12 @@ export function ListMembers({
|
|||
<Button
|
||||
testID={`user-${profile.handle}-editBtn`}
|
||||
type="default"
|
||||
label="Edit"
|
||||
label={_(msg({message: 'Edit', context: 'action'}))}
|
||||
onPress={() => onPressEditMembership(profile)}
|
||||
/>
|
||||
)
|
||||
},
|
||||
[isOwner, onPressEditMembership],
|
||||
[isOwner, onPressEditMembership, _],
|
||||
)
|
||||
|
||||
const renderItem = React.useCallback(
|
||||
|
@ -165,7 +168,9 @@ export function ListMembers({
|
|||
} else if (item === LOAD_MORE_ERROR_ITEM) {
|
||||
return (
|
||||
<LoadMoreRetryBtn
|
||||
label="There was an issue fetching the list. Tap here to try again."
|
||||
label={_(
|
||||
msg`There was an issue fetching the list. Tap here to try again.`,
|
||||
)}
|
||||
onPress={onPressRetryLoadMore}
|
||||
/>
|
||||
)
|
||||
|
@ -191,6 +196,7 @@ export function ListMembers({
|
|||
onPressTryAgain,
|
||||
onPressRetryLoadMore,
|
||||
isMobile,
|
||||
_,
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -10,11 +10,12 @@ import {useAnalytics} from 'lib/analytics/analytics'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useProfileListsQuery, RQKEY} from '#/state/queries/profile-lists'
|
||||
import {logger} from '#/logger'
|
||||
import {Trans} from '@lingui/macro'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {useTheme} from '#/lib/ThemeContext'
|
||||
import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||
import {isNative} from '#/platform/detection'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
const LOADING = {_reactKey: '__loading__'}
|
||||
const EMPTY = {_reactKey: '__empty__'}
|
||||
|
@ -42,6 +43,7 @@ export const ProfileLists = React.forwardRef<SectionRef, ProfileListsProps>(
|
|||
const pal = usePalette('default')
|
||||
const theme = useTheme()
|
||||
const {track} = useAnalytics()
|
||||
const {_} = useLingui()
|
||||
const [isPTRing, setIsPTRing] = React.useState(false)
|
||||
const opts = React.useMemo(() => ({enabled}), [enabled])
|
||||
const {
|
||||
|
@ -149,7 +151,9 @@ export const ProfileLists = React.forwardRef<SectionRef, ProfileListsProps>(
|
|||
} else if (item === LOAD_MORE_ERROR_ITEM) {
|
||||
return (
|
||||
<LoadMoreRetryBtn
|
||||
label="There was an issue fetching your lists. Tap here to try again."
|
||||
label={_(
|
||||
msg`There was an issue fetching your lists. Tap here to try again.`,
|
||||
)}
|
||||
onPress={onPressRetryLoadMore}
|
||||
/>
|
||||
)
|
||||
|
@ -164,7 +168,7 @@ export const ProfileLists = React.forwardRef<SectionRef, ProfileListsProps>(
|
|||
/>
|
||||
)
|
||||
},
|
||||
[error, refetch, onPressRetryLoadMore, pal],
|
||||
[error, refetch, onPressRetryLoadMore, pal, _],
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
|
@ -72,10 +72,10 @@ export function Component({}: {}) {
|
|||
const onCopy = React.useCallback(() => {
|
||||
if (appPassword) {
|
||||
Clipboard.setString(appPassword)
|
||||
Toast.show('Copied to clipboard')
|
||||
Toast.show(_(msg`Copied to clipboard`))
|
||||
setWasCopied(true)
|
||||
}
|
||||
}, [appPassword])
|
||||
}, [appPassword, _])
|
||||
|
||||
const onDone = React.useCallback(() => {
|
||||
closeModal()
|
||||
|
@ -85,7 +85,9 @@ export function Component({}: {}) {
|
|||
// if name is all whitespace, we don't allow it
|
||||
if (!name || !name.trim()) {
|
||||
Toast.show(
|
||||
'Please enter a name for your app password. All spaces is not allowed.',
|
||||
_(
|
||||
msg`Please enter a name for your app password. All spaces is not allowed.`,
|
||||
),
|
||||
'times',
|
||||
)
|
||||
return
|
||||
|
@ -93,14 +95,14 @@ export function Component({}: {}) {
|
|||
// if name is too short (under 4 chars), we don't allow it
|
||||
if (name.length < 4) {
|
||||
Toast.show(
|
||||
'App Password names must be at least 4 characters long.',
|
||||
_(msg`App Password names must be at least 4 characters long.`),
|
||||
'times',
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (passwords?.find(p => p.name === name)) {
|
||||
Toast.show('This name is already in use', 'times')
|
||||
Toast.show(_(msg`This name is already in use`), 'times')
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -109,11 +111,11 @@ export function Component({}: {}) {
|
|||
if (newPassword) {
|
||||
setAppPassword(newPassword.password)
|
||||
} else {
|
||||
Toast.show('Failed to create app password.', 'times')
|
||||
Toast.show(_(msg`Failed to create app password.`), 'times')
|
||||
// TODO: better error handling (?)
|
||||
}
|
||||
} catch (e) {
|
||||
Toast.show('Failed to create app password.', 'times')
|
||||
Toast.show(_(msg`Failed to create app password.`), 'times')
|
||||
logger.error('Failed to create app password', {error: e})
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +129,9 @@ export function Component({}: {}) {
|
|||
setName(text)
|
||||
} else {
|
||||
Toast.show(
|
||||
'App Password names can only contain letters, numbers, spaces, dashes, and underscores.',
|
||||
_(
|
||||
msg`App Password names can only contain letters, numbers, spaces, dashes, and underscores.`,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -158,7 +162,7 @@ export function Component({}: {}) {
|
|||
style={[styles.input, pal.text]}
|
||||
onChangeText={_onChangeText}
|
||||
value={name}
|
||||
placeholder="Enter a name for this App Password"
|
||||
placeholder={_(msg`Enter a name for this App Password`)}
|
||||
placeholderTextColor={pal.colors.textLight}
|
||||
autoCorrect={false}
|
||||
autoComplete="off"
|
||||
|
@ -175,7 +179,7 @@ export function Component({}: {}) {
|
|||
onEndEditing={createAppPassword}
|
||||
accessible={true}
|
||||
accessibilityLabel={_(msg`Name`)}
|
||||
accessibilityHint="Input name for app password"
|
||||
accessibilityHint={_(msg`Input name for app password`)}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
|
@ -184,7 +188,7 @@ export function Component({}: {}) {
|
|||
onPress={onCopy}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Copy`)}
|
||||
accessibilityHint="Copies app password">
|
||||
accessibilityHint={_(msg`Copies app password`)}>
|
||||
<Text type="2xl-bold" style={[pal.text]}>
|
||||
{appPassword}
|
||||
</Text>
|
||||
|
@ -221,7 +225,7 @@ export function Component({}: {}) {
|
|||
<View style={styles.btnContainer}>
|
||||
<Button
|
||||
type="primary"
|
||||
label={!appPassword ? 'Create App Password' : 'Done'}
|
||||
label={!appPassword ? _(msg`Create App Password`) : _(msg`Done`)}
|
||||
style={styles.btn}
|
||||
labelStyle={styles.btnLabel}
|
||||
onPress={!appPassword ? createAppPassword : onDone}
|
||||
|
|
|
@ -45,7 +45,7 @@ export function Component(props: ReportComponentProps) {
|
|||
},
|
||||
reason: details,
|
||||
})
|
||||
Toast.show("We'll look into your appeal promptly.")
|
||||
Toast.show(_(msg`We'll look into your appeal promptly.`))
|
||||
} finally {
|
||||
closeModal()
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ function Inner({preferences}: {preferences: UsePreferencesQueryResponse}) {
|
|||
buttonStyle={[pal.border, styles.dateInputButton]}
|
||||
buttonLabelType="lg"
|
||||
accessibilityLabel={_(msg`Birthday`)}
|
||||
accessibilityHint="Enter your birth date"
|
||||
accessibilityHint={_(msg`Enter your birth date`)}
|
||||
accessibilityLabelledBy="birthDate"
|
||||
/>
|
||||
</View>
|
||||
|
|
|
@ -38,7 +38,7 @@ export function Component() {
|
|||
|
||||
const onRequestChange = async () => {
|
||||
if (email === currentAccount?.email) {
|
||||
setError('Enter your new email above')
|
||||
setError(_(msg`Enter your new email above`))
|
||||
return
|
||||
}
|
||||
setError('')
|
||||
|
@ -53,7 +53,7 @@ export function Component() {
|
|||
email: email.trim(),
|
||||
emailConfirmed: false,
|
||||
})
|
||||
Toast.show('Email updated')
|
||||
Toast.show(_(msg`Email updated`))
|
||||
setStage(Stages.Done)
|
||||
}
|
||||
} catch (e) {
|
||||
|
@ -85,7 +85,7 @@ export function Component() {
|
|||
email: email.trim(),
|
||||
emailConfirmed: false,
|
||||
})
|
||||
Toast.show('Email updated')
|
||||
Toast.show(_(msg`Email updated`))
|
||||
setStage(Stages.Done)
|
||||
} catch (e) {
|
||||
setError(cleanError(String(e)))
|
||||
|
|
|
@ -147,7 +147,7 @@ export function Inner({
|
|||
onPress={onPressCancel}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Cancel change handle`)}
|
||||
accessibilityHint="Exits handle change process"
|
||||
accessibilityHint={_(msg`Exits handle change process`)}
|
||||
onAccessibilityEscape={onPressCancel}>
|
||||
<Text type="lg" style={pal.textLight}>
|
||||
Cancel
|
||||
|
@ -168,7 +168,7 @@ export function Inner({
|
|||
onPress={onPressSave}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Save handle change`)}
|
||||
accessibilityHint={`Saves handle change to ${handle}`}>
|
||||
accessibilityHint={_(msg`Saves handle change to ${handle}`)}>
|
||||
<Text type="2xl-medium" style={pal.link}>
|
||||
<Trans>Save</Trans>
|
||||
</Text>
|
||||
|
@ -263,14 +263,16 @@ function ProvidedHandleForm({
|
|||
editable={!isProcessing}
|
||||
accessible={true}
|
||||
accessibilityLabel={_(msg`Handle`)}
|
||||
accessibilityHint="Sets Bluesky username"
|
||||
accessibilityHint={_(msg`Sets Bluesky username`)}
|
||||
/>
|
||||
</View>
|
||||
<Text type="md" style={[pal.textLight, s.pl10, s.pt10]}>
|
||||
<Trans>Your full handle will be</Trans>{' '}
|
||||
<Text type="md-bold" style={pal.textLight}>
|
||||
@{createFullHandle(handle, userDomain)}
|
||||
</Text>
|
||||
<Trans>
|
||||
Your full handle will be{' '}
|
||||
<Text type="md-bold" style={pal.textLight}>
|
||||
@{createFullHandle(handle, userDomain)}
|
||||
</Text>
|
||||
</Trans>
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
onPress={onToggleCustom}
|
||||
|
|
|
@ -12,7 +12,7 @@ import {cleanError} from 'lib/strings/errors'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import type {ConfirmModal} from '#/state/modals'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
|
@ -72,10 +72,10 @@ export function Component({
|
|||
onPress={onPress}
|
||||
style={[styles.btn, confirmBtnStyle]}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Confirm`)}
|
||||
accessibilityLabel={_(msg({message: 'Confirm', context: 'action'}))}
|
||||
accessibilityHint="">
|
||||
<Text style={[s.white, s.bold, s.f18]}>
|
||||
{confirmBtnText ?? 'Confirm'}
|
||||
{confirmBtnText ?? <Trans context="action">Confirm</Trans>}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
|
@ -85,10 +85,10 @@ export function Component({
|
|||
onPress={onPressCancel}
|
||||
style={[styles.btnCancel, s.mt10]}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Cancel`)}
|
||||
accessibilityLabel={_(msg({message: 'Cancel', context: 'action'}))}
|
||||
accessibilityHint="">
|
||||
<Text type="button-lg" style={pal.textLight}>
|
||||
{cancelBtnText ?? 'Cancel'}
|
||||
{cancelBtnText ?? <Trans context="action">Cancel</Trans>}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
|
|
|
@ -148,9 +148,13 @@ function AdultContentEnabledPref() {
|
|||
) : typeof preferences?.birthDate === 'undefined' ? (
|
||||
<View style={[pal.viewLight, styles.agePrompt]}>
|
||||
<Text type="md" style={[pal.text, {flex: 1}]}>
|
||||
Confirm your age to enable adult content.
|
||||
<Trans>Confirm your age to enable adult content.</Trans>
|
||||
</Text>
|
||||
<Button type="primary" label="Set Age" onPress={onSetAge} />
|
||||
<Button
|
||||
type="primary"
|
||||
label={_(msg({message: 'Set Age', context: 'action'}))}
|
||||
onPress={onSetAge}
|
||||
/>
|
||||
</View>
|
||||
) : (preferences.userAge || 0) >= 18 ? (
|
||||
<ToggleButton
|
||||
|
@ -165,7 +169,11 @@ function AdultContentEnabledPref() {
|
|||
<Text type="md" style={[pal.text, {flex: 1}]}>
|
||||
<Trans>You must be 18 or older to enable adult content.</Trans>
|
||||
</Text>
|
||||
<Button type="primary" label="Set Age" onPress={onSetAge} />
|
||||
<Button
|
||||
type="primary"
|
||||
label={_(msg({message: 'Set Age', context: 'action'}))}
|
||||
onPress={onSetAge}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
@ -208,7 +216,7 @@ function ContentLabelPref({
|
|||
|
||||
{disabled || !visibility ? (
|
||||
<Text type="sm-bold" style={pal.textLight}>
|
||||
<Trans>Hide</Trans>
|
||||
<Trans context="action">Hide</Trans>
|
||||
</Text>
|
||||
) : (
|
||||
<SelectGroup
|
||||
|
@ -229,6 +237,7 @@ interface SelectGroupProps {
|
|||
|
||||
function SelectGroup({current, onChange, labelGroup}: SelectGroupProps) {
|
||||
const {_} = useLingui()
|
||||
|
||||
return (
|
||||
<View style={styles.selectableBtns}>
|
||||
<SelectableBtn
|
||||
|
@ -279,6 +288,8 @@ function SelectableBtn({
|
|||
}: SelectableBtnProps) {
|
||||
const pal = usePalette('default')
|
||||
const palPrimary = usePalette('inverted')
|
||||
const {_} = useLingui()
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
style={[
|
||||
|
@ -291,7 +302,9 @@ function SelectableBtn({
|
|||
onPress={() => onChange(value)}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={value}
|
||||
accessibilityHint={`Set ${value} for ${labelGroup} content moderation policy`}>
|
||||
accessibilityHint={_(
|
||||
msg`Set ${value} for ${labelGroup} content moderation policy`,
|
||||
)}>
|
||||
<Text style={current === value ? palPrimary.text : pal.text}>
|
||||
{label}
|
||||
</Text>
|
||||
|
|
|
@ -65,7 +65,6 @@ export function Component({
|
|||
return 'app.bsky.graph.defs#curatelist'
|
||||
}, [list, purpose])
|
||||
const isCurateList = activePurpose === 'app.bsky.graph.defs#curatelist'
|
||||
const purposeLabel = isCurateList ? 'User' : 'Moderation'
|
||||
|
||||
const [isProcessing, setProcessing] = useState<boolean>(false)
|
||||
const [name, setName] = useState<string>(list?.name || '')
|
||||
|
@ -106,7 +105,7 @@ export function Component({
|
|||
}
|
||||
const nameTrimmed = name.trim()
|
||||
if (!nameTrimmed) {
|
||||
setError('Name is required')
|
||||
setError(_(msg`Name is required`))
|
||||
return
|
||||
}
|
||||
setProcessing(true)
|
||||
|
@ -121,7 +120,11 @@ export function Component({
|
|||
description: description.trim(),
|
||||
avatar: newAvatar,
|
||||
})
|
||||
Toast.show(`${purposeLabel} list updated`)
|
||||
Toast.show(
|
||||
isCurateList
|
||||
? _(msg`User list updated`)
|
||||
: _(msg`Moderation list updated`),
|
||||
)
|
||||
onSave?.(list.uri)
|
||||
} else {
|
||||
const res = await listCreateMutation.mutateAsync({
|
||||
|
@ -130,14 +133,20 @@ export function Component({
|
|||
description,
|
||||
avatar: newAvatar,
|
||||
})
|
||||
Toast.show(`${purposeLabel} list created`)
|
||||
Toast.show(
|
||||
isCurateList
|
||||
? _(msg`User list created`)
|
||||
: _(msg`Moderation list created`),
|
||||
)
|
||||
onSave?.(res.uri)
|
||||
}
|
||||
closeModal()
|
||||
} catch (e: any) {
|
||||
if (isNetworkError(e)) {
|
||||
setError(
|
||||
'Failed to create the list. Check your internet connection and try again.',
|
||||
_(
|
||||
msg`Failed to create the list. Check your internet connection and try again.`,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
setError(cleanError(e))
|
||||
|
@ -153,13 +162,13 @@ export function Component({
|
|||
closeModal,
|
||||
activePurpose,
|
||||
isCurateList,
|
||||
purposeLabel,
|
||||
name,
|
||||
description,
|
||||
newAvatar,
|
||||
list,
|
||||
listMetadataMutation,
|
||||
listCreateMutation,
|
||||
_,
|
||||
])
|
||||
|
||||
return (
|
||||
|
@ -174,7 +183,17 @@ export function Component({
|
|||
testID="createOrEditListModal">
|
||||
<Text style={[styles.title, pal.text]}>
|
||||
<Trans>
|
||||
{list ? 'Edit' : 'New'} {purposeLabel} List
|
||||
{isCurateList ? (
|
||||
list ? (
|
||||
<Trans>Edit User List</Trans>
|
||||
) : (
|
||||
<Trans>New User List</Trans>
|
||||
)
|
||||
) : list ? (
|
||||
<Trans>Edit Moderation List</Trans>
|
||||
) : (
|
||||
<Trans>New Moderation List</Trans>
|
||||
)}
|
||||
</Trans>
|
||||
</Text>
|
||||
{error !== '' && (
|
||||
|
@ -202,7 +221,9 @@ export function Component({
|
|||
testID="editNameInput"
|
||||
style={[styles.textInput, pal.border, pal.text]}
|
||||
placeholder={
|
||||
isCurateList ? 'e.g. Great Posters' : 'e.g. Spammers'
|
||||
isCurateList
|
||||
? _(msg`e.g. Great Posters`)
|
||||
: _(msg`e.g. Spammers`)
|
||||
}
|
||||
placeholderTextColor={colors.gray4}
|
||||
value={name}
|
||||
|
@ -222,8 +243,8 @@ export function Component({
|
|||
style={[styles.textArea, pal.border, pal.text]}
|
||||
placeholder={
|
||||
isCurateList
|
||||
? 'e.g. The posters who never miss.'
|
||||
: 'e.g. Users that repeatedly reply with ads.'
|
||||
? _(msg`e.g. The posters who never miss.`)
|
||||
: _(msg`e.g. Users that repeatedly reply with ads.`)
|
||||
}
|
||||
placeholderTextColor={colors.gray4}
|
||||
keyboardAppearance={theme.colorScheme}
|
||||
|
@ -254,7 +275,7 @@ export function Component({
|
|||
end={{x: 1, y: 1}}
|
||||
style={[styles.btn]}>
|
||||
<Text style={[s.white, s.bold]}>
|
||||
<Trans>Save</Trans>
|
||||
<Trans context="action">Save</Trans>
|
||||
</Text>
|
||||
</LinearGradient>
|
||||
</TouchableOpacity>
|
||||
|
@ -269,7 +290,7 @@ export function Component({
|
|||
onAccessibilityEscape={onPressCancel}>
|
||||
<View style={[styles.btn]}>
|
||||
<Text style={[s.black, s.bold, pal.text]}>
|
||||
<Trans>Cancel</Trans>
|
||||
<Trans context="action">Cancel</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
|
|
|
@ -62,7 +62,7 @@ export function Component({}: {}) {
|
|||
password,
|
||||
token,
|
||||
})
|
||||
Toast.show('Your account has been deleted')
|
||||
Toast.show(_(msg`Your account has been deleted`))
|
||||
resetToTab('HomeTab')
|
||||
removeAccount(currentAccount)
|
||||
clearCurrentAccount()
|
||||
|
@ -125,7 +125,9 @@ export function Component({}: {}) {
|
|||
onPress={onPressSendEmail}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Send email`)}
|
||||
accessibilityHint="Sends email with confirmation code for account deletion">
|
||||
accessibilityHint={_(
|
||||
msg`Sends email with confirmation code for account deletion`,
|
||||
)}>
|
||||
<LinearGradient
|
||||
colors={[
|
||||
gradients.blueLight.start,
|
||||
|
@ -135,7 +137,7 @@ export function Component({}: {}) {
|
|||
end={{x: 1, y: 1}}
|
||||
style={[styles.btn]}>
|
||||
<Text type="button-lg" style={[s.white, s.bold]}>
|
||||
<Trans>Send Email</Trans>
|
||||
<Trans context="action">Send Email</Trans>
|
||||
</Text>
|
||||
</LinearGradient>
|
||||
</TouchableOpacity>
|
||||
|
@ -147,7 +149,7 @@ export function Component({}: {}) {
|
|||
accessibilityHint=""
|
||||
onAccessibilityEscape={onCancel}>
|
||||
<Text type="button-lg" style={pal.textLight}>
|
||||
<Trans>Cancel</Trans>
|
||||
<Trans context="action">Cancel</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</>
|
||||
|
@ -174,7 +176,9 @@ export function Component({}: {}) {
|
|||
onChangeText={setConfirmCode}
|
||||
accessibilityLabelledBy="confirmationCode"
|
||||
accessibilityLabel={_(msg`Confirmation code`)}
|
||||
accessibilityHint="Input confirmation code for account deletion"
|
||||
accessibilityHint={_(
|
||||
msg`Input confirmation code for account deletion`,
|
||||
)}
|
||||
/>
|
||||
<Text type="lg" style={styles.description} nativeID="password">
|
||||
<Trans>Please enter your password as well:</Trans>
|
||||
|
@ -189,7 +193,7 @@ export function Component({}: {}) {
|
|||
onChangeText={setPassword}
|
||||
accessibilityLabelledBy="password"
|
||||
accessibilityLabel={_(msg`Password`)}
|
||||
accessibilityHint="Input password for account deletion"
|
||||
accessibilityHint={_(msg`Input password for account deletion`)}
|
||||
/>
|
||||
{error ? (
|
||||
<View style={styles.mt20}>
|
||||
|
@ -220,7 +224,7 @@ export function Component({}: {}) {
|
|||
accessibilityHint="Exits account deletion process"
|
||||
onAccessibilityEscape={onCancel}>
|
||||
<Text type="button-lg" style={pal.textLight}>
|
||||
<Trans>Cancel</Trans>
|
||||
<Trans context="action">Cancel</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</>
|
||||
|
|
|
@ -112,16 +112,16 @@ export const Component = observer(function EditImageImpl({
|
|||
// },
|
||||
{
|
||||
name: 'flip' as const,
|
||||
label: 'Flip horizontal',
|
||||
label: _(msg`Flip horizontal`),
|
||||
onPress: onFlipHorizontal,
|
||||
},
|
||||
{
|
||||
name: 'flip' as const,
|
||||
label: 'Flip vertically',
|
||||
label: _(msg`Flip vertically`),
|
||||
onPress: onFlipVertical,
|
||||
},
|
||||
],
|
||||
[onFlipHorizontal, onFlipVertical],
|
||||
[onFlipHorizontal, onFlipVertical, _],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -284,7 +284,7 @@ export const Component = observer(function EditImageImpl({
|
|||
size={label?.startsWith('Flip') ? 22 : 24}
|
||||
style={[
|
||||
pal.text,
|
||||
label === 'Flip vertically'
|
||||
label === _(msg`Flip vertically`)
|
||||
? styles.flipVertical
|
||||
: undefined,
|
||||
]}
|
||||
|
@ -330,7 +330,7 @@ export const Component = observer(function EditImageImpl({
|
|||
end={{x: 1, y: 1}}
|
||||
style={[styles.btn]}>
|
||||
<Text type="xl-medium" style={s.white}>
|
||||
<Trans>Done</Trans>
|
||||
<Trans context="action">Done</Trans>
|
||||
</Text>
|
||||
</LinearGradient>
|
||||
</Pressable>
|
||||
|
|
|
@ -125,7 +125,7 @@ export function Component({
|
|||
newUserAvatar,
|
||||
newUserBanner,
|
||||
})
|
||||
Toast.show('Profile updated')
|
||||
Toast.show(_(msg`Profile updated`))
|
||||
onUpdate?.()
|
||||
closeModal()
|
||||
} catch (e: any) {
|
||||
|
@ -142,6 +142,7 @@ export function Component({
|
|||
newUserAvatar,
|
||||
newUserBanner,
|
||||
setImageError,
|
||||
_,
|
||||
])
|
||||
|
||||
return (
|
||||
|
@ -181,7 +182,7 @@ export function Component({
|
|||
<TextInput
|
||||
testID="editProfileDisplayNameInput"
|
||||
style={[styles.textInput, pal.border, pal.text]}
|
||||
placeholder="e.g. Alice Roberts"
|
||||
placeholder={_(msg`e.g. Alice Roberts`)}
|
||||
placeholderTextColor={colors.gray4}
|
||||
value={displayName}
|
||||
onChangeText={v =>
|
||||
|
@ -189,7 +190,7 @@ export function Component({
|
|||
}
|
||||
accessible={true}
|
||||
accessibilityLabel={_(msg`Display name`)}
|
||||
accessibilityHint="Edit your display name"
|
||||
accessibilityHint={_(msg`Edit your display name`)}
|
||||
/>
|
||||
</View>
|
||||
<View style={s.pb10}>
|
||||
|
@ -199,7 +200,7 @@ export function Component({
|
|||
<TextInput
|
||||
testID="editProfileDescriptionInput"
|
||||
style={[styles.textArea, pal.border, pal.text]}
|
||||
placeholder="e.g. Artist, dog-lover, and avid reader."
|
||||
placeholder={_(msg`e.g. Artist, dog-lover, and avid reader.`)}
|
||||
placeholderTextColor={colors.gray4}
|
||||
keyboardAppearance={theme.colorScheme}
|
||||
multiline
|
||||
|
@ -207,7 +208,7 @@ export function Component({
|
|||
onChangeText={v => setDescription(enforceLen(v, MAX_DESCRIPTION))}
|
||||
accessible={true}
|
||||
accessibilityLabel={_(msg`Description`)}
|
||||
accessibilityHint="Edit your profile description"
|
||||
accessibilityHint={_(msg`Edit your profile description`)}
|
||||
/>
|
||||
</View>
|
||||
{updateMutation.isPending ? (
|
||||
|
@ -221,7 +222,7 @@ export function Component({
|
|||
onPress={onPressSave}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Save`)}
|
||||
accessibilityHint="Saves any changes to your profile">
|
||||
accessibilityHint={_(msg`Saves any changes to your profile`)}>
|
||||
<LinearGradient
|
||||
colors={[gradients.blueLight.start, gradients.blueLight.end]}
|
||||
start={{x: 0, y: 0}}
|
||||
|
|
|
@ -19,7 +19,6 @@ import {usePalette} from 'lib/hooks/usePalette'
|
|||
import {isWeb} from 'platform/detection'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {cleanError} from 'lib/strings/errors'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {useInvitesState, useInvitesAPI} from '#/state/invites'
|
||||
|
@ -31,6 +30,7 @@ import {
|
|||
useInviteCodesQuery,
|
||||
InviteCodesQueryResponse,
|
||||
} from '#/state/queries/invites'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
export const snapPoints = ['70%']
|
||||
|
||||
|
@ -166,10 +166,10 @@ function InviteCode({
|
|||
accessibilityRole="button"
|
||||
accessibilityLabel={
|
||||
invites.available.length === 1
|
||||
? 'Invite codes: 1 available'
|
||||
: `Invite codes: ${invites.available.length} available`
|
||||
? _(msg`Invite codes: 1 available`)
|
||||
: _(msg`Invite codes: ${invites.available.length} available`)
|
||||
}
|
||||
accessibilityHint="Opens list of invite codes">
|
||||
accessibilityHint={_(msg`Opens list of invite codes`)}>
|
||||
<Text
|
||||
testID={`${testID}-code`}
|
||||
type={used ? 'md' : 'md-bold'}
|
||||
|
|
|
@ -67,7 +67,7 @@ export function Component({
|
|||
<TextInput
|
||||
testID="searchInput"
|
||||
style={[styles.searchInput, pal.border, pal.text]}
|
||||
placeholder="Search for users"
|
||||
placeholder={_(msg`Search for users`)}
|
||||
placeholderTextColor={pal.colors.textLight}
|
||||
value={query}
|
||||
onChangeText={setQuery}
|
||||
|
@ -85,7 +85,7 @@ export function Component({
|
|||
onPress={onPressCancelSearch}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Cancel search`)}
|
||||
accessibilityHint="Exits inputting search query"
|
||||
accessibilityHint={_(msg`Exits inputting search query`)}
|
||||
onAccessibilityEscape={onPressCancelSearch}
|
||||
hitSlop={HITSLOP_20}>
|
||||
<FontAwesomeIcon
|
||||
|
@ -141,7 +141,7 @@ export function Component({
|
|||
}}
|
||||
accessibilityLabel={_(msg`Done`)}
|
||||
accessibilityHint=""
|
||||
label="Done"
|
||||
label={_(msg({message: 'Done', context: 'action'}))}
|
||||
labelContainerStyle={{justifyContent: 'center', padding: 4}}
|
||||
labelStyle={[s.f18]}
|
||||
/>
|
||||
|
|
|
@ -10,6 +10,8 @@ import {isWeb} from 'platform/detection'
|
|||
import {listUriToHref} from 'lib/strings/url-helpers'
|
||||
import {Button} from '../util/forms/Button'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
|
||||
export const snapPoints = [300]
|
||||
|
||||
|
@ -23,19 +25,21 @@ export function Component({
|
|||
const {closeModal} = useModalControls()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
|
||||
let name
|
||||
let description
|
||||
if (!moderation.cause) {
|
||||
name = 'Content Warning'
|
||||
description =
|
||||
'Moderator has chosen to set a general warning on the content.'
|
||||
name = _(msg`Content Warning`)
|
||||
description = _(
|
||||
msg`Moderator has chosen to set a general warning on the content.`,
|
||||
)
|
||||
} else if (moderation.cause.type === 'blocking') {
|
||||
if (moderation.cause.source.type === 'list') {
|
||||
const list = moderation.cause.source.list
|
||||
name = 'User Blocked by List'
|
||||
name = _(msg`User Blocked by List`)
|
||||
description = (
|
||||
<>
|
||||
<Trans>
|
||||
This user is included in the{' '}
|
||||
<TextLink
|
||||
type="2xl"
|
||||
|
@ -44,25 +48,30 @@ export function Component({
|
|||
style={pal.link}
|
||||
/>{' '}
|
||||
list which you have blocked.
|
||||
</>
|
||||
</Trans>
|
||||
)
|
||||
} else {
|
||||
name = 'User Blocked'
|
||||
description = 'You have blocked this user. You cannot view their content.'
|
||||
name = _(msg`User Blocked`)
|
||||
description = _(
|
||||
msg`You have blocked this user. You cannot view their content.`,
|
||||
)
|
||||
}
|
||||
} else if (moderation.cause.type === 'blocked-by') {
|
||||
name = 'User Blocks You'
|
||||
description = 'This user has blocked you. You cannot view their content.'
|
||||
name = _(msg`User Blocks You`)
|
||||
description = _(
|
||||
msg`This user has blocked you. You cannot view their content.`,
|
||||
)
|
||||
} else if (moderation.cause.type === 'block-other') {
|
||||
name = 'Content Not Available'
|
||||
description =
|
||||
'This content is not available because one of the users involved has blocked the other.'
|
||||
name = _(msg`Content Not Available`)
|
||||
description = _(
|
||||
msg`This content is not available because one of the users involved has blocked the other.`,
|
||||
)
|
||||
} else if (moderation.cause.type === 'muted') {
|
||||
if (moderation.cause.source.type === 'list') {
|
||||
const list = moderation.cause.source.list
|
||||
name = <>Account Muted by List</>
|
||||
name = _(msg`Account Muted by List`)
|
||||
description = (
|
||||
<>
|
||||
<Trans>
|
||||
This user is included the{' '}
|
||||
<TextLink
|
||||
type="2xl"
|
||||
|
@ -71,11 +80,11 @@ export function Component({
|
|||
style={pal.link}
|
||||
/>{' '}
|
||||
list which you have muted.
|
||||
</>
|
||||
</Trans>
|
||||
)
|
||||
} else {
|
||||
name = 'Account Muted'
|
||||
description = 'You have muted this user.'
|
||||
name = _(msg`Account Muted`)
|
||||
description = _(msg`You have muted this user.`)
|
||||
}
|
||||
} else {
|
||||
name = moderation.cause.labelDef.strings[context].en.name
|
||||
|
|
|
@ -14,11 +14,14 @@ import {ErrorScreen} from '../util/error/ErrorScreen'
|
|||
import {CenteredView} from '../util/Views'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
export const snapPoints = [520, '100%']
|
||||
|
||||
export function Component({did}: {did: string}) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const moderationOpts = useModerationOpts()
|
||||
const {
|
||||
data: profile,
|
||||
|
@ -43,7 +46,7 @@ export function Component({did}: {did: string}) {
|
|||
if (profileError) {
|
||||
return (
|
||||
<ErrorScreen
|
||||
title="Oops!"
|
||||
title={_(msg`Oops!`)}
|
||||
message={cleanError(profileError)}
|
||||
onPressTryAgain={refetchProfile}
|
||||
/>
|
||||
|
@ -55,8 +58,8 @@ export function Component({did}: {did: string}) {
|
|||
// should never happen
|
||||
return (
|
||||
<ErrorScreen
|
||||
title="Oops!"
|
||||
message="Something went wrong and we're not sure what."
|
||||
title={_(msg`Oops!`)}
|
||||
message={_(msg`Something went wrong and we're not sure what.`)}
|
||||
onPressTryAgain={refetchProfile}
|
||||
/>
|
||||
)
|
||||
|
@ -104,7 +107,7 @@ function ComponentLoaded({
|
|||
<>
|
||||
<InfoCircleIcon size={21} style={pal.textLight} />
|
||||
<ThemedText type="xl" fg="light">
|
||||
Swipe up to see more
|
||||
<Trans>Swipe up to see more</Trans>
|
||||
</ThemedText>
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -37,11 +37,23 @@ export function Component({
|
|||
style={[styles.actionBtn]}
|
||||
onPress={onRepost}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={isReposted ? 'Undo repost' : 'Repost'}
|
||||
accessibilityHint={isReposted ? 'Remove repost' : 'Repost '}>
|
||||
accessibilityLabel={
|
||||
isReposted
|
||||
? _(msg`Undo repost`)
|
||||
: _(msg({message: `Repost`, context: 'action'}))
|
||||
}
|
||||
accessibilityHint={
|
||||
isReposted
|
||||
? _(msg`Remove repost`)
|
||||
: _(msg({message: `Repost`, context: 'action'}))
|
||||
}>
|
||||
<RepostIcon strokeWidth={2} size={24} style={s.blue3} />
|
||||
<Text type="title-lg" style={[styles.actionBtnLabel, pal.text]}>
|
||||
<Trans>{!isReposted ? 'Repost' : 'Undo repost'}</Trans>
|
||||
{!isReposted ? (
|
||||
<Trans context="action">Repost</Trans>
|
||||
) : (
|
||||
<Trans>Undo repost</Trans>
|
||||
)}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
|
@ -49,11 +61,13 @@ export function Component({
|
|||
style={[styles.actionBtn]}
|
||||
onPress={onQuote}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Quote post`)}
|
||||
accessibilityLabel={_(
|
||||
msg({message: `Quote post`, context: 'action'}),
|
||||
)}
|
||||
accessibilityHint="">
|
||||
<FontAwesomeIcon icon="quote-left" size={24} style={s.blue3} />
|
||||
<Text type="title-lg" style={[styles.actionBtnLabel, pal.text]}>
|
||||
<Trans>Quote Post</Trans>
|
||||
<Trans context="action">Quote Post</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
|
|
@ -92,7 +92,7 @@ export function Component({
|
|||
testID="sexualLabelBtn"
|
||||
selected={selected.includes('sexual')}
|
||||
left
|
||||
label="Suggestive"
|
||||
label={_(msg`Suggestive`)}
|
||||
onSelect={() => toggleAdultLabel('sexual')}
|
||||
accessibilityHint=""
|
||||
style={s.flex1}
|
||||
|
@ -100,7 +100,7 @@ export function Component({
|
|||
<SelectableBtn
|
||||
testID="nudityLabelBtn"
|
||||
selected={selected.includes('nudity')}
|
||||
label="Nudity"
|
||||
label={_(msg`Nudity`)}
|
||||
onSelect={() => toggleAdultLabel('nudity')}
|
||||
accessibilityHint=""
|
||||
style={s.flex1}
|
||||
|
@ -108,7 +108,7 @@ export function Component({
|
|||
<SelectableBtn
|
||||
testID="pornLabelBtn"
|
||||
selected={selected.includes('porn')}
|
||||
label="Porn"
|
||||
label={_(msg`Porn`)}
|
||||
right
|
||||
onSelect={() => toggleAdultLabel('porn')}
|
||||
accessibilityHint=""
|
||||
|
@ -154,7 +154,7 @@ export function Component({
|
|||
accessibilityLabel={_(msg`Confirm`)}
|
||||
accessibilityHint="">
|
||||
<Text style={[s.white, s.bold, s.f18]}>
|
||||
<Trans>Done</Trans>
|
||||
<Trans context="action">Done</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
|
|
@ -101,7 +101,9 @@ export function Component({onSelect}: {onSelect: (url: string) => void}) {
|
|||
onChangeText={setCustomUrl}
|
||||
accessibilityLabel={_(msg`Custom domain`)}
|
||||
// TODO: Simplify this wording further to be understandable by everyone
|
||||
accessibilityHint="Use your domain as your Bluesky client service provider"
|
||||
accessibilityHint={_(
|
||||
msg`Use your domain as your Bluesky client service provider`,
|
||||
)}
|
||||
/>
|
||||
<TouchableOpacity
|
||||
testID="customServerSelectBtn"
|
||||
|
@ -110,7 +112,7 @@ export function Component({onSelect}: {onSelect: (url: string) => void}) {
|
|||
accessibilityRole="button"
|
||||
accessibilityLabel={`Confirm service. ${
|
||||
customUrl === ''
|
||||
? 'Button disabled. Input custom domain to proceed.'
|
||||
? _(msg`Button disabled. Input custom domain to proceed.`)
|
||||
: ''
|
||||
}`}
|
||||
accessibilityHint=""
|
||||
|
|
|
@ -62,7 +62,9 @@ function SwitchAccountCard({account}: {account: SessionAccount}) {
|
|||
onPress={isSwitchingAccounts ? undefined : onPressSignout}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Sign out`)}
|
||||
accessibilityHint={`Signs ${profile?.displayName} out of Bluesky`}>
|
||||
accessibilityHint={_(
|
||||
msg`Signs ${profile?.displayName} out of Bluesky`,
|
||||
)}>
|
||||
<Text type="lg" style={pal.link}>
|
||||
<Trans>Sign out</Trans>
|
||||
</Text>
|
||||
|
@ -92,8 +94,8 @@ function SwitchAccountCard({account}: {account: SessionAccount}) {
|
|||
isSwitchingAccounts ? undefined : () => onPressSwitchAccount(account)
|
||||
}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={`Switch to ${account.handle}`}
|
||||
accessibilityHint="Switches the account you are logged in to">
|
||||
accessibilityLabel={_(msg`Switch to ${account.handle}`)}
|
||||
accessibilityHint={_(msg`Switches the account you are logged in to`)}>
|
||||
{contents}
|
||||
</TouchableOpacity>
|
||||
)
|
||||
|
|
|
@ -126,10 +126,10 @@ export function Component({
|
|||
}}
|
||||
style={styles.btn}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Done`)}
|
||||
accessibilityLabel={_(msg({message: `Done`, context: 'action'}))}
|
||||
accessibilityHint="">
|
||||
<Text style={[s.white, s.bold, s.f18]}>
|
||||
<Trans>Done</Trans>
|
||||
<Trans context="action">Done</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
|
|
@ -76,10 +76,10 @@ export function Component({
|
|||
type="default"
|
||||
onPress={onPressDone}
|
||||
style={styles.footerBtn}
|
||||
accessibilityLabel={_(msg`Done`)}
|
||||
accessibilityLabel={_(msg({message: `Done`, context: 'action'}))}
|
||||
accessibilityHint=""
|
||||
onAccessibilityEscape={onPressDone}
|
||||
label={_(msg`Done`)}
|
||||
label={_(msg({message: `Done`, context: 'action'}))}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
@ -175,12 +175,22 @@ function ListItem({
|
|||
{sanitizeDisplayName(list.name)}
|
||||
</Text>
|
||||
<Text type="md" style={[pal.textLight]} numberOfLines={1}>
|
||||
{list.purpose === 'app.bsky.graph.defs#curatelist' && 'User list '}
|
||||
{list.purpose === 'app.bsky.graph.defs#modlist' && 'Moderation list '}
|
||||
by{' '}
|
||||
{list.creator.did === currentAccount?.did
|
||||
? 'you'
|
||||
: sanitizeHandle(list.creator.handle, '@')}
|
||||
{list.purpose === 'app.bsky.graph.defs#curatelist' &&
|
||||
(list.creator.did === currentAccount?.did ? (
|
||||
<Trans>User list by you</Trans>
|
||||
) : (
|
||||
<Trans>
|
||||
User list by {sanitizeHandle(list.creator.handle, '@')}
|
||||
</Trans>
|
||||
))}
|
||||
{list.purpose === 'app.bsky.graph.defs#modlist' &&
|
||||
(list.creator.did === currentAccount?.did ? (
|
||||
<Trans>Moderation list by you</Trans>
|
||||
) : (
|
||||
<Trans>
|
||||
Moderation list by {sanitizeHandle(list.creator.handle, '@')}
|
||||
</Trans>
|
||||
))}
|
||||
</Text>
|
||||
</View>
|
||||
<View>
|
||||
|
|
|
@ -75,7 +75,7 @@ export function Component({showReminder}: {showReminder?: boolean}) {
|
|||
token: confirmationCode.trim(),
|
||||
})
|
||||
updateCurrentAccount({emailConfirmed: true})
|
||||
Toast.show('Email verified')
|
||||
Toast.show(_(msg`Email verified`))
|
||||
closeModal()
|
||||
} catch (e) {
|
||||
setError(cleanError(String(e)))
|
||||
|
@ -97,9 +97,15 @@ export function Component({showReminder}: {showReminder?: boolean}) {
|
|||
{stage === Stages.Reminder && <ReminderIllustration />}
|
||||
<View style={styles.titleSection}>
|
||||
<Text type="title-lg" style={[pal.text, styles.title]}>
|
||||
{stage === Stages.Reminder ? 'Please Verify Your Email' : ''}
|
||||
{stage === Stages.ConfirmCode ? 'Enter Confirmation Code' : ''}
|
||||
{stage === Stages.Email ? 'Verify Your Email' : ''}
|
||||
{stage === Stages.Reminder ? (
|
||||
<Trans>Please Verify Your Email</Trans>
|
||||
) : stage === Stages.Email ? (
|
||||
<Trans>Verify Your Email</Trans>
|
||||
) : stage === Stages.ConfirmCode ? (
|
||||
<Trans>Enter Confirmation Code</Trans>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
|
@ -133,7 +139,7 @@ export function Component({showReminder}: {showReminder?: boolean}) {
|
|||
size={16}
|
||||
/>
|
||||
<Text type="xl-medium" style={[pal.text, s.flex1, {minWidth: 0}]}>
|
||||
{currentAccount?.email || '(no email)'}
|
||||
{currentAccount?.email || _(msg`(no email)`)}
|
||||
</Text>
|
||||
</View>
|
||||
<Pressable
|
||||
|
@ -182,7 +188,7 @@ export function Component({showReminder}: {showReminder?: boolean}) {
|
|||
onPress={() => setStage(Stages.Email)}
|
||||
accessibilityLabel={_(msg`Get Started`)}
|
||||
accessibilityHint=""
|
||||
label="Get Started"
|
||||
label={_(msg`Get Started`)}
|
||||
labelContainerStyle={{justifyContent: 'center', padding: 4}}
|
||||
labelStyle={[s.f18]}
|
||||
/>
|
||||
|
@ -195,7 +201,7 @@ export function Component({showReminder}: {showReminder?: boolean}) {
|
|||
onPress={onSendEmail}
|
||||
accessibilityLabel={_(msg`Send Confirmation Email`)}
|
||||
accessibilityHint=""
|
||||
label="Send Confirmation Email"
|
||||
label={_(msg`Send Confirmation Email`)}
|
||||
labelContainerStyle={{
|
||||
justifyContent: 'center',
|
||||
padding: 4,
|
||||
|
@ -207,7 +213,7 @@ export function Component({showReminder}: {showReminder?: boolean}) {
|
|||
type="default"
|
||||
accessibilityLabel={_(msg`I have a code`)}
|
||||
accessibilityHint=""
|
||||
label="I have a confirmation code"
|
||||
label={_(msg`I have a confirmation code`)}
|
||||
labelContainerStyle={{
|
||||
justifyContent: 'center',
|
||||
padding: 4,
|
||||
|
@ -224,7 +230,7 @@ export function Component({showReminder}: {showReminder?: boolean}) {
|
|||
onPress={onConfirm}
|
||||
accessibilityLabel={_(msg`Confirm`)}
|
||||
accessibilityHint=""
|
||||
label="Confirm"
|
||||
label={_(msg`Confirm`)}
|
||||
labelContainerStyle={{justifyContent: 'center', padding: 4}}
|
||||
labelStyle={[s.f18]}
|
||||
/>
|
||||
|
@ -236,10 +242,16 @@ export function Component({showReminder}: {showReminder?: boolean}) {
|
|||
closeModal()
|
||||
}}
|
||||
accessibilityLabel={
|
||||
stage === Stages.Reminder ? 'Not right now' : 'Cancel'
|
||||
stage === Stages.Reminder
|
||||
? _(msg`Not right now`)
|
||||
: _(msg`Cancel`)
|
||||
}
|
||||
accessibilityHint=""
|
||||
label={stage === Stages.Reminder ? 'Not right now' : 'Cancel'}
|
||||
label={
|
||||
stage === Stages.Reminder
|
||||
? _(msg`Not right now`)
|
||||
: _(msg`Cancel`)
|
||||
}
|
||||
labelContainerStyle={{justifyContent: 'center', padding: 4}}
|
||||
labelStyle={[s.f18]}
|
||||
/>
|
||||
|
|
|
@ -48,7 +48,7 @@ export function Component({}: {}) {
|
|||
} else {
|
||||
setError(
|
||||
resBody.error ||
|
||||
'Something went wrong. Check your email and try again.',
|
||||
_(msg`Something went wrong. Check your email and try again.`),
|
||||
)
|
||||
}
|
||||
} catch (e: any) {
|
||||
|
@ -75,7 +75,7 @@ export function Component({}: {}) {
|
|||
</Text>
|
||||
<TextInput
|
||||
style={[styles.textInput, pal.borderDark, pal.text, s.mb10, s.mt10]}
|
||||
placeholder="Enter your email"
|
||||
placeholder={_(msg`Enter your email`)}
|
||||
placeholderTextColor={pal.textLight.color}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
|
@ -86,7 +86,9 @@ export function Component({}: {}) {
|
|||
enterKeyHint="done"
|
||||
accessible={true}
|
||||
accessibilityLabel={_(msg`Email`)}
|
||||
accessibilityHint="Input your email to get on the Bluesky waitlist"
|
||||
accessibilityHint={_(
|
||||
msg`Input your email to get on the Bluesky waitlist`,
|
||||
)}
|
||||
/>
|
||||
{error ? (
|
||||
<View style={s.mt10}>
|
||||
|
@ -114,7 +116,9 @@ export function Component({}: {}) {
|
|||
<TouchableOpacity
|
||||
onPress={onPressSignup}
|
||||
accessibilityRole="button"
|
||||
accessibilityHint={`Confirms signing up ${email} to the waitlist`}>
|
||||
accessibilityHint={_(
|
||||
msg`Confirms signing up ${email} to the waitlist`,
|
||||
)}>
|
||||
<LinearGradient
|
||||
colors={[gradients.blueLight.start, gradients.blueLight.end]}
|
||||
start={{x: 0, y: 0}}
|
||||
|
@ -130,7 +134,9 @@ export function Component({}: {}) {
|
|||
onPress={onCancel}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Cancel waitlist signup`)}
|
||||
accessibilityHint={`Exits signing up for waitlist with ${email}`}
|
||||
accessibilityHint={_(
|
||||
msg`Exits signing up for waitlist with ${email}`,
|
||||
)}
|
||||
onAccessibilityEscape={onCancel}>
|
||||
<Text type="button-lg" style={pal.textLight}>
|
||||
<Trans>Cancel</Trans>
|
||||
|
|
|
@ -13,6 +13,8 @@ import {logger} from '#/logger'
|
|||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {useModerationOpts} from '#/state/queries/preferences'
|
||||
import {List, ListRef} from '../util/List'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
|
||||
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
|
||||
const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'}
|
||||
|
@ -31,6 +33,7 @@ export function Feed({
|
|||
}) {
|
||||
const [isPTRing, setIsPTRing] = React.useState(false)
|
||||
|
||||
const {_} = useLingui()
|
||||
const moderationOpts = useModerationOpts()
|
||||
const {checkUnread} = useUnreadNotificationsApi()
|
||||
const {
|
||||
|
@ -101,14 +104,16 @@ export function Feed({
|
|||
return (
|
||||
<EmptyState
|
||||
icon="bell"
|
||||
message="No notifications yet!"
|
||||
message={_(msg`No notifications yet!`)}
|
||||
style={styles.emptyState}
|
||||
/>
|
||||
)
|
||||
} else if (item === LOAD_MORE_ERROR_ITEM) {
|
||||
return (
|
||||
<LoadMoreRetryBtn
|
||||
label="There was an issue fetching notifications. Tap here to try again."
|
||||
label={_(
|
||||
msg`There was an issue fetching notifications. Tap here to try again.`,
|
||||
)}
|
||||
onPress={onPressRetryLoadMore}
|
||||
/>
|
||||
)
|
||||
|
@ -117,7 +122,7 @@ export function Feed({
|
|||
}
|
||||
return <FeedItem item={item} moderationOpts={moderationOpts!} />
|
||||
},
|
||||
[onPressRetryLoadMore, moderationOpts],
|
||||
[onPressRetryLoadMore, moderationOpts, _],
|
||||
)
|
||||
|
||||
const FeedFooter = React.useCallback(
|
||||
|
|
|
@ -65,6 +65,7 @@ let FeedItem = ({
|
|||
moderationOpts: ModerationOpts
|
||||
}): React.ReactNode => {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const [isAuthorsExpanded, setAuthorsExpanded] = useState<boolean>(false)
|
||||
const itemHref = useMemo(() => {
|
||||
if (item.type === 'post-like' || item.type === 'repost') {
|
||||
|
@ -151,24 +152,26 @@ let FeedItem = ({
|
|||
let icon: Props['icon'] | 'HeartIconSolid'
|
||||
let iconStyle: Props['style'] = []
|
||||
if (item.type === 'post-like') {
|
||||
action = 'liked your post'
|
||||
action = _(msg`liked your post`)
|
||||
icon = 'HeartIconSolid'
|
||||
iconStyle = [
|
||||
s.likeColor as FontAwesomeIconStyle,
|
||||
{position: 'relative', top: -4},
|
||||
]
|
||||
} else if (item.type === 'repost') {
|
||||
action = 'reposted your post'
|
||||
action = _(msg`reposted your post`)
|
||||
icon = 'retweet'
|
||||
iconStyle = [s.green3 as FontAwesomeIconStyle]
|
||||
} else if (item.type === 'follow') {
|
||||
action = 'followed you'
|
||||
action = _(msg`followed you`)
|
||||
icon = 'user-plus'
|
||||
iconStyle = [s.blue3 as FontAwesomeIconStyle]
|
||||
} else if (item.type === 'feedgen-like') {
|
||||
action = `liked your custom feed${
|
||||
item.subjectUri ? ` '${new AtUri(item.subjectUri).rkey}'` : ''
|
||||
}`
|
||||
action = _(
|
||||
msg`liked your custom feed${
|
||||
item.subjectUri ? ` '${new AtUri(item.subjectUri).rkey}'` : ''
|
||||
}`,
|
||||
)
|
||||
icon = 'HeartIconSolid'
|
||||
iconStyle = [
|
||||
s.likeColor as FontAwesomeIconStyle,
|
||||
|
@ -314,14 +317,16 @@ function CondensedAuthorsList({
|
|||
onPress={onToggleAuthorsExpanded}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Hide user list`)}
|
||||
accessibilityHint="Collapses list of users for a given notification">
|
||||
accessibilityHint={_(
|
||||
msg`Collapses list of users for a given notification`,
|
||||
)}>
|
||||
<FontAwesomeIcon
|
||||
icon="angle-up"
|
||||
size={18}
|
||||
style={[styles.expandedAuthorsCloseBtnIcon, pal.text]}
|
||||
/>
|
||||
<Text type="sm-medium" style={pal.text}>
|
||||
<Trans>Hide</Trans>
|
||||
<Trans context="action">Hide</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
@ -343,7 +348,9 @@ function CondensedAuthorsList({
|
|||
return (
|
||||
<TouchableOpacity
|
||||
accessibilityLabel={_(msg`Show users`)}
|
||||
accessibilityHint="Opens an expanded list of users in this notification"
|
||||
accessibilityHint={_(
|
||||
msg`Opens an expanded list of users in this notification`,
|
||||
)}
|
||||
onPress={onToggleAuthorsExpanded}>
|
||||
<View style={styles.avis}>
|
||||
{authors.slice(0, MAX_AUTHORS).map(author => (
|
||||
|
|
|
@ -74,7 +74,9 @@ export function FeedsTabBar(
|
|||
onPress={onPressAvi}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Open navigation`)}
|
||||
accessibilityHint="Access profile and other navigation links"
|
||||
accessibilityHint={_(
|
||||
msg`Access profile and other navigation links`,
|
||||
)}
|
||||
hitSlop={HITSLOP_10}>
|
||||
<FontAwesomeIcon
|
||||
icon="bars"
|
||||
|
|
|
@ -222,7 +222,11 @@ function PostThreadLoaded({
|
|||
const renderItem = React.useCallback(
|
||||
({item, index}: {item: YieldedItem; index: number}) => {
|
||||
if (item === TOP_COMPONENT) {
|
||||
return isTablet ? <ViewHeader title={_(msg`Post`)} /> : null
|
||||
return isTablet ? (
|
||||
<ViewHeader
|
||||
title={_(msg({message: `Post`, context: 'description'}))}
|
||||
/>
|
||||
) : null
|
||||
} else if (item === PARENT_SPINNER) {
|
||||
return (
|
||||
<View style={styles.parentSpinner}>
|
||||
|
@ -393,7 +397,7 @@ function PostThreadBlocked() {
|
|||
style={[pal.link as FontAwesomeIconStyle, s.mr5]}
|
||||
size={14}
|
||||
/>
|
||||
Back
|
||||
<Trans context="action">Back</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
|
|
@ -158,6 +158,7 @@ let PostThreadItemLoaded = ({
|
|||
onPostReply: () => void
|
||||
}): React.ReactNode => {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const langPrefs = useLanguagePrefs()
|
||||
const {openComposer} = useComposerControls()
|
||||
const {currentAccount} = useSession()
|
||||
|
@ -172,7 +173,7 @@ let PostThreadItemLoaded = ({
|
|||
const urip = new AtUri(post.uri)
|
||||
return makeProfileLink(post.author, 'post', urip.rkey)
|
||||
}, [post.uri, post.author])
|
||||
const itemTitle = `Post by ${post.author.handle}`
|
||||
const itemTitle = _(msg`Post by ${post.author.handle}`)
|
||||
const authorHref = makeProfileLink(post.author)
|
||||
const authorTitle = post.author.handle
|
||||
const isAuthorMuted = post.author.viewer?.muted
|
||||
|
@ -180,12 +181,12 @@ let PostThreadItemLoaded = ({
|
|||
const urip = new AtUri(post.uri)
|
||||
return makeProfileLink(post.author, 'post', urip.rkey, 'liked-by')
|
||||
}, [post.uri, post.author])
|
||||
const likesTitle = 'Likes on this post'
|
||||
const likesTitle = _(msg`Likes on this post`)
|
||||
const repostsHref = React.useMemo(() => {
|
||||
const urip = new AtUri(post.uri)
|
||||
return makeProfileLink(post.author, 'post', urip.rkey, 'reposted-by')
|
||||
}, [post.uri, post.author])
|
||||
const repostsTitle = 'Reposts of this post'
|
||||
const repostsTitle = _(msg`Reposts of this post`)
|
||||
const isModeratedPost =
|
||||
moderation.decisions.post.cause?.type === 'label' &&
|
||||
moderation.decisions.post.cause.label.src !== currentAccount?.did
|
||||
|
@ -225,7 +226,7 @@ let PostThreadItemLoaded = ({
|
|||
}, [setLimitLines])
|
||||
|
||||
if (!record) {
|
||||
return <ErrorMessage message="Invalid or unsupported post record" />
|
||||
return <ErrorMessage message={_(msg`Invalid or unsupported post record`)} />
|
||||
}
|
||||
|
||||
if (isHighlightedPost) {
|
||||
|
@ -563,7 +564,7 @@ let PostThreadItemLoaded = ({
|
|||
) : undefined}
|
||||
{limitLines ? (
|
||||
<TextLink
|
||||
text="Show More"
|
||||
text={_(msg`Show More`)}
|
||||
style={pal.link}
|
||||
onPress={onPressShowMore}
|
||||
href="#"
|
||||
|
|
|
@ -27,6 +27,8 @@ import {countLines} from 'lib/strings/helpers'
|
|||
import {useModerationOpts} from '#/state/queries/preferences'
|
||||
import {useComposerControls} from '#/state/shell/composer'
|
||||
import {Shadow, usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
export function Post({
|
||||
post,
|
||||
|
@ -95,6 +97,7 @@ function PostInner({
|
|||
style?: StyleProp<ViewStyle>
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {openComposer} = useComposerControls()
|
||||
const [limitLines, setLimitLines] = useState(
|
||||
() => countLines(richText?.text) >= MAX_POST_LINES,
|
||||
|
@ -159,13 +162,15 @@ function PostInner({
|
|||
style={[pal.textLight, s.mr2]}
|
||||
lineHeight={1.2}
|
||||
numberOfLines={1}>
|
||||
Reply to{' '}
|
||||
<UserInfoText
|
||||
type="sm"
|
||||
did={replyAuthorDid}
|
||||
attr="displayName"
|
||||
style={[pal.textLight]}
|
||||
/>
|
||||
<Trans context="description">
|
||||
Reply to{' '}
|
||||
<UserInfoText
|
||||
type="sm"
|
||||
did={replyAuthorDid}
|
||||
attr="displayName"
|
||||
style={[pal.textLight]}
|
||||
/>
|
||||
</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
|
@ -188,7 +193,7 @@ function PostInner({
|
|||
) : undefined}
|
||||
{limitLines ? (
|
||||
<TextLink
|
||||
text="Show More"
|
||||
text={_(msg`Show More`)}
|
||||
style={pal.link}
|
||||
onPress={onPressShowMore}
|
||||
href="#"
|
||||
|
|
|
@ -12,6 +12,7 @@ import {NavigationProp} from 'lib/routes/types'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {s} from 'lib/styles'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {Trans} from '@lingui/macro'
|
||||
|
||||
export function CustomFeedEmptyState() {
|
||||
const pal = usePalette('default')
|
||||
|
@ -33,15 +34,17 @@ export function CustomFeedEmptyState() {
|
|||
<MagnifyingGlassIcon style={[styles.emptyIcon, pal.text]} size={62} />
|
||||
</View>
|
||||
<Text type="xl-medium" style={[s.textCenter, pal.text]}>
|
||||
This feed is empty! You may need to follow more users or tune your
|
||||
language settings.
|
||||
<Trans>
|
||||
This feed is empty! You may need to follow more users or tune your
|
||||
language settings.
|
||||
</Trans>
|
||||
</Text>
|
||||
<Button
|
||||
type="inverted"
|
||||
style={styles.emptyBtn}
|
||||
onPress={onPressFindAccounts}>
|
||||
<Text type="lg-medium" style={palInverted.text}>
|
||||
Find accounts to follow
|
||||
<Trans>Find accounts to follow</Trans>
|
||||
</Text>
|
||||
<FontAwesomeIcon
|
||||
icon="angle-right"
|
||||
|
|
|
@ -28,6 +28,8 @@ import {isWeb} from '#/platform/detection'
|
|||
import {listenPostCreated} from '#/state/events'
|
||||
import {useSession} from '#/state/session'
|
||||
import {STALE} from '#/state/queries'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
const LOADING_ITEM = {_reactKey: '__loading__'}
|
||||
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
|
||||
|
@ -74,6 +76,7 @@ let Feed = ({
|
|||
}): React.ReactNode => {
|
||||
const theme = useTheme()
|
||||
const {track} = useAnalytics()
|
||||
const {_} = useLingui()
|
||||
const queryClient = useQueryClient()
|
||||
const {currentAccount} = useSession()
|
||||
const [isPTRing, setIsPTRing] = React.useState(false)
|
||||
|
@ -250,7 +253,9 @@ let Feed = ({
|
|||
} else if (item === LOAD_MORE_ERROR_ITEM) {
|
||||
return (
|
||||
<LoadMoreRetryBtn
|
||||
label="There was an issue fetching posts. Tap here to try again."
|
||||
label={_(
|
||||
msg`There was an issue fetching posts. Tap here to try again.`,
|
||||
)}
|
||||
onPress={onPressRetryLoadMore}
|
||||
/>
|
||||
)
|
||||
|
@ -259,7 +264,7 @@ let Feed = ({
|
|||
}
|
||||
return <FeedSlice slice={item} />
|
||||
},
|
||||
[feed, error, onPressTryAgain, onPressRetryLoadMore, renderEmptyState],
|
||||
[feed, error, onPressTryAgain, onPressRetryLoadMore, renderEmptyState, _],
|
||||
)
|
||||
|
||||
const shouldRenderEndOfFeed =
|
||||
|
|
|
@ -38,6 +38,7 @@ export function FeedErrorMessage({
|
|||
error?: Error
|
||||
onPressTryAgain: () => void
|
||||
}) {
|
||||
const {_: _l} = useLingui()
|
||||
const knownError = React.useMemo(
|
||||
() => detectKnownError(feedDesc, error),
|
||||
[feedDesc, error],
|
||||
|
@ -60,7 +61,7 @@ export function FeedErrorMessage({
|
|||
return (
|
||||
<EmptyState
|
||||
icon="ban"
|
||||
message="Posts hidden"
|
||||
message={_l(msgLingui`Posts hidden`)}
|
||||
style={{paddingVertical: 40}}
|
||||
/>
|
||||
)
|
||||
|
@ -134,7 +135,9 @@ function FeedgenErrorMessage({
|
|||
await removeFeed({uri})
|
||||
} catch (err) {
|
||||
Toast.show(
|
||||
'There was an an issue removing this feed. Please check your internet connection and try again.',
|
||||
_l(
|
||||
msgLingui`There was an an issue removing this feed. Please check your internet connection and try again.`,
|
||||
),
|
||||
)
|
||||
logger.error('Failed to remove feed', {error: err})
|
||||
}
|
||||
|
@ -160,20 +163,20 @@ function FeedgenErrorMessage({
|
|||
{knownError === KnownError.FeedgenDoesNotExist && (
|
||||
<Button
|
||||
type="inverted"
|
||||
label="Remove feed"
|
||||
label={_l(msgLingui`Remove feed`)}
|
||||
onPress={onRemoveFeed}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
type="default-light"
|
||||
label="View profile"
|
||||
label={_l(msgLingui`View profile`)}
|
||||
onPress={onViewProfile}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
}
|
||||
}, [knownError, onViewProfile, onRemoveFeed])
|
||||
}, [knownError, onViewProfile, onRemoveFeed, _l])
|
||||
|
||||
return (
|
||||
<View
|
||||
|
@ -191,7 +194,7 @@ function FeedgenErrorMessage({
|
|||
|
||||
{rawError?.message && (
|
||||
<Text style={pal.textLight}>
|
||||
<Trans>Message from server</Trans>: {rawError.message}
|
||||
<Trans>Message from server: {rawError.message}</Trans>
|
||||
</Text>
|
||||
)}
|
||||
|
||||
|
|
|
@ -35,6 +35,8 @@ import {useComposerControls} from '#/state/shell/composer'
|
|||
import {Shadow, usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow'
|
||||
import {FeedNameText} from '../util/FeedInfoText'
|
||||
import {useSession} from '#/state/session'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
export function FeedItem({
|
||||
post,
|
||||
|
@ -103,6 +105,7 @@ let FeedItemInner = ({
|
|||
}): React.ReactNode => {
|
||||
const {openComposer} = useComposerControls()
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {currentAccount} = useSession()
|
||||
const href = useMemo(() => {
|
||||
const urip = new AtUri(post.uri)
|
||||
|
@ -182,24 +185,28 @@ let FeedItemInner = ({
|
|||
style={pal.textLight}
|
||||
lineHeight={1.2}
|
||||
numberOfLines={1}>
|
||||
From{' '}
|
||||
<FeedNameText
|
||||
type="sm-bold"
|
||||
uri={reason.uri}
|
||||
href={reason.href}
|
||||
lineHeight={1.2}
|
||||
numberOfLines={1}
|
||||
style={pal.textLight}
|
||||
/>
|
||||
<Trans context="from-feed">
|
||||
From{' '}
|
||||
<FeedNameText
|
||||
type="sm-bold"
|
||||
uri={reason.uri}
|
||||
href={reason.href}
|
||||
lineHeight={1.2}
|
||||
numberOfLines={1}
|
||||
style={pal.textLight}
|
||||
/>
|
||||
</Trans>
|
||||
</Text>
|
||||
</Link>
|
||||
) : AppBskyFeedDefs.isReasonRepost(reason) ? (
|
||||
<Link
|
||||
style={styles.includeReason}
|
||||
href={makeProfileLink(reason.by)}
|
||||
title={`Reposted by ${sanitizeDisplayName(
|
||||
reason.by.displayName || reason.by.handle,
|
||||
)}`}>
|
||||
title={_(
|
||||
msg`Reposted by ${sanitizeDisplayName(
|
||||
reason.by.displayName || reason.by.handle,
|
||||
)})`,
|
||||
)}>
|
||||
<FontAwesomeIcon
|
||||
icon="retweet"
|
||||
style={{
|
||||
|
@ -213,17 +220,19 @@ let FeedItemInner = ({
|
|||
style={pal.textLight}
|
||||
lineHeight={1.2}
|
||||
numberOfLines={1}>
|
||||
Reposted by{' '}
|
||||
<TextLinkOnWebOnly
|
||||
type="sm-bold"
|
||||
style={pal.textLight}
|
||||
lineHeight={1.2}
|
||||
numberOfLines={1}
|
||||
text={sanitizeDisplayName(
|
||||
reason.by.displayName || sanitizeHandle(reason.by.handle),
|
||||
)}
|
||||
href={makeProfileLink(reason.by)}
|
||||
/>
|
||||
<Trans>
|
||||
Reposted by{' '}
|
||||
<TextLinkOnWebOnly
|
||||
type="sm-bold"
|
||||
style={pal.textLight}
|
||||
lineHeight={1.2}
|
||||
numberOfLines={1}
|
||||
text={sanitizeDisplayName(
|
||||
reason.by.displayName || sanitizeHandle(reason.by.handle),
|
||||
)}
|
||||
href={makeProfileLink(reason.by)}
|
||||
/>
|
||||
</Trans>
|
||||
</Text>
|
||||
</Link>
|
||||
) : null}
|
||||
|
@ -274,13 +283,15 @@ let FeedItemInner = ({
|
|||
style={[pal.textLight, s.mr2]}
|
||||
lineHeight={1.2}
|
||||
numberOfLines={1}>
|
||||
Reply to{' '}
|
||||
<UserInfoText
|
||||
type="md"
|
||||
did={replyAuthorDid}
|
||||
attr="displayName"
|
||||
style={[pal.textLight, s.ml2]}
|
||||
/>
|
||||
<Trans context="description">
|
||||
Reply to{' '}
|
||||
<UserInfoText
|
||||
type="md"
|
||||
did={replyAuthorDid}
|
||||
attr="displayName"
|
||||
style={[pal.textLight, s.ml2]}
|
||||
/>
|
||||
</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
|
@ -317,6 +328,7 @@ let PostContent = ({
|
|||
postAuthor: AppBskyFeedDefs.PostView['author']
|
||||
}): React.ReactNode => {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const [limitLines, setLimitLines] = useState(
|
||||
() => countLines(richText.text) >= MAX_POST_LINES,
|
||||
)
|
||||
|
@ -346,7 +358,7 @@ let PostContent = ({
|
|||
) : undefined}
|
||||
{limitLines ? (
|
||||
<TextLink
|
||||
text="Show More"
|
||||
text={_(msg`Show More`)}
|
||||
style={pal.link}
|
||||
onPress={onPressShowMore}
|
||||
href="#"
|
||||
|
|
|
@ -8,6 +8,7 @@ import Svg, {Circle, Line} from 'react-native-svg'
|
|||
import {FeedItem} from './FeedItem'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {makeProfileLink} from 'lib/routes/links'
|
||||
import {Trans} from '@lingui/macro'
|
||||
|
||||
let FeedSlice = ({slice}: {slice: FeedPostSlice}): React.ReactNode => {
|
||||
if (slice.isThread && slice.items.length > 3) {
|
||||
|
@ -99,7 +100,7 @@ function ViewFullThread({slice}: {slice: FeedPostSlice}) {
|
|||
</View>
|
||||
|
||||
<Text type="md" style={[pal.link, {paddingTop: 18, paddingBottom: 4}]}>
|
||||
View full thread
|
||||
<Trans>View full thread</Trans>
|
||||
</Text>
|
||||
</Link>
|
||||
)
|
||||
|
|
|
@ -12,6 +12,7 @@ import {NavigationProp} from 'lib/routes/types'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {s} from 'lib/styles'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {Trans} from '@lingui/macro'
|
||||
|
||||
export function FollowingEmptyState() {
|
||||
const pal = usePalette('default')
|
||||
|
@ -43,15 +44,17 @@ export function FollowingEmptyState() {
|
|||
<MagnifyingGlassIcon style={[styles.icon, pal.text]} size={62} />
|
||||
</View>
|
||||
<Text type="xl-medium" style={[s.textCenter, pal.text]}>
|
||||
Your following feed is empty! Follow more users to see what's
|
||||
happening.
|
||||
<Trans>
|
||||
Your following feed is empty! Follow more users to see what's
|
||||
happening.
|
||||
</Trans>
|
||||
</Text>
|
||||
<Button
|
||||
type="inverted"
|
||||
style={styles.emptyBtn}
|
||||
onPress={onPressFindAccounts}>
|
||||
<Text type="lg-medium" style={palInverted.text}>
|
||||
Find accounts to follow
|
||||
<Trans>Find accounts to follow</Trans>
|
||||
</Text>
|
||||
<FontAwesomeIcon
|
||||
icon="angle-right"
|
||||
|
@ -61,14 +64,14 @@ export function FollowingEmptyState() {
|
|||
</Button>
|
||||
|
||||
<Text type="xl-medium" style={[s.textCenter, pal.text, s.mt20]}>
|
||||
You can also discover new Custom Feeds to follow.
|
||||
<Trans>You can also discover new Custom Feeds to follow.</Trans>
|
||||
</Text>
|
||||
<Button
|
||||
type="inverted"
|
||||
style={[styles.emptyBtn, s.mt10]}
|
||||
onPress={onPressDiscoverFeeds}>
|
||||
<Text type="lg-medium" style={palInverted.text}>
|
||||
Discover new custom feeds
|
||||
<Trans>Discover new custom feeds</Trans>
|
||||
</Text>
|
||||
<FontAwesomeIcon
|
||||
icon="angle-right"
|
||||
|
|
|
@ -11,6 +11,7 @@ import {NavigationProp} from 'lib/routes/types'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {s} from 'lib/styles'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {Trans} from '@lingui/macro'
|
||||
|
||||
export function FollowingEndOfFeed() {
|
||||
const pal = usePalette('default')
|
||||
|
@ -44,15 +45,17 @@ export function FollowingEndOfFeed() {
|
|||
]}>
|
||||
<View style={styles.inner}>
|
||||
<Text type="xl-medium" style={[s.textCenter, pal.text]}>
|
||||
You've reached the end of your feed! Find some more accounts to
|
||||
follow.
|
||||
<Trans>
|
||||
You've reached the end of your feed! Find some more accounts to
|
||||
follow.
|
||||
</Trans>
|
||||
</Text>
|
||||
<Button
|
||||
type="inverted"
|
||||
style={styles.emptyBtn}
|
||||
onPress={onPressFindAccounts}>
|
||||
<Text type="lg-medium" style={palInverted.text}>
|
||||
Find accounts to follow
|
||||
<Trans>Find accounts to follow</Trans>
|
||||
</Text>
|
||||
<FontAwesomeIcon
|
||||
icon="angle-right"
|
||||
|
@ -62,14 +65,14 @@ export function FollowingEndOfFeed() {
|
|||
</Button>
|
||||
|
||||
<Text type="xl-medium" style={[s.textCenter, pal.text, s.mt20]}>
|
||||
You can also discover new Custom Feeds to follow.
|
||||
<Trans>You can also discover new Custom Feeds to follow.</Trans>
|
||||
</Text>
|
||||
<Button
|
||||
type="inverted"
|
||||
style={[styles.emptyBtn, s.mt10]}
|
||||
onPress={onPressDiscoverFeeds}>
|
||||
<Text type="lg-medium" style={palInverted.text}>
|
||||
Discover new custom feeds
|
||||
<Trans>Discover new custom feeds</Trans>
|
||||
</Text>
|
||||
<FontAwesomeIcon
|
||||
icon="angle-right"
|
||||
|
|
|
@ -5,6 +5,8 @@ import {Button, ButtonType} from '../util/forms/Button'
|
|||
import * as Toast from '../util/Toast'
|
||||
import {useProfileFollowMutationQueue} from '#/state/queries/profile'
|
||||
import {Shadow} from '#/state/cache/types'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
|
||||
export function FollowButton({
|
||||
unfollowedType = 'inverted',
|
||||
|
@ -18,13 +20,14 @@ export function FollowButton({
|
|||
labelStyle?: StyleProp<TextStyle>
|
||||
}) {
|
||||
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(profile)
|
||||
const {_} = useLingui()
|
||||
|
||||
const onPressFollow = async () => {
|
||||
try {
|
||||
await queueFollow()
|
||||
} catch (e: any) {
|
||||
if (e?.name !== 'AbortError') {
|
||||
Toast.show(`An issue occurred, please try again.`)
|
||||
Toast.show(_(msg`An issue occurred, please try again.`))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +37,7 @@ export function FollowButton({
|
|||
await queueUnfollow()
|
||||
} catch (e: any) {
|
||||
if (e?.name !== 'AbortError') {
|
||||
Toast.show(`An issue occurred, please try again.`)
|
||||
Toast.show(_(msg`An issue occurred, please try again.`))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +52,7 @@ export function FollowButton({
|
|||
type={followedType}
|
||||
labelStyle={labelStyle}
|
||||
onPress={onPressUnfollow}
|
||||
label="Unfollow"
|
||||
label={_(msg({message: 'Unfollow', context: 'action'}))}
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
|
@ -58,7 +61,7 @@ export function FollowButton({
|
|||
type={unfollowedType}
|
||||
labelStyle={labelStyle}
|
||||
onPress={onPressFollow}
|
||||
label="Follow"
|
||||
label={_(msg({message: 'Follow', context: 'action'}))}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import {Shadow} from '#/state/cache/types'
|
|||
import {useModerationOpts} from '#/state/queries/preferences'
|
||||
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
||||
import {useSession} from '#/state/session'
|
||||
import {Trans} from '@lingui/macro'
|
||||
|
||||
export function ProfileCard({
|
||||
testID,
|
||||
|
@ -137,7 +138,7 @@ function ProfileCardPills({
|
|||
{followedBy && (
|
||||
<View style={[s.mt5, pal.btn, styles.pill]}>
|
||||
<Text type="xs" style={pal.text}>
|
||||
Follows You
|
||||
<Trans>Follows You</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
|
@ -190,8 +191,10 @@ function FollowersList({
|
|||
style={[styles.followsByDesc, pal.textLight]}
|
||||
numberOfLines={2}
|
||||
lineHeight={1.2}>
|
||||
Followed by{' '}
|
||||
{followersWithMods.map(({f}) => f.displayName || f.handle).join(', ')}
|
||||
<Trans>
|
||||
Followed by{' '}
|
||||
{followersWithMods.map(({f}) => f.displayName || f.handle).join(', ')}
|
||||
</Trans>
|
||||
</Text>
|
||||
{followersWithMods.slice(0, 3).map(({f, mod}) => (
|
||||
<View key={f.did} style={styles.followedByAviContainer}>
|
||||
|
|
|
@ -192,14 +192,16 @@ let ProfileHeaderLoaded = ({
|
|||
track('ProfileHeader:FollowButtonClicked')
|
||||
await queueFollow()
|
||||
Toast.show(
|
||||
`Following ${sanitizeDisplayName(
|
||||
profile.displayName || profile.handle,
|
||||
)}`,
|
||||
_(
|
||||
msg`Following ${sanitizeDisplayName(
|
||||
profile.displayName || profile.handle,
|
||||
)}`,
|
||||
),
|
||||
)
|
||||
} catch (e: any) {
|
||||
if (e?.name !== 'AbortError') {
|
||||
logger.error('Failed to follow', {error: String(e)})
|
||||
Toast.show(`There was an issue! ${e.toString()}`)
|
||||
Toast.show(_(msg`There was an issue! ${e.toString()}`))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -211,14 +213,16 @@ let ProfileHeaderLoaded = ({
|
|||
track('ProfileHeader:UnfollowButtonClicked')
|
||||
await queueUnfollow()
|
||||
Toast.show(
|
||||
`No longer following ${sanitizeDisplayName(
|
||||
profile.displayName || profile.handle,
|
||||
)}`,
|
||||
_(
|
||||
msg`No longer following ${sanitizeDisplayName(
|
||||
profile.displayName || profile.handle,
|
||||
)}`,
|
||||
),
|
||||
)
|
||||
} catch (e: any) {
|
||||
if (e?.name !== 'AbortError') {
|
||||
logger.error('Failed to unfollow', {error: String(e)})
|
||||
Toast.show(`There was an issue! ${e.toString()}`)
|
||||
Toast.show(_(msg`There was an issue! ${e.toString()}`))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -253,27 +257,27 @@ let ProfileHeaderLoaded = ({
|
|||
track('ProfileHeader:MuteAccountButtonClicked')
|
||||
try {
|
||||
await queueMute()
|
||||
Toast.show('Account muted')
|
||||
Toast.show(_(msg`Account muted`))
|
||||
} catch (e: any) {
|
||||
if (e?.name !== 'AbortError') {
|
||||
logger.error('Failed to mute account', {error: e})
|
||||
Toast.show(`There was an issue! ${e.toString()}`)
|
||||
Toast.show(_(msg`There was an issue! ${e.toString()}`))
|
||||
}
|
||||
}
|
||||
}, [track, queueMute])
|
||||
}, [track, queueMute, _])
|
||||
|
||||
const onPressUnmuteAccount = React.useCallback(async () => {
|
||||
track('ProfileHeader:UnmuteAccountButtonClicked')
|
||||
try {
|
||||
await queueUnmute()
|
||||
Toast.show('Account unmuted')
|
||||
Toast.show(_(msg`Account unmuted`))
|
||||
} catch (e: any) {
|
||||
if (e?.name !== 'AbortError') {
|
||||
logger.error('Failed to unmute account', {error: e})
|
||||
Toast.show(`There was an issue! ${e.toString()}`)
|
||||
Toast.show(_(msg`There was an issue! ${e.toString()}`))
|
||||
}
|
||||
}
|
||||
}, [track, queueUnmute])
|
||||
}, [track, queueUnmute, _])
|
||||
|
||||
const onPressBlockAccount = React.useCallback(async () => {
|
||||
track('ProfileHeader:BlockAccountButtonClicked')
|
||||
|
@ -286,11 +290,11 @@ let ProfileHeaderLoaded = ({
|
|||
onPressConfirm: async () => {
|
||||
try {
|
||||
await queueBlock()
|
||||
Toast.show('Account blocked')
|
||||
Toast.show(_(msg`Account blocked`))
|
||||
} catch (e: any) {
|
||||
if (e?.name !== 'AbortError') {
|
||||
logger.error('Failed to block account', {error: e})
|
||||
Toast.show(`There was an issue! ${e.toString()}`)
|
||||
Toast.show(_(msg`There was an issue! ${e.toString()}`))
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -308,11 +312,11 @@ let ProfileHeaderLoaded = ({
|
|||
onPressConfirm: async () => {
|
||||
try {
|
||||
await queueUnblock()
|
||||
Toast.show('Account unblocked')
|
||||
Toast.show(_(msg`Account unblocked`))
|
||||
} catch (e: any) {
|
||||
if (e?.name !== 'AbortError') {
|
||||
logger.error('Failed to unblock account', {error: e})
|
||||
Toast.show(`There was an issue! ${e.toString()}`)
|
||||
Toast.show(_(msg`There was an issue! ${e.toString()}`))
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -451,7 +455,9 @@ let ProfileHeaderLoaded = ({
|
|||
style={[styles.btn, styles.mainBtn, pal.btn]}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Edit profile`)}
|
||||
accessibilityHint="Opens editor for profile display name, avatar, background image, and description">
|
||||
accessibilityHint={_(
|
||||
msg`Opens editor for profile display name, avatar, background image, and description`,
|
||||
)}>
|
||||
<Text type="button" style={pal.text}>
|
||||
<Trans>Edit Profile</Trans>
|
||||
</Text>
|
||||
|
@ -466,7 +472,7 @@ let ProfileHeaderLoaded = ({
|
|||
accessibilityLabel={_(msg`Unblock`)}
|
||||
accessibilityHint="">
|
||||
<Text type="button" style={[pal.text, s.bold]}>
|
||||
<Trans>Unblock</Trans>
|
||||
<Trans context="action">Unblock</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
)
|
||||
|
@ -488,8 +494,12 @@ let ProfileHeaderLoaded = ({
|
|||
},
|
||||
]}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={`Show follows similar to ${profile.handle}`}
|
||||
accessibilityHint={`Shows a list of users similar to this user.`}>
|
||||
accessibilityLabel={_(
|
||||
msg`Show follows similar to ${profile.handle}`,
|
||||
)}
|
||||
accessibilityHint={_(
|
||||
msg`Shows a list of users similar to this user.`,
|
||||
)}>
|
||||
<FontAwesomeIcon
|
||||
icon="user-plus"
|
||||
style={[
|
||||
|
@ -511,8 +521,10 @@ let ProfileHeaderLoaded = ({
|
|||
onPress={onPressUnfollow}
|
||||
style={[styles.btn, styles.mainBtn, pal.btn]}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={`Unfollow ${profile.handle}`}
|
||||
accessibilityHint={`Hides posts from ${profile.handle} in your feed`}>
|
||||
accessibilityLabel={_(msg`Unfollow ${profile.handle}`)}
|
||||
accessibilityHint={_(
|
||||
msg`Hides posts from ${profile.handle} in your feed`,
|
||||
)}>
|
||||
<FontAwesomeIcon
|
||||
icon="check"
|
||||
style={[pal.text, s.mr5]}
|
||||
|
@ -528,8 +540,10 @@ let ProfileHeaderLoaded = ({
|
|||
onPress={onPressFollow}
|
||||
style={[styles.btn, styles.mainBtn, palInverted.view]}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={`Follow ${profile.handle}`}
|
||||
accessibilityHint={`Shows posts from ${profile.handle} in your feed`}>
|
||||
accessibilityLabel={_(msg`Follow ${profile.handle}`)}
|
||||
accessibilityHint={_(
|
||||
msg`Shows posts from ${profile.handle} in your feed`,
|
||||
)}>
|
||||
<FontAwesomeIcon
|
||||
icon="plus"
|
||||
style={[palInverted.text, s.mr5]}
|
||||
|
@ -580,7 +594,7 @@ let ProfileHeaderLoaded = ({
|
|||
invalidHandle ? styles.invalidHandle : undefined,
|
||||
styles.handle,
|
||||
]}>
|
||||
{invalidHandle ? '⚠Invalid Handle' : `@${profile.handle}`}
|
||||
{invalidHandle ? _(msg`⚠Invalid Handle`) : `@${profile.handle}`}
|
||||
</ThemedText>
|
||||
</View>
|
||||
{!blockHide && (
|
||||
|
@ -597,7 +611,7 @@ let ProfileHeaderLoaded = ({
|
|||
}
|
||||
asAnchor
|
||||
accessibilityLabel={`${followers} ${pluralizedFollowers}`}
|
||||
accessibilityHint={'Opens followers list'}>
|
||||
accessibilityHint={_(msg`Opens followers list`)}>
|
||||
<Text type="md" style={[s.bold, pal.text]}>
|
||||
{followers}{' '}
|
||||
</Text>
|
||||
|
@ -615,14 +629,16 @@ let ProfileHeaderLoaded = ({
|
|||
})
|
||||
}
|
||||
asAnchor
|
||||
accessibilityLabel={`${following} following`}
|
||||
accessibilityHint={'Opens following list'}>
|
||||
<Text type="md" style={[s.bold, pal.text]}>
|
||||
{following}{' '}
|
||||
</Text>
|
||||
<Text type="md" style={[pal.textLight]}>
|
||||
<Trans>following</Trans>
|
||||
</Text>
|
||||
accessibilityLabel={_(msg`${following} following`)}
|
||||
accessibilityHint={_(msg`Opens following list`)}>
|
||||
<Trans>
|
||||
<Text type="md" style={[s.bold, pal.text]}>
|
||||
{following}{' '}
|
||||
</Text>
|
||||
<Text type="md" style={[pal.textLight]}>
|
||||
following
|
||||
</Text>
|
||||
</Trans>
|
||||
</Link>
|
||||
<Text type="md" style={[s.bold, pal.text]}>
|
||||
{formatCount(profile.postsCount || 0)}{' '}
|
||||
|
@ -682,7 +698,7 @@ let ProfileHeaderLoaded = ({
|
|||
testID="profileHeaderAviButton"
|
||||
onPress={onPressAvi}
|
||||
accessibilityRole="image"
|
||||
accessibilityLabel={`View ${profile.handle}'s avatar`}
|
||||
accessibilityLabel={_(msg`View ${profile.handle}'s avatar`)}
|
||||
accessibilityHint="">
|
||||
<View
|
||||
style={[pal.view, {borderColor: pal.colors.background}, styles.avi]}>
|
||||
|
|
|
@ -21,6 +21,7 @@ import {useModerationOpts} from '#/state/queries/preferences'
|
|||
import {useSuggestedFollowsByActorQuery} from '#/state/queries/suggested-follows'
|
||||
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
||||
import {useProfileFollowMutationQueue} from '#/state/queries/profile'
|
||||
import {Trans} from '@lingui/macro'
|
||||
|
||||
const OUTER_PADDING = 10
|
||||
const INNER_PADDING = 14
|
||||
|
@ -60,7 +61,7 @@ export function ProfileHeaderSuggestedFollows({
|
|||
paddingRight: INNER_PADDING / 2,
|
||||
}}>
|
||||
<Text type="sm-bold" style={[pal.textLight]}>
|
||||
Suggested for you
|
||||
<Trans>Suggested for you</Trans>
|
||||
</Text>
|
||||
|
||||
<Pressable
|
||||
|
|
|
@ -16,7 +16,7 @@ import {BACK_HITSLOP} from 'lib/constants'
|
|||
import {isNative} from 'platform/detection'
|
||||
import {useLightboxControls, ImagesLightbox} from '#/state/lightbox'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useSetDrawerOpen} from '#/state/shell'
|
||||
import {emitSoftReset} from '#/state/events'
|
||||
|
||||
|
@ -153,17 +153,19 @@ export function ProfileSubpageHeader({
|
|||
<LoadingPlaceholder width={50} height={8} />
|
||||
) : (
|
||||
<Text type="xl" style={[pal.textLight]} numberOfLines={1}>
|
||||
by{' '}
|
||||
{!creator ? (
|
||||
'—'
|
||||
<Trans>by —</Trans>
|
||||
) : isOwner ? (
|
||||
'you'
|
||||
<Trans>by you</Trans>
|
||||
) : (
|
||||
<TextLink
|
||||
text={sanitizeHandle(creator.handle, '@')}
|
||||
href={makeProfileLink(creator)}
|
||||
style={pal.textLight}
|
||||
/>
|
||||
<Trans>
|
||||
by{' '}
|
||||
<TextLink
|
||||
text={sanitizeHandle(creator.handle, '@')}
|
||||
href={makeProfileLink(creator)}
|
||||
style={pal.textLight}
|
||||
/>
|
||||
</Trans>
|
||||
)}
|
||||
</Text>
|
||||
)}
|
||||
|
|
|
@ -22,7 +22,7 @@ export function AccountDropdownBtn({account}: {account: SessionAccount}) {
|
|||
label: _(msg`Remove account`),
|
||||
onPress: () => {
|
||||
removeAccount(account)
|
||||
Toast.show('Account removed from quick access')
|
||||
Toast.show(_(msg`Account removed from quick access`))
|
||||
},
|
||||
icon: {
|
||||
ios: {
|
||||
|
|
|
@ -2,6 +2,8 @@ import React, {createRef, useState, useMemo, useRef} from 'react'
|
|||
import {Animated, Pressable, StyleSheet, View} from 'react-native'
|
||||
import {Text} from './text/Text'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
|
||||
interface Layout {
|
||||
x: number
|
||||
|
@ -19,6 +21,7 @@ export function Selector({
|
|||
panX: Animated.Value
|
||||
onSelect?: (index: number) => void
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
const containerRef = useRef<View>(null)
|
||||
const pal = usePalette('default')
|
||||
const [itemLayouts, setItemLayouts] = useState<undefined | Layout[]>(
|
||||
|
@ -100,8 +103,8 @@ export function Selector({
|
|||
testID={`selector-${i}`}
|
||||
key={item}
|
||||
onPress={() => onPressItem(i)}
|
||||
accessibilityLabel={`Select ${item}`}
|
||||
accessibilityHint={`Select option ${i} of ${numItems}`}>
|
||||
accessibilityLabel={_(msg`Select ${item}`)}
|
||||
accessibilityHint={_(msg`Select option ${i} of ${numItems}`)}>
|
||||
<View style={styles.item} ref={itemRefs[i]}>
|
||||
<Text
|
||||
style={
|
||||
|
|
|
@ -11,6 +11,8 @@ import {NavigationProp} from 'lib/routes/types'
|
|||
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
||||
import Animated from 'react-native-reanimated'
|
||||
import {useSetDrawerOpen} from '#/state/shell'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
const BACK_HITSLOP = {left: 20, top: 20, right: 50, bottom: 20}
|
||||
|
||||
|
@ -32,6 +34,7 @@ export function ViewHeader({
|
|||
renderButton?: () => JSX.Element
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const setDrawerOpen = useSetDrawerOpen()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const {track} = useAnalytics()
|
||||
|
@ -75,9 +78,9 @@ export function ViewHeader({
|
|||
hitSlop={BACK_HITSLOP}
|
||||
style={canGoBack ? styles.backBtn : styles.backBtnWide}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={canGoBack ? 'Back' : 'Menu'}
|
||||
accessibilityLabel={canGoBack ? _(msg`Back`) : _(msg`Menu`)}
|
||||
accessibilityHint={
|
||||
canGoBack ? '' : 'Access navigation links and settings'
|
||||
canGoBack ? '' : _(msg`Access navigation links and settings`)
|
||||
}>
|
||||
{canGoBack ? (
|
||||
<FontAwesomeIcon
|
||||
|
|
|
@ -53,7 +53,9 @@ export function ErrorMessage({
|
|||
onPress={onPressTryAgain}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Retry`)}
|
||||
accessibilityHint="Retries the last action, which errored out">
|
||||
accessibilityHint={_(
|
||||
msg`Retries the last action, which errored out`,
|
||||
)}>
|
||||
<FontAwesomeIcon
|
||||
icon="arrows-rotate"
|
||||
style={{color: theme.palette.error.icon}}
|
||||
|
|
|
@ -63,14 +63,16 @@ export function ErrorScreen({
|
|||
style={[styles.btn]}
|
||||
onPress={onPressTryAgain}
|
||||
accessibilityLabel={_(msg`Retry`)}
|
||||
accessibilityHint="Retries the last action, which errored out">
|
||||
accessibilityHint={_(
|
||||
msg`Retries the last action, which errored out`,
|
||||
)}>
|
||||
<FontAwesomeIcon
|
||||
icon="arrows-rotate"
|
||||
style={pal.link as FontAwesomeIconStyle}
|
||||
size={16}
|
||||
/>
|
||||
<Text type="button" style={[styles.btnText, pal.link]}>
|
||||
<Trans>Try again</Trans>
|
||||
<Trans context="action">Try again</Trans>
|
||||
</Text>
|
||||
</Button>
|
||||
</View>
|
||||
|
|
|
@ -75,6 +75,8 @@ export function DropdownButton({
|
|||
bottomOffset = 0,
|
||||
accessibilityLabel,
|
||||
}: PropsWithChildren<DropdownButtonProps>) {
|
||||
const {_} = useLingui()
|
||||
|
||||
const ref1 = useRef<TouchableOpacity>(null)
|
||||
const ref2 = useRef<View>(null)
|
||||
|
||||
|
@ -141,7 +143,9 @@ export function DropdownButton({
|
|||
hitSlop={HITSLOP_10}
|
||||
ref={ref1}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={accessibilityLabel || `Opens ${numItems} options`}
|
||||
accessibilityLabel={
|
||||
accessibilityLabel || _(msg`Opens ${numItems} options`)
|
||||
}
|
||||
accessibilityHint="">
|
||||
{children}
|
||||
</TouchableOpacity>
|
||||
|
@ -247,7 +251,7 @@ const DropdownItems = ({
|
|||
onPress={() => onPressItem(index)}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={item.label}
|
||||
accessibilityHint={`Option ${index + 1} of ${numItems}`}>
|
||||
accessibilityHint={_(msg`Option ${index + 1} of ${numItems}`)}>
|
||||
{item.icon && (
|
||||
<FontAwesomeIcon
|
||||
style={styles.icon}
|
||||
|
|
|
@ -71,32 +71,34 @@ let PostDropdownBtn = ({
|
|||
const onDeletePost = React.useCallback(() => {
|
||||
postDeleteMutation.mutateAsync({uri: postUri}).then(
|
||||
() => {
|
||||
Toast.show('Post deleted')
|
||||
Toast.show(_(msg`Post deleted`))
|
||||
},
|
||||
e => {
|
||||
logger.error('Failed to delete post', {error: e})
|
||||
Toast.show('Failed to delete post, please try again')
|
||||
Toast.show(_(msg`Failed to delete post, please try again`))
|
||||
},
|
||||
)
|
||||
}, [postUri, postDeleteMutation])
|
||||
}, [postUri, postDeleteMutation, _])
|
||||
|
||||
const onToggleThreadMute = React.useCallback(() => {
|
||||
try {
|
||||
const muted = toggleThreadMute(rootUri)
|
||||
if (muted) {
|
||||
Toast.show('You will no longer receive notifications for this thread')
|
||||
Toast.show(
|
||||
_(msg`You will no longer receive notifications for this thread`),
|
||||
)
|
||||
} else {
|
||||
Toast.show('You will now receive notifications for this thread')
|
||||
Toast.show(_(msg`You will now receive notifications for this thread`))
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error('Failed to toggle thread mute', {error: e})
|
||||
}
|
||||
}, [rootUri, toggleThreadMute])
|
||||
}, [rootUri, toggleThreadMute, _])
|
||||
|
||||
const onCopyPostText = React.useCallback(() => {
|
||||
Clipboard.setString(record?.text || '')
|
||||
Toast.show('Copied to clipboard')
|
||||
}, [record])
|
||||
Toast.show(_(msg`Copied to clipboard`))
|
||||
}, [record, _])
|
||||
|
||||
const onOpenTranslate = React.useCallback(() => {
|
||||
Linking.openURL(translatorUrl)
|
||||
|
@ -253,7 +255,7 @@ let PostDropdownBtn = ({
|
|||
<NativeDropdown
|
||||
testID={testID}
|
||||
items={dropdownItems}
|
||||
accessibilityLabel="More post options"
|
||||
accessibilityLabel={_(msg`More post options`)}
|
||||
accessibilityHint="">
|
||||
<View style={style}>
|
||||
<FontAwesomeIcon icon="ellipsis" size={20} color={defaultCtrlColor} />
|
||||
|
|
|
@ -50,7 +50,7 @@ export function SearchInput({
|
|||
<TextInput
|
||||
testID="searchTextInput"
|
||||
ref={textInput}
|
||||
placeholder="Search"
|
||||
placeholder={_(msg`Search`)}
|
||||
placeholderTextColor={pal.colors.textLight}
|
||||
selectTextOnFocus
|
||||
returnKeyType="search"
|
||||
|
|
|
@ -4,6 +4,8 @@ import {Image} from 'expo-image'
|
|||
import {clamp} from 'lib/numbers'
|
||||
import {Dimensions} from 'lib/media/types'
|
||||
import * as imageSizes from 'lib/media/image-sizes'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
const MIN_ASPECT_RATIO = 0.33 // 1/3
|
||||
const MAX_ASPECT_RATIO = 10 // 10/1
|
||||
|
@ -29,6 +31,7 @@ export function AutoSizedImage({
|
|||
style,
|
||||
children = null,
|
||||
}: Props) {
|
||||
const {_} = useLingui()
|
||||
const [dim, setDim] = React.useState<Dimensions | undefined>(
|
||||
dimensionsHint || imageSizes.get(uri),
|
||||
)
|
||||
|
@ -64,7 +67,7 @@ export function AutoSizedImage({
|
|||
accessible={true} // Must set for `accessibilityLabel` to work
|
||||
accessibilityIgnoresInvertColors
|
||||
accessibilityLabel={alt}
|
||||
accessibilityHint="Tap to view fully"
|
||||
accessibilityHint={_(msg`Tap to view fully`)}
|
||||
/>
|
||||
{children}
|
||||
</Pressable>
|
||||
|
|
|
@ -2,6 +2,8 @@ import {AppBskyEmbedImages} from '@atproto/api'
|
|||
import React, {ComponentProps, FC} from 'react'
|
||||
import {StyleSheet, Text, Pressable, View} from 'react-native'
|
||||
import {Image} from 'expo-image'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
type EventFunction = (index: number) => void
|
||||
|
||||
|
@ -22,6 +24,7 @@ export const GalleryItem: FC<GalleryItemProps> = ({
|
|||
onPressIn,
|
||||
onLongPress,
|
||||
}) => {
|
||||
const {_} = useLingui()
|
||||
const image = images[index]
|
||||
return (
|
||||
<View style={styles.fullWidth}>
|
||||
|
@ -31,7 +34,7 @@ export const GalleryItem: FC<GalleryItemProps> = ({
|
|||
onLongPress={onLongPress ? () => onLongPress(index) : undefined}
|
||||
style={styles.fullWidth}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={image.alt || 'Image'}
|
||||
accessibilityLabel={image.alt || _(msg`Image`)}
|
||||
accessibilityHint="">
|
||||
<Image
|
||||
source={{uri: image.thumb}}
|
||||
|
|
|
@ -63,7 +63,9 @@ export function ContentHider({
|
|||
}
|
||||
}}
|
||||
accessibilityRole="button"
|
||||
accessibilityHint={override ? 'Hide the content' : 'Show the content'}
|
||||
accessibilityHint={
|
||||
override ? _(msg`Hide the content`) : _(msg`Show the content`)
|
||||
}
|
||||
accessibilityLabel=""
|
||||
style={[
|
||||
styles.cover,
|
||||
|
|
|
@ -9,7 +9,7 @@ import {addStyle} from 'lib/styles'
|
|||
import {describeModerationCause} from 'lib/moderation'
|
||||
import {ShieldExclamation} from 'lib/icons'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
interface Props extends ComponentProps<typeof Link> {
|
||||
|
@ -57,7 +57,9 @@ export function PostHider({
|
|||
}
|
||||
}}
|
||||
accessibilityRole="button"
|
||||
accessibilityHint={override ? 'Hide the content' : 'Show the content'}
|
||||
accessibilityHint={
|
||||
override ? _(msg`Hide the content`) : _(msg`Show the content`)
|
||||
}
|
||||
accessibilityLabel=""
|
||||
style={[
|
||||
styles.description,
|
||||
|
@ -103,7 +105,7 @@ export function PostHider({
|
|||
</Text>
|
||||
{!moderation.noOverride && (
|
||||
<Text type="sm" style={[styles.showBtn, pal.link]}>
|
||||
{override ? 'Hide' : 'Show'}
|
||||
{override ? <Trans>Hide</Trans> : <Trans>Show</Trans>}
|
||||
</Text>
|
||||
)}
|
||||
</Pressable>
|
||||
|
|
|
@ -26,6 +26,8 @@ import {
|
|||
import {useComposerControls} from '#/state/shell/composer'
|
||||
import {Shadow} from '#/state/cache/types'
|
||||
import {useRequireAuth} from '#/state/session'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
let PostCtrls = ({
|
||||
big,
|
||||
|
@ -43,6 +45,7 @@ let PostCtrls = ({
|
|||
onPressReply: () => void
|
||||
}): React.ReactNode => {
|
||||
const theme = useTheme()
|
||||
const {_} = useLingui()
|
||||
const {openComposer} = useComposerControls()
|
||||
const {closeModal} = useModalControls()
|
||||
const postLikeMutation = usePostLikeMutation()
|
||||
|
@ -176,9 +179,9 @@ let PostCtrls = ({
|
|||
requireAuth(() => onPressToggleLike())
|
||||
}}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={`${post.viewer?.like ? 'Unlike' : 'Like'} (${
|
||||
post.likeCount
|
||||
} ${pluralize(post.likeCount || 0, 'like')})`}
|
||||
accessibilityLabel={`${
|
||||
post.viewer?.like ? _(msg`Unlike`) : _(msg`Like`)
|
||||
} (${post.likeCount} ${pluralize(post.likeCount || 0, 'like')})`}
|
||||
accessibilityHint=""
|
||||
hitSlop={big ? HITSLOP_20 : HITSLOP_10}>
|
||||
{post.viewer?.like ? (
|
||||
|
|
|
@ -8,6 +8,8 @@ import {pluralize} from 'lib/strings/helpers'
|
|||
import {HITSLOP_10, HITSLOP_20} from 'lib/constants'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {useRequireAuth} from '#/state/session'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
interface Props {
|
||||
isReposted: boolean
|
||||
|
@ -25,6 +27,7 @@ let RepostButton = ({
|
|||
onQuote,
|
||||
}: Props): React.ReactNode => {
|
||||
const theme = useTheme()
|
||||
const {_} = useLingui()
|
||||
const {openModal} = useModalControls()
|
||||
const requireAuth = useRequireAuth()
|
||||
|
||||
|
@ -53,7 +56,9 @@ let RepostButton = ({
|
|||
style={[styles.control, !big && styles.controlPad]}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={`${
|
||||
isReposted ? 'Undo repost' : 'Repost'
|
||||
isReposted
|
||||
? _(msg`Undo repost`)
|
||||
: _(msg({message: 'Repost', context: 'action'}))
|
||||
} (${repostCount} ${pluralize(repostCount || 0, 'repost')})`}
|
||||
accessibilityHint=""
|
||||
hitSlop={big ? HITSLOP_20 : HITSLOP_10}>
|
||||
|
|
|
@ -17,6 +17,7 @@ import {PostEmbeds} from '.'
|
|||
import {PostAlerts} from '../moderation/PostAlerts'
|
||||
import {makeProfileLink} from 'lib/routes/links'
|
||||
import {InfoCircleIcon} from 'lib/icons'
|
||||
import {Trans} from '@lingui/macro'
|
||||
|
||||
export function MaybeQuoteEmbed({
|
||||
embed,
|
||||
|
@ -52,7 +53,7 @@ export function MaybeQuoteEmbed({
|
|||
<View style={[styles.errorContainer, pal.borderDark]}>
|
||||
<InfoCircleIcon size={18} style={pal.text} />
|
||||
<Text type="lg" style={pal.text}>
|
||||
Blocked
|
||||
<Trans>Blocked</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
)
|
||||
|
@ -61,7 +62,7 @@ export function MaybeQuoteEmbed({
|
|||
<View style={[styles.errorContainer, pal.borderDark]}>
|
||||
<InfoCircleIcon size={18} style={pal.text} />
|
||||
<Text type="lg" style={pal.text}>
|
||||
Deleted
|
||||
<Trans>Deleted</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
)
|
||||
|
|
|
@ -62,8 +62,8 @@ export function AppPasswords({}: Props) {
|
|||
]}
|
||||
testID="appPasswordsScreen">
|
||||
<ErrorScreen
|
||||
title="Oops!"
|
||||
message="There was an issue with fetching your app passwords"
|
||||
title={_(msg`Oops!`)}
|
||||
message={_(msg`There was an issue with fetching your app passwords`)}
|
||||
details={cleanError(error)}
|
||||
/>
|
||||
</CenteredView>
|
||||
|
|
|
@ -16,6 +16,8 @@ import {ToggleButton} from '../com/util/forms/ToggleButton'
|
|||
import {RadioGroup} from '../com/util/forms/RadioGroup'
|
||||
import {ErrorScreen} from '../com/util/error/ErrorScreen'
|
||||
import {ErrorMessage} from '../com/util/error/ErrorMessage'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
const MAIN_VIEWS = ['Base', 'Controls', 'Error', 'Notifs']
|
||||
|
||||
|
@ -48,6 +50,7 @@ function DebugInner({
|
|||
}) {
|
||||
const [currentView, setCurrentView] = React.useState<number>(0)
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
|
||||
const renderItem = (item: any) => {
|
||||
return (
|
||||
|
@ -57,7 +60,7 @@ function DebugInner({
|
|||
type="default-light"
|
||||
onPress={onToggleColorScheme}
|
||||
isSelected={colorScheme === 'dark'}
|
||||
label="Dark mode"
|
||||
label={_(msg`Dark mode`)}
|
||||
/>
|
||||
</View>
|
||||
{item.currentView === 3 ? (
|
||||
|
@ -77,7 +80,7 @@ function DebugInner({
|
|||
|
||||
return (
|
||||
<View style={[s.hContentRegion, pal.view]}>
|
||||
<ViewHeader title="Debug panel" />
|
||||
<ViewHeader title={_(msg`Debug panel`)} />
|
||||
<ViewSelector
|
||||
swipeEnabled
|
||||
sections={MAIN_VIEWS}
|
||||
|
|
|
@ -328,7 +328,7 @@ export function FeedsScreen(_props: Props) {
|
|||
hitSlop={10}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Edit Saved Feeds`)}
|
||||
accessibilityHint="Opens screen to edit Saved Feeds">
|
||||
accessibilityHint={_(msg`Opens screen to edit Saved Feeds`)}>
|
||||
<CogIcon size={22} strokeWidth={2} style={pal.textLight} />
|
||||
</Link>
|
||||
)
|
||||
|
|
|
@ -73,7 +73,7 @@ export function ListsScreen({}: Props) {
|
|||
}}>
|
||||
<FontAwesomeIcon icon="plus" color={pal.colors.text} />
|
||||
<Text type="button" style={pal.text}>
|
||||
<Trans>New</Trans>
|
||||
<Trans context="action">New</Trans>
|
||||
</Text>
|
||||
</Button>
|
||||
</View>
|
||||
|
|
|
@ -50,7 +50,9 @@ export function LogScreen({}: NativeStackScreenProps<
|
|||
style={[styles.entry, pal.border, pal.view]}
|
||||
onPress={toggler(entry.id)}
|
||||
accessibilityLabel={_(msg`View debug entry`)}
|
||||
accessibilityHint="Opens additional details for a debug entry">
|
||||
accessibilityHint={_(
|
||||
msg`Opens additional details for a debug entry`,
|
||||
)}>
|
||||
{entry.level === 'debug' ? (
|
||||
<FontAwesomeIcon icon="info" />
|
||||
) : (
|
||||
|
|
|
@ -78,7 +78,9 @@ export function PostThreadScreen({route}: Props) {
|
|||
|
||||
return (
|
||||
<View style={s.hContentRegion}>
|
||||
{isMobile && <ViewHeader title={_(msg`Post`)} />}
|
||||
{isMobile && (
|
||||
<ViewHeader title={_(msg({message: 'Post', context: 'description'}))} />
|
||||
)}
|
||||
<View style={s.flex1}>
|
||||
{uriError ? (
|
||||
<CenteredView>
|
||||
|
|
|
@ -72,7 +72,7 @@ export function PreferencesExternalEmbeds({}: Props) {
|
|||
</View>
|
||||
</View>
|
||||
<Text type="xl-bold" style={[pal.text, styles.heading]}>
|
||||
Enable media players for
|
||||
<Trans>Enable media players for</Trans>
|
||||
</Text>
|
||||
{Object.entries(externalEmbedLabels).map(([key, label]) => (
|
||||
<PrefSelector
|
||||
|
|
|
@ -27,6 +27,7 @@ function RepliesThresholdInput({
|
|||
initialValue: number
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const [value, setValue] = useState(initialValue)
|
||||
const {mutate: setFeedViewPref} = useSetFeedViewPreferencesMutation()
|
||||
const preValue = React.useRef(initialValue)
|
||||
|
@ -64,10 +65,12 @@ function RepliesThresholdInput({
|
|||
/>
|
||||
<Text type="xs" style={pal.text}>
|
||||
{value === 0
|
||||
? `Show all replies`
|
||||
: `Show replies with at least ${value} ${
|
||||
value > 1 ? `likes` : `like`
|
||||
}`}
|
||||
? _(msg`Show all replies`)
|
||||
: _(
|
||||
msg`Show replies with at least ${value} ${
|
||||
value > 1 ? `likes` : `like`
|
||||
}`,
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
)
|
||||
|
|
|
@ -159,7 +159,7 @@ export function PreferencesThreads({navigation}: Props) {
|
|||
accessibilityLabel={_(msg`Confirm`)}
|
||||
accessibilityHint="">
|
||||
<Text style={[s.white, s.bold, s.f18]}>
|
||||
<Trans>Done</Trans>
|
||||
<Trans context="action">Done</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
|
|
@ -371,6 +371,7 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
|||
{feed, headerHeight, isFocused, scrollElRef, ignoreFilterFor},
|
||||
ref,
|
||||
) {
|
||||
const {_} = useLingui()
|
||||
const queryClient = useQueryClient()
|
||||
const [hasNew, setHasNew] = React.useState(false)
|
||||
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
|
||||
|
@ -388,8 +389,8 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
|||
}))
|
||||
|
||||
const renderPostsEmpty = React.useCallback(() => {
|
||||
return <EmptyState icon="feed" message="This feed is empty!" />
|
||||
}, [])
|
||||
return <EmptyState icon="feed" message={_(msg`This feed is empty!`)} />
|
||||
}, [_])
|
||||
|
||||
return (
|
||||
<View>
|
||||
|
@ -408,7 +409,7 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
|||
{(isScrolledDown || hasNew) && (
|
||||
<LoadLatestBtn
|
||||
onPress={onScrollToTop}
|
||||
label="Load new posts"
|
||||
label={_(msg`Load new posts`)}
|
||||
showIndicator={hasNew}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -214,11 +214,21 @@ export function ProfileFeedScreenInner({
|
|||
}
|
||||
} catch (err) {
|
||||
Toast.show(
|
||||
'There was an an issue updating your feeds, please check your internet connection and try again.',
|
||||
_(
|
||||
msg`There was an an issue updating your feeds, please check your internet connection and try again.`,
|
||||
),
|
||||
)
|
||||
logger.error('Failed up update feeds', {error: err})
|
||||
}
|
||||
}, [feedInfo, isSaved, saveFeed, removeFeed, resetSaveFeed, resetRemoveFeed])
|
||||
}, [
|
||||
feedInfo,
|
||||
isSaved,
|
||||
saveFeed,
|
||||
removeFeed,
|
||||
resetSaveFeed,
|
||||
resetRemoveFeed,
|
||||
_,
|
||||
])
|
||||
|
||||
const onTogglePinned = React.useCallback(async () => {
|
||||
try {
|
||||
|
@ -232,10 +242,10 @@ export function ProfileFeedScreenInner({
|
|||
resetPinFeed()
|
||||
}
|
||||
} catch (e) {
|
||||
Toast.show('There was an issue contacting the server')
|
||||
Toast.show(_(msg`There was an issue contacting the server`))
|
||||
logger.error('Failed to toggle pinned feed', {error: e})
|
||||
}
|
||||
}, [isPinned, feedInfo, pinFeed, unpinFeed, resetPinFeed, resetUnpinFeed])
|
||||
}, [isPinned, feedInfo, pinFeed, unpinFeed, resetPinFeed, resetUnpinFeed, _])
|
||||
|
||||
const onPressShare = React.useCallback(() => {
|
||||
const url = toShareUrl(feedInfo.route.href)
|
||||
|
@ -341,7 +351,7 @@ export function ProfileFeedScreenInner({
|
|||
<Button
|
||||
disabled={isSavePending || isRemovePending}
|
||||
type="default"
|
||||
label={isSaved ? 'Unsave' : 'Save'}
|
||||
label={isSaved ? _(msg`Unsave`) : _(msg`Save`)}
|
||||
onPress={onToggleSaved}
|
||||
style={styles.btn}
|
||||
/>
|
||||
|
@ -349,7 +359,7 @@ export function ProfileFeedScreenInner({
|
|||
testID={isPinned ? 'unpinBtn' : 'pinBtn'}
|
||||
disabled={isPinPending || isUnpinPending}
|
||||
type={isPinned ? 'default' : 'inverted'}
|
||||
label={isPinned ? 'Unpin' : 'Pin to home'}
|
||||
label={isPinned ? _(msg`Unpin`) : _(msg`Pin to home`)}
|
||||
onPress={onTogglePinned}
|
||||
style={styles.btn}
|
||||
/>
|
||||
|
@ -444,6 +454,7 @@ interface FeedSectionProps {
|
|||
}
|
||||
const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
||||
function FeedSectionImpl({feed, headerHeight, scrollElRef, isFocused}, ref) {
|
||||
const {_} = useLingui()
|
||||
const [hasNew, setHasNew] = React.useState(false)
|
||||
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
|
||||
const queryClient = useQueryClient()
|
||||
|
@ -470,8 +481,8 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
|||
}, [onScrollToTop, isScreenFocused])
|
||||
|
||||
const renderPostsEmpty = useCallback(() => {
|
||||
return <EmptyState icon="feed" message="This feed is empty!" />
|
||||
}, [])
|
||||
return <EmptyState icon="feed" message={_(msg`This feed is empty!`)} />
|
||||
}, [_])
|
||||
|
||||
return (
|
||||
<View>
|
||||
|
@ -488,7 +499,7 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
|||
{(isScrolledDown || hasNew) && (
|
||||
<LoadLatestBtn
|
||||
onPress={onScrollToTop}
|
||||
label="Load new posts"
|
||||
label={_(msg`Load new posts`)}
|
||||
showIndicator={hasNew}
|
||||
/>
|
||||
)}
|
||||
|
@ -542,11 +553,13 @@ function AboutSection({
|
|||
}
|
||||
} catch (err) {
|
||||
Toast.show(
|
||||
'There was an an issue contacting the server, please check your internet connection and try again.',
|
||||
_(
|
||||
msg`There was an an issue contacting the server, please check your internet connection and try again.`,
|
||||
),
|
||||
)
|
||||
logger.error('Failed up toggle like', {error: err})
|
||||
}
|
||||
}, [likeUri, isLiked, feedInfo, likeFeed, unlikeFeed, track])
|
||||
}, [likeUri, isLiked, feedInfo, likeFeed, unlikeFeed, track, _])
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
|
@ -597,24 +610,28 @@ function AboutSection({
|
|||
{typeof likeCount === 'number' && (
|
||||
<TextLink
|
||||
href={makeCustomFeedLink(feedOwnerDid, feedRkey, 'liked-by')}
|
||||
text={`Liked by ${likeCount} ${pluralize(likeCount, 'user')}`}
|
||||
text={_(
|
||||
msg`Liked by ${likeCount} ${pluralize(likeCount, 'user')}`,
|
||||
)}
|
||||
style={[pal.textLight, s.semiBold]}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
<Text type="md" style={[pal.textLight]} numberOfLines={1}>
|
||||
Created by{' '}
|
||||
{isOwner ? (
|
||||
'you'
|
||||
<Trans>Created by you</Trans>
|
||||
) : (
|
||||
<TextLink
|
||||
text={sanitizeHandle(feedInfo.creatorHandle, '@')}
|
||||
href={makeProfileLink({
|
||||
did: feedInfo.creatorDid,
|
||||
handle: feedInfo.creatorHandle,
|
||||
})}
|
||||
style={pal.textLight}
|
||||
/>
|
||||
<Trans>
|
||||
Created by{' '}
|
||||
<TextLink
|
||||
text={sanitizeHandle(feedInfo.creatorHandle, '@')}
|
||||
href={makeProfileLink({
|
||||
did: feedInfo.creatorDid,
|
||||
handle: feedInfo.creatorHandle,
|
||||
})}
|
||||
style={pal.textLight}
|
||||
/>
|
||||
</Trans>
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
|
|
|
@ -68,6 +68,7 @@ interface SectionRef {
|
|||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileList'>
|
||||
export function ProfileListScreen(props: Props) {
|
||||
const {_} = useLingui()
|
||||
const {name: handleOrDid, rkey} = props.route.params
|
||||
const {data: resolvedUri, error: resolveError} = useResolveUriQuery(
|
||||
AtUri.make(handleOrDid, 'app.bsky.graph.list', rkey).toString(),
|
||||
|
@ -78,7 +79,9 @@ export function ProfileListScreen(props: Props) {
|
|||
return (
|
||||
<CenteredView>
|
||||
<ErrorScreen
|
||||
error={`We're sorry, but we were unable to resolve this list. If this persists, please contact the list creator, @${handleOrDid}.`}
|
||||
error={_(
|
||||
msg`We're sorry, but we were unable to resolve this list. If this persists, please contact the list creator, @${handleOrDid}.`,
|
||||
)}
|
||||
/>
|
||||
</CenteredView>
|
||||
)
|
||||
|
@ -260,10 +263,10 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
await pinFeed({uri: list.uri})
|
||||
}
|
||||
} catch (e) {
|
||||
Toast.show('There was an issue contacting the server')
|
||||
Toast.show(_(msg`There was an issue contacting the server`))
|
||||
logger.error('Failed to toggle pinned feed', {error: e})
|
||||
}
|
||||
}, [list.uri, isPinned, pinFeed, unpinFeed])
|
||||
}, [list.uri, isPinned, pinFeed, unpinFeed, _])
|
||||
|
||||
const onSubscribeMute = useCallback(() => {
|
||||
openModal({
|
||||
|
@ -272,15 +275,17 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
message: _(
|
||||
msg`Muting is private. Muted accounts can interact with you, but you will not see their posts or receive notifications from them.`,
|
||||
),
|
||||
confirmBtnText: 'Mute this List',
|
||||
confirmBtnText: _(msg`Mute this List`),
|
||||
async onPressConfirm() {
|
||||
try {
|
||||
await listMuteMutation.mutateAsync({uri: list.uri, mute: true})
|
||||
Toast.show('List muted')
|
||||
Toast.show(_(msg`List muted`))
|
||||
track('Lists:Mute')
|
||||
} catch {
|
||||
Toast.show(
|
||||
'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.`,
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
|
@ -293,14 +298,16 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
const onUnsubscribeMute = useCallback(async () => {
|
||||
try {
|
||||
await listMuteMutation.mutateAsync({uri: list.uri, mute: false})
|
||||
Toast.show('List unmuted')
|
||||
Toast.show(_(msg`List unmuted`))
|
||||
track('Lists:Unmute')
|
||||
} catch {
|
||||
Toast.show(
|
||||
'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.`,
|
||||
),
|
||||
)
|
||||
}
|
||||
}, [list, listMuteMutation, track])
|
||||
}, [list, listMuteMutation, track, _])
|
||||
|
||||
const onSubscribeBlock = useCallback(() => {
|
||||
openModal({
|
||||
|
@ -309,15 +316,17 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
message: _(
|
||||
msg`Blocking is public. Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.`,
|
||||
),
|
||||
confirmBtnText: 'Block this List',
|
||||
confirmBtnText: _(msg`Block this List`),
|
||||
async onPressConfirm() {
|
||||
try {
|
||||
await listBlockMutation.mutateAsync({uri: list.uri, block: true})
|
||||
Toast.show('List blocked')
|
||||
Toast.show(_(msg`List blocked`))
|
||||
track('Lists:Block')
|
||||
} catch {
|
||||
Toast.show(
|
||||
'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.`,
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
|
@ -330,14 +339,16 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
const onUnsubscribeBlock = useCallback(async () => {
|
||||
try {
|
||||
await listBlockMutation.mutateAsync({uri: list.uri, block: false})
|
||||
Toast.show('List unblocked')
|
||||
Toast.show(_(msg`List unblocked`))
|
||||
track('Lists:Unblock')
|
||||
} catch {
|
||||
Toast.show(
|
||||
'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.`,
|
||||
),
|
||||
)
|
||||
}
|
||||
}, [list, listBlockMutation, track])
|
||||
}, [list, listBlockMutation, track, _])
|
||||
|
||||
const onPressEdit = useCallback(() => {
|
||||
openModal({
|
||||
|
@ -353,7 +364,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
message: _(msg`Are you sure?`),
|
||||
async onPressConfirm() {
|
||||
await listDeleteMutation.mutateAsync({uri: list.uri})
|
||||
Toast.show('List deleted')
|
||||
Toast.show(_(msg`List deleted`))
|
||||
track('Lists:Delete')
|
||||
if (navigation.canGoBack()) {
|
||||
navigation.goBack()
|
||||
|
@ -545,7 +556,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
<Button
|
||||
testID={isPinned ? 'unpinBtn' : 'pinBtn'}
|
||||
type={isPinned ? 'default' : 'inverted'}
|
||||
label={isPinned ? 'Unpin' : 'Pin to home'}
|
||||
label={isPinned ? _(msg`Unpin`) : _(msg`Pin to home`)}
|
||||
onPress={onTogglePinned}
|
||||
disabled={isPending}
|
||||
/>
|
||||
|
@ -554,14 +565,14 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
<Button
|
||||
testID="unblockBtn"
|
||||
type="default"
|
||||
label="Unblock"
|
||||
label={_(msg`Unblock`)}
|
||||
onPress={onUnsubscribeBlock}
|
||||
/>
|
||||
) : isMuting ? (
|
||||
<Button
|
||||
testID="unmuteBtn"
|
||||
type="default"
|
||||
label="Unmute"
|
||||
label={_(msg`Unmute`)}
|
||||
onPress={onUnsubscribeMute}
|
||||
/>
|
||||
) : (
|
||||
|
@ -603,6 +614,7 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
|||
const [hasNew, setHasNew] = React.useState(false)
|
||||
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
|
||||
const isScreenFocused = useIsFocused()
|
||||
const {_} = useLingui()
|
||||
|
||||
const onScrollToTop = useCallback(() => {
|
||||
scrollElRef.current?.scrollToOffset({
|
||||
|
@ -624,8 +636,8 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
|||
}, [onScrollToTop, isScreenFocused])
|
||||
|
||||
const renderPostsEmpty = useCallback(() => {
|
||||
return <EmptyState icon="feed" message="This feed is empty!" />
|
||||
}, [])
|
||||
return <EmptyState icon="feed" message={_(msg`This feed is empty!`)} />
|
||||
}, [_])
|
||||
|
||||
return (
|
||||
<View>
|
||||
|
@ -643,7 +655,7 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
|||
{(isScrolledDown || hasNew) && (
|
||||
<LoadLatestBtn
|
||||
onPress={onScrollToTop}
|
||||
label="Load new posts"
|
||||
label={_(msg`Load new posts`)}
|
||||
showIndicator={hasNew}
|
||||
/>
|
||||
)}
|
||||
|
@ -721,15 +733,30 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>(
|
|||
</Text>
|
||||
)}
|
||||
<Text type="md" style={[pal.textLight]} numberOfLines={1}>
|
||||
{isCurateList ? 'User list' : 'Moderation list'} by{' '}
|
||||
{isOwner ? (
|
||||
'you'
|
||||
{isCurateList ? (
|
||||
isOwner ? (
|
||||
<Trans>User list by you</Trans>
|
||||
) : (
|
||||
<Trans>
|
||||
User list by{' '}
|
||||
<TextLink
|
||||
text={sanitizeHandle(list.creator.handle || '', '@')}
|
||||
href={makeProfileLink(list.creator)}
|
||||
style={pal.textLight}
|
||||
/>
|
||||
</Trans>
|
||||
)
|
||||
) : isOwner ? (
|
||||
<Trans>Moderation list by you</Trans>
|
||||
) : (
|
||||
<TextLink
|
||||
text={sanitizeHandle(list.creator.handle || '', '@')}
|
||||
href={makeProfileLink(list.creator)}
|
||||
style={pal.textLight}
|
||||
/>
|
||||
<Trans>
|
||||
Moderation list by{' '}
|
||||
<TextLink
|
||||
text={sanitizeHandle(list.creator.handle || '', '@')}
|
||||
href={makeProfileLink(list.creator)}
|
||||
style={pal.textLight}
|
||||
/>
|
||||
</Trans>
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
|
@ -782,11 +809,11 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>(
|
|||
return (
|
||||
<EmptyState
|
||||
icon="users-slash"
|
||||
message="This list is empty!"
|
||||
message={_(msg`This list is empty!`)}
|
||||
style={{paddingTop: 40}}
|
||||
/>
|
||||
)
|
||||
}, [])
|
||||
}, [_])
|
||||
|
||||
return (
|
||||
<View>
|
||||
|
@ -802,7 +829,7 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>(
|
|||
{isScrolledDown && (
|
||||
<LoadLatestBtn
|
||||
onPress={onScrollToTop}
|
||||
label="Scroll to top"
|
||||
label={_(msg`Scroll to top`)}
|
||||
showIndicator={false}
|
||||
/>
|
||||
)}
|
||||
|
@ -846,7 +873,7 @@ function ErrorScreen({error}: {error: string}) {
|
|||
<Button
|
||||
type="default"
|
||||
accessibilityLabel={_(msg`Go Back`)}
|
||||
accessibilityHint="Return to previous page"
|
||||
accessibilityHint={_(msg`Return to previous page`)}
|
||||
onPress={onPressBack}
|
||||
style={{flexShrink: 1}}>
|
||||
<Text type="button" style={pal.text}>
|
||||
|
|
|
@ -160,7 +160,7 @@ export function SavedFeeds({}: Props) {
|
|||
type="sm"
|
||||
style={pal.link}
|
||||
href="https://github.com/bluesky-social/feed-generator"
|
||||
text="See this guide"
|
||||
text={_(msg`See this guide`)}
|
||||
/>{' '}
|
||||
for more information.
|
||||
</Trans>
|
||||
|
@ -188,6 +188,7 @@ function ListItem({
|
|||
>['reset']
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {isPending: isPinPending, mutateAsync: pinFeed} = usePinFeedMutation()
|
||||
const {isPending: isUnpinPending, mutateAsync: unpinFeed} =
|
||||
useUnpinFeedMutation()
|
||||
|
@ -205,10 +206,10 @@ function ListItem({
|
|||
await pinFeed({uri: feedUri})
|
||||
}
|
||||
} catch (e) {
|
||||
Toast.show('There was an issue contacting the server')
|
||||
Toast.show(_(msg`There was an issue contacting the server`))
|
||||
logger.error('Failed to toggle pinned feed', {error: e})
|
||||
}
|
||||
}, [feedUri, isPinned, pinFeed, unpinFeed, resetSaveFeedsMutationState])
|
||||
}, [feedUri, isPinned, pinFeed, unpinFeed, resetSaveFeedsMutationState, _])
|
||||
|
||||
const onPressUp = React.useCallback(async () => {
|
||||
if (!isPinned) return
|
||||
|
@ -227,10 +228,10 @@ function ListItem({
|
|||
index: pinned.indexOf(feedUri),
|
||||
})
|
||||
} catch (e) {
|
||||
Toast.show('There was an issue contacting the server')
|
||||
Toast.show(_(msg`There was an issue contacting the server`))
|
||||
logger.error('Failed to set pinned feed order', {error: e})
|
||||
}
|
||||
}, [feedUri, isPinned, setSavedFeeds, currentFeeds])
|
||||
}, [feedUri, isPinned, setSavedFeeds, currentFeeds, _])
|
||||
|
||||
const onPressDown = React.useCallback(async () => {
|
||||
if (!isPinned) return
|
||||
|
@ -248,10 +249,10 @@ function ListItem({
|
|||
index: pinned.indexOf(feedUri),
|
||||
})
|
||||
} catch (e) {
|
||||
Toast.show('There was an issue contacting the server')
|
||||
Toast.show(_(msg`There was an issue contacting the server`))
|
||||
logger.error('Failed to set pinned feed order', {error: e})
|
||||
}
|
||||
}, [feedUri, isPinned, setSavedFeeds, currentFeeds])
|
||||
}, [feedUri, isPinned, setSavedFeeds, currentFeeds, _])
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue