Hindi Internationalization (#1914)
* get basic hindi support to work * get web app language switcher in * Refactor i18n implementation and remove unused code * add missing strings * add dropdowns and modals missing strings * complete all hindi translations * fix merge conflicts * fix legeacy persisted state * fix data in RecommendedFeeds * fix lintzio/stable
parent
019aae5f01
commit
c5b6f88e9a
|
@ -28,6 +28,7 @@ import {Provider as LightboxStateProvider} from 'state/lightbox'
|
|||
import {Provider as MutedThreadsProvider} from 'state/muted-threads'
|
||||
import {Provider as InvitesStateProvider} from 'state/invites'
|
||||
import {Provider as PrefsStateProvider} from 'state/preferences'
|
||||
import I18nProvider from './locale/i18nProvider'
|
||||
import {
|
||||
Provider as SessionProvider,
|
||||
useSession,
|
||||
|
@ -35,11 +36,6 @@ import {
|
|||
} from 'state/session'
|
||||
import {Provider as UnreadNotifsProvider} from 'state/queries/notifications/unread'
|
||||
import * as persisted from '#/state/persisted'
|
||||
import {i18n} from '@lingui/core'
|
||||
import {I18nProvider} from '@lingui/react'
|
||||
import {messages} from './locale/locales/en/messages'
|
||||
i18n.load('en', messages)
|
||||
i18n.activate('en')
|
||||
|
||||
enableFreeze(true)
|
||||
SplashScreen.preventAutoHideAsync()
|
||||
|
@ -76,15 +72,13 @@ function InnerApp() {
|
|||
<UnreadNotifsProvider>
|
||||
<ThemeProvider theme={colorMode}>
|
||||
<analytics.Provider>
|
||||
<I18nProvider i18n={i18n}>
|
||||
{/* All components should be within this provider */}
|
||||
<RootSiblingParent>
|
||||
<GestureHandlerRootView style={s.h100pct}>
|
||||
<TestCtrls />
|
||||
<Shell />
|
||||
</GestureHandlerRootView>
|
||||
</RootSiblingParent>
|
||||
</I18nProvider>
|
||||
{/* All components should be within this provider */}
|
||||
<RootSiblingParent>
|
||||
<GestureHandlerRootView style={s.h100pct}>
|
||||
<TestCtrls />
|
||||
<Shell />
|
||||
</GestureHandlerRootView>
|
||||
</RootSiblingParent>
|
||||
</analytics.Provider>
|
||||
</ThemeProvider>
|
||||
</UnreadNotifsProvider>
|
||||
|
@ -115,7 +109,9 @@ function App() {
|
|||
<InvitesStateProvider>
|
||||
<ModalStateProvider>
|
||||
<LightboxStateProvider>
|
||||
<InnerApp />
|
||||
<I18nProvider>
|
||||
<InnerApp />
|
||||
</I18nProvider>
|
||||
</LightboxStateProvider>
|
||||
</ModalStateProvider>
|
||||
</InvitesStateProvider>
|
||||
|
|
|
@ -16,15 +16,13 @@ import {Shell} from 'view/shell/index'
|
|||
import {ToastContainer} from 'view/com/util/Toast.web'
|
||||
import {ThemeProvider} from 'lib/ThemeContext'
|
||||
import {queryClient} from 'lib/react-query'
|
||||
import {i18n} from '@lingui/core'
|
||||
import {I18nProvider} from '@lingui/react'
|
||||
import {defaultLocale, dynamicActivate} from './locale/i18n'
|
||||
import {Provider as ShellStateProvider} from 'state/shell'
|
||||
import {Provider as ModalStateProvider} from 'state/modals'
|
||||
import {Provider as LightboxStateProvider} from 'state/lightbox'
|
||||
import {Provider as MutedThreadsProvider} from 'state/muted-threads'
|
||||
import {Provider as InvitesStateProvider} from 'state/invites'
|
||||
import {Provider as PrefsStateProvider} from 'state/preferences'
|
||||
import I18nProvider from './locale/i18nProvider'
|
||||
import {
|
||||
Provider as SessionProvider,
|
||||
useSession,
|
||||
|
@ -44,8 +42,6 @@ function InnerApp() {
|
|||
useEffect(() => {
|
||||
initReminders()
|
||||
analytics.init()
|
||||
dynamicActivate(defaultLocale) // async import of locale data
|
||||
|
||||
const account = persisted.get('session').currentAccount
|
||||
resumeSession(account)
|
||||
}, [resumeSession])
|
||||
|
@ -64,14 +60,12 @@ function InnerApp() {
|
|||
<UnreadNotifsProvider>
|
||||
<ThemeProvider theme={colorMode}>
|
||||
<analytics.Provider>
|
||||
<I18nProvider i18n={i18n}>
|
||||
{/* All components should be within this provider */}
|
||||
<RootSiblingParent>
|
||||
<SafeAreaProvider>
|
||||
<Shell />
|
||||
</SafeAreaProvider>
|
||||
</RootSiblingParent>
|
||||
</I18nProvider>
|
||||
{/* All components should be within this provider */}
|
||||
<RootSiblingParent>
|
||||
<SafeAreaProvider>
|
||||
<Shell />
|
||||
</SafeAreaProvider>
|
||||
</RootSiblingParent>
|
||||
<ToastContainer />
|
||||
</analytics.Provider>
|
||||
</ThemeProvider>
|
||||
|
@ -103,7 +97,9 @@ function App() {
|
|||
<InvitesStateProvider>
|
||||
<ModalStateProvider>
|
||||
<LightboxStateProvider>
|
||||
<InnerApp />
|
||||
<I18nProvider>
|
||||
<InnerApp />
|
||||
</I18nProvider>
|
||||
</LightboxStateProvider>
|
||||
</ModalStateProvider>
|
||||
</InvitesStateProvider>
|
||||
|
|
|
@ -3,6 +3,7 @@ import {useCallback, useEffect} from 'react'
|
|||
import {AppState} from 'react-native'
|
||||
import {logger} from '#/logger'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {t} from '@lingui/macro'
|
||||
|
||||
export function useOTAUpdate() {
|
||||
const {openModal} = useModalControls()
|
||||
|
@ -11,9 +12,8 @@ export function useOTAUpdate() {
|
|||
const showUpdatePopup = useCallback(() => {
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Update Available',
|
||||
message:
|
||||
'A new version of the app is available. Please update to continue using the app.',
|
||||
title: t`Update Available`,
|
||||
message: t`A new version of the app is available. Please update to continue using the app.`,
|
||||
onPressConfirm: async () => {
|
||||
Updates.reloadAsync().catch(err => {
|
||||
throw err
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
import {useLanguagePrefs} from '#/state/preferences'
|
||||
import {i18n} from '@lingui/core'
|
||||
import {useEffect} from 'react'
|
||||
import {messages as messagesEn} from './locales/en/messages'
|
||||
import {messages as messagesHi} from './locales/hi/messages'
|
||||
|
||||
export const locales = {
|
||||
en: 'English',
|
||||
|
@ -14,7 +18,22 @@ export const defaultLocale = 'en'
|
|||
* @param locale any locale string
|
||||
*/
|
||||
export async function dynamicActivate(locale: string) {
|
||||
const {messages} = await import(`./locales/${locale}/messages`)
|
||||
i18n.load(locale, messages)
|
||||
i18n.activate(locale)
|
||||
console.log('dynamicActivate', locale)
|
||||
if (locale === 'en') {
|
||||
i18n.loadAndActivate({locale, messages: messagesEn})
|
||||
return
|
||||
} else if (locale === 'hi') {
|
||||
i18n.loadAndActivate({locale, messages: messagesHi})
|
||||
return
|
||||
} else {
|
||||
i18n.loadAndActivate({locale, messages: messagesEn})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
export async function useLocaleLanguage() {
|
||||
const {appLanguage} = useLanguagePrefs()
|
||||
useEffect(() => {
|
||||
dynamicActivate(appLanguage)
|
||||
}, [appLanguage])
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import React from 'react'
|
||||
import {I18nProvider as DefaultI18nProvider} from '@lingui/react'
|
||||
import {i18n} from '@lingui/core'
|
||||
import {useLocaleLanguage} from './i18n'
|
||||
|
||||
export default function I18nProvider({children}: {children: React.ReactNode}) {
|
||||
useLocaleLanguage()
|
||||
return <DefaultI18nProvider i18n={i18n}>{children}</DefaultI18nProvider>
|
||||
}
|
|
@ -4,6 +4,16 @@ interface Language {
|
|||
name: string
|
||||
}
|
||||
|
||||
interface AppLanguage {
|
||||
code2: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export const APP_LANGUAGES: AppLanguage[] = [
|
||||
{code2: 'en', name: 'English'},
|
||||
{code2: 'hi', name: 'हिंदी'},
|
||||
]
|
||||
|
||||
export const LANGUAGES: Language[] = [
|
||||
{code3: 'aar', code2: 'aa', name: 'Afar'},
|
||||
{code3: 'abk', code2: 'ab', name: 'Abkhazian'},
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -94,6 +94,8 @@ export function transform(legacy: Partial<LegacySchema>): Schema {
|
|||
postLanguageHistory:
|
||||
legacy.preferences?.postLanguageHistory ||
|
||||
defaults.languagePrefs.postLanguageHistory,
|
||||
appLanguage:
|
||||
legacy.preferences?.postLanguage || defaults.languagePrefs.appLanguage,
|
||||
},
|
||||
requireAltTextEnabled:
|
||||
legacy.preferences?.requireAltTextEnabled ||
|
||||
|
|
|
@ -30,6 +30,7 @@ export const schema = z.object({
|
|||
contentLanguages: z.array(z.string()), // should move to server
|
||||
postLanguage: z.string(), // should move to server
|
||||
postLanguageHistory: z.array(z.string()),
|
||||
appLanguage: z.string(),
|
||||
}),
|
||||
requireAltTextEnabled: z.boolean(), // should move to server
|
||||
mutedThreads: z.array(z.string()), // should move to server
|
||||
|
@ -58,6 +59,7 @@ export const defaults: Schema = {
|
|||
postLanguageHistory: (deviceLocales || [])
|
||||
.concat(['en', 'ja', 'pt', 'de'])
|
||||
.slice(0, 6),
|
||||
appLanguage: deviceLocales[0] || 'en',
|
||||
},
|
||||
requireAltTextEnabled: false,
|
||||
mutedThreads: [],
|
||||
|
|
|
@ -11,6 +11,7 @@ type ApiContext = {
|
|||
toggleContentLanguage: (code2: string) => void
|
||||
togglePostLanguage: (code2: string) => void
|
||||
savePostLanguageToHistory: () => void
|
||||
setAppLanguage: (code2: string) => void
|
||||
}
|
||||
|
||||
const stateContext = React.createContext<StateContext>(
|
||||
|
@ -22,6 +23,7 @@ const apiContext = React.createContext<ApiContext>({
|
|||
toggleContentLanguage: (_: string) => {},
|
||||
togglePostLanguage: (_: string) => {},
|
||||
savePostLanguageToHistory: () => {},
|
||||
setAppLanguage: (_: string) => {},
|
||||
})
|
||||
|
||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
|
@ -104,6 +106,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||
.slice(0, 6),
|
||||
}))
|
||||
},
|
||||
setAppLanguage(code2: string) {
|
||||
setStateWrapped(s => ({...s, appLanguage: code2}))
|
||||
},
|
||||
}),
|
||||
[state, setStateWrapped],
|
||||
)
|
||||
|
|
|
@ -10,6 +10,8 @@ import {RecommendedFeedsItem} from './RecommendedFeedsItem'
|
|||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {ErrorMessage} from 'view/com/util/error/ErrorMessage'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useSuggestedFeedsQuery} from '#/state/queries/suggested-feeds'
|
||||
|
||||
type Props = {
|
||||
|
@ -17,40 +19,45 @@ type Props = {
|
|||
}
|
||||
export function RecommendedFeeds({next}: Props) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {isTabletOrMobile} = useWebMediaQueries()
|
||||
const {isLoading, data} = useSuggestedFeedsQuery()
|
||||
|
||||
const hasFeeds = data && data?.pages?.[0]?.feeds?.length
|
||||
const hasFeeds = data && data.pages[0].feeds.length
|
||||
|
||||
const title = (
|
||||
<>
|
||||
<Text
|
||||
style={[
|
||||
pal.textLight,
|
||||
tdStyles.title1,
|
||||
isTabletOrMobile && tdStyles.title1Small,
|
||||
]}>
|
||||
Choose your
|
||||
</Text>
|
||||
<Text
|
||||
style={[
|
||||
pal.link,
|
||||
tdStyles.title2,
|
||||
isTabletOrMobile && tdStyles.title2Small,
|
||||
]}>
|
||||
Recommended
|
||||
</Text>
|
||||
<Text
|
||||
style={[
|
||||
pal.link,
|
||||
tdStyles.title2,
|
||||
isTabletOrMobile && tdStyles.title2Small,
|
||||
]}>
|
||||
Feeds
|
||||
</Text>
|
||||
<Trans>
|
||||
<Text
|
||||
style={[
|
||||
pal.textLight,
|
||||
tdStyles.title1,
|
||||
isTabletOrMobile && tdStyles.title1Small,
|
||||
]}>
|
||||
Choose your
|
||||
</Text>
|
||||
<Text
|
||||
style={[
|
||||
pal.link,
|
||||
tdStyles.title2,
|
||||
isTabletOrMobile && tdStyles.title2Small,
|
||||
]}>
|
||||
Recommended
|
||||
</Text>
|
||||
<Text
|
||||
style={[
|
||||
pal.link,
|
||||
tdStyles.title2,
|
||||
isTabletOrMobile && tdStyles.title2Small,
|
||||
]}>
|
||||
Feeds
|
||||
</Text>
|
||||
</Trans>
|
||||
<Text type="2xl-medium" style={[pal.textLight, tdStyles.description]}>
|
||||
Feeds are created by users to curate content. Choose some feeds that you
|
||||
find interesting.
|
||||
<Trans>
|
||||
Feeds are created by users to curate content. Choose some feeds that
|
||||
you find interesting.
|
||||
</Trans>
|
||||
</Text>
|
||||
<View
|
||||
style={{
|
||||
|
@ -69,7 +76,7 @@ export function RecommendedFeeds({next}: Props) {
|
|||
<Text
|
||||
type="2xl-medium"
|
||||
style={{color: '#fff', position: 'relative', top: -1}}>
|
||||
Next
|
||||
<Trans>Next</Trans>
|
||||
</Text>
|
||||
<FontAwesomeIcon icon="angle-right" color="#fff" size={14} />
|
||||
</View>
|
||||
|
@ -99,20 +106,22 @@ export function RecommendedFeeds({next}: Props) {
|
|||
<ActivityIndicator size="large" />
|
||||
</View>
|
||||
) : (
|
||||
<ErrorMessage message="Failed to load recommended feeds" />
|
||||
<ErrorMessage message={_(msg`Failed to load recommended feeds`)} />
|
||||
)}
|
||||
</TitleColumnLayout>
|
||||
</TabletOrDesktop>
|
||||
<Mobile>
|
||||
<View style={[mStyles.container]} testID="recommendedFeedsOnboarding">
|
||||
<ViewHeader
|
||||
title="Recommended Feeds"
|
||||
title={_(msg`Recommended Feeds`)}
|
||||
showBackButton={false}
|
||||
showOnDesktop
|
||||
/>
|
||||
<Text type="lg-medium" style={[pal.text, mStyles.header]}>
|
||||
Check out some recommended feeds. Tap + to add them to your list of
|
||||
pinned feeds.
|
||||
<Trans>
|
||||
Check out some recommended feeds. Tap + to add them to your list
|
||||
of pinned feeds.
|
||||
</Trans>
|
||||
</Text>
|
||||
|
||||
{hasFeeds ? (
|
||||
|
@ -128,13 +137,15 @@ export function RecommendedFeeds({next}: Props) {
|
|||
</View>
|
||||
) : (
|
||||
<View style={{flex: 1}}>
|
||||
<ErrorMessage message="Failed to load recommended feeds" />
|
||||
<ErrorMessage
|
||||
message={_(msg`Failed to load recommended feeds`)}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<Button
|
||||
onPress={next}
|
||||
label="Continue"
|
||||
label={_(msg`Continue`)}
|
||||
testID="continueBtn"
|
||||
style={mStyles.button}
|
||||
labelStyle={mStyles.buttonText}
|
||||
|
|
|
@ -14,12 +14,15 @@ import {useSuggestedFollowsQuery} from '#/state/queries/suggested-follows'
|
|||
import {useGetSuggestedFollowersByActor} from '#/state/queries/suggested-follows'
|
||||
import {useModerationOpts} from '#/state/queries/preferences'
|
||||
import {logger} from '#/logger'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
type Props = {
|
||||
next: () => void
|
||||
}
|
||||
export function RecommendedFollows({next}: Props) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {isTabletOrMobile} = useWebMediaQueries()
|
||||
const {data: suggestedFollows, dataUpdatedAt} = useSuggestedFollowsQuery()
|
||||
const getSuggestedFollowsByActor = useGetSuggestedFollowersByActor()
|
||||
|
@ -31,33 +34,37 @@ export function RecommendedFollows({next}: Props) {
|
|||
|
||||
const title = (
|
||||
<>
|
||||
<Text
|
||||
style={[
|
||||
pal.textLight,
|
||||
tdStyles.title1,
|
||||
isTabletOrMobile && tdStyles.title1Small,
|
||||
]}>
|
||||
Follow some
|
||||
</Text>
|
||||
<Text
|
||||
style={[
|
||||
pal.link,
|
||||
tdStyles.title2,
|
||||
isTabletOrMobile && tdStyles.title2Small,
|
||||
]}>
|
||||
Recommended
|
||||
</Text>
|
||||
<Text
|
||||
style={[
|
||||
pal.link,
|
||||
tdStyles.title2,
|
||||
isTabletOrMobile && tdStyles.title2Small,
|
||||
]}>
|
||||
Users
|
||||
</Text>
|
||||
<Trans>
|
||||
<Text
|
||||
style={[
|
||||
pal.textLight,
|
||||
tdStyles.title1,
|
||||
isTabletOrMobile && tdStyles.title1Small,
|
||||
]}>
|
||||
Follow some
|
||||
</Text>
|
||||
<Text
|
||||
style={[
|
||||
pal.link,
|
||||
tdStyles.title2,
|
||||
isTabletOrMobile && tdStyles.title2Small,
|
||||
]}>
|
||||
Recommended
|
||||
</Text>
|
||||
<Text
|
||||
style={[
|
||||
pal.link,
|
||||
tdStyles.title2,
|
||||
isTabletOrMobile && tdStyles.title2Small,
|
||||
]}>
|
||||
Users
|
||||
</Text>
|
||||
</Trans>
|
||||
<Text type="2xl-medium" style={[pal.textLight, tdStyles.description]}>
|
||||
Follow some users to get started. We can recommend you more users based
|
||||
on who you find interesting.
|
||||
<Trans>
|
||||
Follow some users to get started. We can recommend you more users
|
||||
based on who you find interesting.
|
||||
</Trans>
|
||||
</Text>
|
||||
<View
|
||||
style={{
|
||||
|
@ -76,7 +83,7 @@ export function RecommendedFollows({next}: Props) {
|
|||
<Text
|
||||
type="2xl-medium"
|
||||
style={{color: '#fff', position: 'relative', top: -1}}>
|
||||
Done
|
||||
<Trans>Done</Trans>
|
||||
</Text>
|
||||
<FontAwesomeIcon icon="angle-right" color="#fff" size={14} />
|
||||
</View>
|
||||
|
@ -171,13 +178,15 @@ export function RecommendedFollows({next}: Props) {
|
|||
<View style={[mStyles.container]} testID="recommendedFollowsOnboarding">
|
||||
<View>
|
||||
<ViewHeader
|
||||
title="Recommended Follows"
|
||||
title={_(msg`Recommended Users`)}
|
||||
showBackButton={false}
|
||||
showOnDesktop
|
||||
/>
|
||||
<Text type="lg-medium" style={[pal.text, mStyles.header]}>
|
||||
Check out some recommended users. Follow them to see similar
|
||||
users.
|
||||
<Trans>
|
||||
Check out some recommended users. Follow them to see similar
|
||||
users.
|
||||
</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
{!suggestedFollows || !moderationOpts ? (
|
||||
|
@ -199,7 +208,7 @@ export function RecommendedFollows({next}: Props) {
|
|||
)}
|
||||
<Button
|
||||
onPress={next}
|
||||
label="Continue"
|
||||
label={_(msg`Continue`)}
|
||||
testID="continueBtn"
|
||||
style={mStyles.button}
|
||||
labelStyle={mStyles.buttonText}
|
||||
|
|
|
@ -43,10 +43,10 @@ export function WelcomeMobile({next, skip}: Props) {
|
|||
/>
|
||||
<View>
|
||||
<Text style={[pal.text, styles.title]}>
|
||||
Welcome to{' '}
|
||||
<Text style={[pal.text, pal.link, styles.title]}>
|
||||
<Trans>Bluesky</Trans>
|
||||
</Text>
|
||||
<Trans>
|
||||
Welcome to{' '}
|
||||
<Text style={[pal.text, pal.link, styles.title]}>Bluesky</Text>
|
||||
</Trans>
|
||||
</Text>
|
||||
<View style={styles.spacer} />
|
||||
<View style={[styles.row]}>
|
||||
|
|
|
@ -129,19 +129,19 @@ export const ComposePost = observer(function ComposePost({
|
|||
}
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Discard draft',
|
||||
title: _(msg`Discard draft`),
|
||||
onPressConfirm: onClose,
|
||||
onPressCancel: () => {
|
||||
closeModal()
|
||||
},
|
||||
message: "Are you sure you'd like to discard this draft?",
|
||||
confirmBtnText: 'Discard',
|
||||
message: _(msg`Are you sure you'd like to discard this draft?`),
|
||||
confirmBtnText: _(msg`Discard`),
|
||||
confirmBtnStyle: {backgroundColor: colors.red4},
|
||||
})
|
||||
} else {
|
||||
onClose()
|
||||
}
|
||||
}, [openModal, closeModal, activeModals, onClose, graphemeLength, gallery])
|
||||
}, [openModal, closeModal, activeModals, onClose, graphemeLength, gallery, _])
|
||||
// android back button
|
||||
useEffect(() => {
|
||||
if (!isAndroid) {
|
||||
|
|
|
@ -14,6 +14,8 @@ 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 {useLingui} from '@lingui/react'
|
||||
import {
|
||||
UsePreferencesQueryResponse,
|
||||
usePreferencesQuery,
|
||||
|
@ -68,6 +70,7 @@ export function FeedSourceCardLoaded({
|
|||
showLikes?: boolean
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
|
@ -85,8 +88,8 @@ export function FeedSourceCardLoaded({
|
|||
if (isSaved) {
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Remove from my feeds',
|
||||
message: `Remove ${feed?.displayName} from my feeds?`,
|
||||
title: _(msg`Remove from my feeds`),
|
||||
message: _(msg`Remove ${feed.displayName} from my feeds?`),
|
||||
onPressConfirm: async () => {
|
||||
try {
|
||||
await removeFeed({uri: feed.uri})
|
||||
|
@ -107,7 +110,7 @@ export function FeedSourceCardLoaded({
|
|||
logger.error('Failed to save feed', {error: e})
|
||||
}
|
||||
}
|
||||
}, [isSaved, openModal, feed, removeFeed, saveFeed])
|
||||
}, [isSaved, openModal, feed, removeFeed, saveFeed, _])
|
||||
|
||||
if (!feed || !preferences) return null
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ function SwitchAccountCard({account}: {account: SessionAccount}) {
|
|||
did: currentAccount.did,
|
||||
handle: currentAccount.handle,
|
||||
})}
|
||||
title="Your profile"
|
||||
title={_(msg`Your profile`)}
|
||||
noFeedback>
|
||||
{contents}
|
||||
</Link>
|
||||
|
|
|
@ -235,7 +235,8 @@ let FeedItem = ({
|
|||
{authors.length > 1 ? (
|
||||
<>
|
||||
<Text style={[pal.text, s.mr5, s.ml5]}>
|
||||
<Trans>and</Trans>
|
||||
{' '}
|
||||
<Trans>and</Trans>{' '}
|
||||
</Text>
|
||||
<Text style={[pal.text, s.bold]}>
|
||||
{formatCount(authors.length - 1)}{' '}
|
||||
|
|
|
@ -220,7 +220,7 @@ function PostThreadLoaded({
|
|||
const renderItem = React.useCallback(
|
||||
({item, index}: {item: YieldedItem; index: number}) => {
|
||||
if (item === TOP_COMPONENT) {
|
||||
return isTablet ? <ViewHeader title="Post" /> : null
|
||||
return isTablet ? <ViewHeader title={_(msg`Post`)} /> : null
|
||||
} else if (item === PARENT_SPINNER) {
|
||||
return (
|
||||
<View style={styles.parentSpinner}>
|
||||
|
|
|
@ -35,7 +35,8 @@ import {TimeElapsed} from 'view/com/util/TimeElapsed'
|
|||
import {makeProfileLink} from 'lib/routes/links'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {MAX_POST_LINES} from 'lib/constants'
|
||||
import {Trans} from '@lingui/macro'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useLanguagePrefs} from '#/state/preferences'
|
||||
import {useComposerControls} from '#/state/shell/composer'
|
||||
import {useModerationOpts} from '#/state/queries/preferences'
|
||||
|
@ -637,13 +638,14 @@ function ExpandedPostDetails({
|
|||
translatorUrl: string
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
return (
|
||||
<View style={[s.flexRow, s.mt2, s.mb10]}>
|
||||
<Text style={pal.textLight}>{niceDate(post.indexedAt)}</Text>
|
||||
{needsTranslation && (
|
||||
<>
|
||||
<Text style={[pal.textLight, s.ml5, s.mr5]}>•</Text>
|
||||
<Link href={translatorUrl} title="Translate">
|
||||
<Link href={translatorUrl} title={_(msg`Translate`)}>
|
||||
<Text style={pal.link}>
|
||||
<Trans>Translate</Trans>
|
||||
</Text>
|
||||
|
|
|
@ -10,6 +10,8 @@ import {useNavigation} from '@react-navigation/native'
|
|||
import {NavigationProp} from 'lib/routes/types'
|
||||
import {logger} from '#/logger'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {msg as msgLingui} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {FeedDescriptor} from '#/state/queries/post-feed'
|
||||
import {EmptyState} from '../util/EmptyState'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
|
@ -86,6 +88,7 @@ function FeedgenErrorMessage({
|
|||
knownError: KnownError
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const {_: _l} = useLingui()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const msg = MESSAGES[knownError]
|
||||
const [_, uri] = feedDesc.split('|')
|
||||
|
@ -100,8 +103,8 @@ function FeedgenErrorMessage({
|
|||
const onRemoveFeed = React.useCallback(async () => {
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Remove feed',
|
||||
message: 'Remove this feed from your saved feeds?',
|
||||
title: _l(msgLingui`Remove feed`),
|
||||
message: _l(msgLingui`Remove this feed from your saved feeds?`),
|
||||
async onPressConfirm() {
|
||||
try {
|
||||
await removeFeed({uri})
|
||||
|
@ -116,7 +119,7 @@ function FeedgenErrorMessage({
|
|||
closeModal()
|
||||
},
|
||||
})
|
||||
}, [openModal, closeModal, uri, removeFeed])
|
||||
}, [openModal, closeModal, uri, removeFeed, _l])
|
||||
|
||||
return (
|
||||
<View
|
||||
|
|
|
@ -236,9 +236,10 @@ let ProfileHeaderLoaded = ({
|
|||
track('ProfileHeader:BlockAccountButtonClicked')
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Block Account',
|
||||
message:
|
||||
'Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.',
|
||||
title: _(msg`Block Account`),
|
||||
message: _(
|
||||
msg`Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.`,
|
||||
),
|
||||
onPressConfirm: async () => {
|
||||
try {
|
||||
await queueBlock()
|
||||
|
@ -251,15 +252,16 @@ let ProfileHeaderLoaded = ({
|
|||
}
|
||||
},
|
||||
})
|
||||
}, [track, queueBlock, openModal])
|
||||
}, [track, queueBlock, openModal, _])
|
||||
|
||||
const onPressUnblockAccount = React.useCallback(async () => {
|
||||
track('ProfileHeader:UnblockAccountButtonClicked')
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Unblock Account',
|
||||
message:
|
||||
'The account will be able to interact with you after unblocking.',
|
||||
title: _(msg`Unblock Account`),
|
||||
message: _(
|
||||
msg`The account will be able to interact with you after unblocking.`,
|
||||
),
|
||||
onPressConfirm: async () => {
|
||||
try {
|
||||
await queueUnblock()
|
||||
|
@ -272,7 +274,7 @@ let ProfileHeaderLoaded = ({
|
|||
}
|
||||
},
|
||||
})
|
||||
}, [track, queueUnblock, openModal])
|
||||
}, [track, queueUnblock, openModal, _])
|
||||
|
||||
const onPressReportAccount = React.useCallback(() => {
|
||||
track('ProfileHeader:ReportAccountButtonClicked')
|
||||
|
@ -290,7 +292,7 @@ let ProfileHeaderLoaded = ({
|
|||
let items: DropdownItem[] = [
|
||||
{
|
||||
testID: 'profileHeaderDropdownShareBtn',
|
||||
label: 'Share',
|
||||
label: _(msg`Share`),
|
||||
onPress: onPressShare,
|
||||
icon: {
|
||||
ios: {
|
||||
|
@ -304,7 +306,7 @@ let ProfileHeaderLoaded = ({
|
|||
items.push({label: 'separator'})
|
||||
items.push({
|
||||
testID: 'profileHeaderDropdownListAddRemoveBtn',
|
||||
label: 'Add to Lists',
|
||||
label: _(msg`Add to Lists`),
|
||||
onPress: onPressAddRemoveLists,
|
||||
icon: {
|
||||
ios: {
|
||||
|
@ -318,7 +320,9 @@ let ProfileHeaderLoaded = ({
|
|||
if (!profile.viewer?.blocking) {
|
||||
items.push({
|
||||
testID: 'profileHeaderDropdownMuteBtn',
|
||||
label: profile.viewer?.muted ? 'Unmute Account' : 'Mute Account',
|
||||
label: profile.viewer?.muted
|
||||
? _(msg`Unmute Account`)
|
||||
: _(msg`Mute Account`),
|
||||
onPress: profile.viewer?.muted
|
||||
? onPressUnmuteAccount
|
||||
: onPressMuteAccount,
|
||||
|
@ -334,7 +338,9 @@ let ProfileHeaderLoaded = ({
|
|||
if (!profile.viewer?.blockingByList) {
|
||||
items.push({
|
||||
testID: 'profileHeaderDropdownBlockBtn',
|
||||
label: profile.viewer?.blocking ? 'Unblock Account' : 'Block Account',
|
||||
label: profile.viewer?.blocking
|
||||
? _(msg`Unblock Account`)
|
||||
: _(msg`Block Account`),
|
||||
onPress: profile.viewer?.blocking
|
||||
? onPressUnblockAccount
|
||||
: onPressBlockAccount,
|
||||
|
@ -349,7 +355,7 @@ let ProfileHeaderLoaded = ({
|
|||
}
|
||||
items.push({
|
||||
testID: 'profileHeaderDropdownReportBtn',
|
||||
label: 'Report Account',
|
||||
label: _(msg`Report Account`),
|
||||
onPress: onPressReportAccount,
|
||||
icon: {
|
||||
ios: {
|
||||
|
@ -373,6 +379,7 @@ let ProfileHeaderLoaded = ({
|
|||
onPressBlockAccount,
|
||||
onPressReportAccount,
|
||||
onPressAddRemoveLists,
|
||||
_,
|
||||
])
|
||||
|
||||
const blockHide =
|
||||
|
|
|
@ -19,7 +19,7 @@ export function AccountDropdownBtn({account}: {account: SessionAccount}) {
|
|||
|
||||
const items: DropdownItem[] = [
|
||||
{
|
||||
label: 'Remove account',
|
||||
label: _(msg`Remove account`),
|
||||
onPress: () => {
|
||||
removeAccount(account)
|
||||
Toast.show('Account removed from quick access')
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React, {Component, ErrorInfo, ReactNode} from 'react'
|
||||
import {ErrorScreen} from './error/ErrorScreen'
|
||||
import {CenteredView} from './Views'
|
||||
import {t} from '@lingui/macro'
|
||||
|
||||
interface Props {
|
||||
children?: ReactNode
|
||||
|
@ -30,8 +31,8 @@ export class ErrorBoundary extends Component<Props, State> {
|
|||
return (
|
||||
<CenteredView style={{height: '100%', flex: 1}}>
|
||||
<ErrorScreen
|
||||
title="Oh no!"
|
||||
message="There was an unexpected issue in the application. Please let us know if this happened to you!"
|
||||
title={t`Oh no!`}
|
||||
message={t`There was an unexpected issue in the application. Please let us know if this happened to you!`}
|
||||
details={this.state.error.toString()}
|
||||
/>
|
||||
</CenteredView>
|
||||
|
|
|
@ -3,7 +3,6 @@ import {ago} from 'lib/strings/time'
|
|||
import {useTickEveryMinute} from '#/state/shell'
|
||||
|
||||
// FIXME(dan): Figure out why the false positives
|
||||
/* eslint-disable react/prop-types */
|
||||
|
||||
export function TimeElapsed({
|
||||
timestamp,
|
||||
|
|
|
@ -208,7 +208,7 @@ export function EditableUserAvatar({
|
|||
[
|
||||
!isWeb && {
|
||||
testID: 'changeAvatarCameraBtn',
|
||||
label: 'Camera',
|
||||
label: _(msg`Camera`),
|
||||
icon: {
|
||||
ios: {
|
||||
name: 'camera',
|
||||
|
@ -232,7 +232,7 @@ export function EditableUserAvatar({
|
|||
},
|
||||
{
|
||||
testID: 'changeAvatarLibraryBtn',
|
||||
label: 'Library',
|
||||
label: _(msg`Library`),
|
||||
icon: {
|
||||
ios: {
|
||||
name: 'photo.on.rectangle.angled',
|
||||
|
@ -269,7 +269,7 @@ export function EditableUserAvatar({
|
|||
},
|
||||
!!avatar && {
|
||||
testID: 'changeAvatarRemoveBtn',
|
||||
label: 'Remove',
|
||||
label: _(msg`Remove`),
|
||||
icon: {
|
||||
ios: {
|
||||
name: 'trash',
|
||||
|
@ -287,6 +287,7 @@ export function EditableUserAvatar({
|
|||
onSelectNewAvatar,
|
||||
requestCameraAccessIfNeeded,
|
||||
requestPhotoAccessIfNeeded,
|
||||
_,
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ export function UserBanner({
|
|||
[
|
||||
!isWeb && {
|
||||
testID: 'changeBannerCameraBtn',
|
||||
label: 'Camera',
|
||||
label: _(msg`Camera`),
|
||||
icon: {
|
||||
ios: {
|
||||
name: 'camera',
|
||||
|
@ -57,7 +57,7 @@ export function UserBanner({
|
|||
},
|
||||
{
|
||||
testID: 'changeBannerLibraryBtn',
|
||||
label: 'Library',
|
||||
label: _(msg`Library`),
|
||||
icon: {
|
||||
ios: {
|
||||
name: 'photo.on.rectangle.angled',
|
||||
|
@ -86,7 +86,7 @@ export function UserBanner({
|
|||
},
|
||||
!!banner && {
|
||||
testID: 'changeBannerRemoveBtn',
|
||||
label: 'Remove',
|
||||
label: _(msg`Remove`),
|
||||
icon: {
|
||||
ios: {
|
||||
name: 'trash',
|
||||
|
@ -104,6 +104,7 @@ export function UserBanner({
|
|||
onSelectNewBanner,
|
||||
requestCameraAccessIfNeeded,
|
||||
requestPhotoAccessIfNeeded,
|
||||
_,
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ import {useMutedThreads, useToggleThreadMute} from '#/state/muted-threads'
|
|||
import {useLanguagePrefs} from '#/state/preferences'
|
||||
import {logger} from '#/logger'
|
||||
import {Shadow} from '#/state/cache/types'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useSession} from '#/state/session'
|
||||
|
||||
export function PostDropdownBtn({
|
||||
|
@ -35,6 +37,7 @@ export function PostDropdownBtn({
|
|||
}) {
|
||||
const {currentAccount} = useSession()
|
||||
const theme = useTheme()
|
||||
const {_} = useLingui()
|
||||
const defaultCtrlColor = theme.palette.default.postCtrl
|
||||
const {openModal} = useModalControls()
|
||||
const langPrefs = useLanguagePrefs()
|
||||
|
@ -91,7 +94,7 @@ export function PostDropdownBtn({
|
|||
|
||||
const dropdownItems: NativeDropdownItem[] = [
|
||||
{
|
||||
label: 'Translate',
|
||||
label: _(msg`Translate`),
|
||||
onPress() {
|
||||
onOpenTranslate()
|
||||
},
|
||||
|
@ -105,7 +108,7 @@ export function PostDropdownBtn({
|
|||
},
|
||||
},
|
||||
{
|
||||
label: 'Copy post text',
|
||||
label: _(msg`Copy post text`),
|
||||
onPress() {
|
||||
onCopyPostText()
|
||||
},
|
||||
|
@ -119,7 +122,7 @@ export function PostDropdownBtn({
|
|||
},
|
||||
},
|
||||
{
|
||||
label: 'Share',
|
||||
label: _(msg`Share`),
|
||||
onPress() {
|
||||
const url = toShareUrl(href)
|
||||
shareUrl(url)
|
||||
|
@ -137,7 +140,7 @@ export function PostDropdownBtn({
|
|||
label: 'separator',
|
||||
},
|
||||
{
|
||||
label: isThreadMuted ? 'Unmute thread' : 'Mute thread',
|
||||
label: isThreadMuted ? _(msg`Unmute thread`) : _(msg`Mute thread`),
|
||||
onPress() {
|
||||
onToggleThreadMute()
|
||||
},
|
||||
|
@ -154,7 +157,7 @@ export function PostDropdownBtn({
|
|||
label: 'separator',
|
||||
},
|
||||
!isAuthor && {
|
||||
label: 'Report post',
|
||||
label: _(msg`Report post`),
|
||||
onPress() {
|
||||
openModal({
|
||||
name: 'report',
|
||||
|
@ -175,12 +178,12 @@ export function PostDropdownBtn({
|
|||
label: 'separator',
|
||||
},
|
||||
isAuthor && {
|
||||
label: 'Delete post',
|
||||
label: _(msg`Delete post`),
|
||||
onPress() {
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Delete this post?',
|
||||
message: 'Are you sure? This can not be undone.',
|
||||
title: _(msg`Delete this post?`),
|
||||
message: _(msg`Are you sure? This cannot be undone.`),
|
||||
onPressConfirm: onDeletePost,
|
||||
})
|
||||
},
|
||||
|
|
|
@ -41,7 +41,7 @@ export const RepostButton = ({
|
|||
|
||||
const dropdownItems: NativeDropdownItem[] = [
|
||||
{
|
||||
label: isReposted ? 'Undo repost' : 'Repost',
|
||||
label: isReposted ? _(msg`Undo repost`) : _(msg`Repost`),
|
||||
testID: 'repostDropdownRepostBtn',
|
||||
icon: {
|
||||
ios: {name: 'repeat'},
|
||||
|
@ -51,7 +51,7 @@ export const RepostButton = ({
|
|||
onPress: onRepost,
|
||||
},
|
||||
{
|
||||
label: 'Quote post',
|
||||
label: _(msg`Quote post`),
|
||||
testID: 'repostDropdownQuoteBtn',
|
||||
icon: {
|
||||
ios: {name: 'quote.bubble'},
|
||||
|
|
|
@ -183,9 +183,10 @@ export const AppPasswords = withAuthRequired(
|
|||
function AppPasswordsHeader() {
|
||||
const {isTabletOrDesktop} = useWebMediaQueries()
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
return (
|
||||
<>
|
||||
<ViewHeader title="App Passwords" showOnDesktop />
|
||||
<ViewHeader title={_(msg`App Passwords`)} showOnDesktop />
|
||||
<Text
|
||||
type="sm"
|
||||
style={[
|
||||
|
@ -220,14 +221,16 @@ function AppPassword({
|
|||
const onDelete = React.useCallback(async () => {
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Delete App Password',
|
||||
message: `Are you sure you want to delete the app password "${name}"?`,
|
||||
title: _(msg`Delete app password`),
|
||||
message: _(
|
||||
msg`Are you sure you want to delete the app password "${name}"?`,
|
||||
),
|
||||
async onPressConfirm() {
|
||||
await deleteMutation.mutateAsync({name})
|
||||
Toast.show('App password deleted')
|
||||
},
|
||||
})
|
||||
}, [deleteMutation, openModal, name])
|
||||
}, [deleteMutation, openModal, name, _])
|
||||
|
||||
const primaryLocale =
|
||||
contentLanguages.length > 0 ? contentLanguages[0] : 'en-US'
|
||||
|
@ -245,15 +248,17 @@ function AppPassword({
|
|||
{name}
|
||||
</Text>
|
||||
<Text type="md" style={[pal.text, styles.pr10]} numberOfLines={1}>
|
||||
Created{' '}
|
||||
{Intl.DateTimeFormat(primaryLocale, {
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
}).format(new Date(createdAt))}
|
||||
<Trans>
|
||||
Created{' '}
|
||||
{Intl.DateTimeFormat(primaryLocale, {
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
}).format(new Date(createdAt))}
|
||||
</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
<FontAwesomeIcon icon={['far', 'trash-can']} style={styles.trashIcon} />
|
||||
|
|
|
@ -9,6 +9,8 @@ import {ScrollView} from 'view/com/util/Views'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {s} from 'lib/styles'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
type Props = NativeStackScreenProps<
|
||||
CommonNavigatorParams,
|
||||
|
@ -16,6 +18,7 @@ type Props = NativeStackScreenProps<
|
|||
>
|
||||
export const CommunityGuidelinesScreen = (_props: Props) => {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
|
||||
useFocusEffect(
|
||||
|
@ -26,16 +29,18 @@ export const CommunityGuidelinesScreen = (_props: Props) => {
|
|||
|
||||
return (
|
||||
<View>
|
||||
<ViewHeader title="Community Guidelines" />
|
||||
<ViewHeader title={_(msg`Community Guidelines`)} />
|
||||
<ScrollView style={[s.hContentRegion, pal.view]}>
|
||||
<View style={[s.p20]}>
|
||||
<Text style={pal.text}>
|
||||
The Community Guidelines have been moved to{' '}
|
||||
<TextLink
|
||||
style={pal.link}
|
||||
href="https://blueskyweb.xyz/support/community-guidelines"
|
||||
text="blueskyweb.xyz/support/community-guidelines"
|
||||
/>
|
||||
<Trans>
|
||||
The Community Guidelines have been moved to{' '}
|
||||
<TextLink
|
||||
style={pal.link}
|
||||
href="https://blueskyweb.xyz/support/community-guidelines"
|
||||
text="blueskyweb.xyz/support/community-guidelines"
|
||||
/>
|
||||
</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
<View style={s.footerSpacer} />
|
||||
|
|
|
@ -9,10 +9,13 @@ import {ScrollView} from 'view/com/util/Views'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {s} from 'lib/styles'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'CopyrightPolicy'>
|
||||
export const CopyrightPolicyScreen = (_props: Props) => {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
|
||||
useFocusEffect(
|
||||
|
@ -23,16 +26,18 @@ export const CopyrightPolicyScreen = (_props: Props) => {
|
|||
|
||||
return (
|
||||
<View>
|
||||
<ViewHeader title="Copyright Policy" />
|
||||
<ViewHeader title={_(msg`Copyright Policy`)} />
|
||||
<ScrollView style={[s.hContentRegion, pal.view]}>
|
||||
<View style={[s.p20]}>
|
||||
<Text style={pal.text}>
|
||||
The Copyright Policy has been moved to{' '}
|
||||
<TextLink
|
||||
style={pal.link}
|
||||
href="https://blueskyweb.xyz/support/community-guidelines"
|
||||
text="blueskyweb.xyz/support/community-guidelines"
|
||||
/>
|
||||
<Trans>
|
||||
The Copyright Policy has been moved to{' '}
|
||||
<TextLink
|
||||
style={pal.link}
|
||||
href="https://blueskyweb.xyz/support/community-guidelines"
|
||||
text="blueskyweb.xyz/support/community-guidelines"
|
||||
/>
|
||||
</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
<View style={s.footerSpacer} />
|
||||
|
|
|
@ -467,7 +467,7 @@ export const FeedsScreen = withAuthRequired(function FeedsScreenImpl(
|
|||
<View style={[pal.view, styles.container]}>
|
||||
{isMobile && (
|
||||
<ViewHeader
|
||||
title="Feeds"
|
||||
title={_(msg`Feeds`)}
|
||||
canGoBack={false}
|
||||
renderButton={renderHeaderBtn}
|
||||
showBorder
|
||||
|
|
|
@ -14,16 +14,19 @@ import {
|
|||
} from '@fortawesome/react-native-fontawesome'
|
||||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {useFocusEffect} from '@react-navigation/native'
|
||||
import {LANGUAGES} from 'lib/../locale/languages'
|
||||
import {APP_LANGUAGES, LANGUAGES} from 'lib/../locale/languages'
|
||||
import RNPickerSelect, {PickerSelectProps} from 'react-native-picker-select'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {useLanguagePrefs, useLanguagePrefsApi} from '#/state/preferences'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'LanguageSettings'>
|
||||
|
||||
export function LanguageSettingsScreen(_: Props) {
|
||||
export function LanguageSettingsScreen(_props: Props) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const langPrefs = useLanguagePrefs()
|
||||
const setLangPrefs = useLanguagePrefsApi()
|
||||
const {isTabletOrDesktop} = useWebMediaQueries()
|
||||
|
@ -52,6 +55,15 @@ export function LanguageSettingsScreen(_: Props) {
|
|||
[langPrefs, setLangPrefs],
|
||||
)
|
||||
|
||||
const onChangeAppLanguage = React.useCallback(
|
||||
(value: Parameters<PickerSelectProps['onValueChange']>[0]) => {
|
||||
if (langPrefs.appLanguage !== value) {
|
||||
setLangPrefs.setAppLanguage(value)
|
||||
}
|
||||
},
|
||||
[langPrefs, setLangPrefs],
|
||||
)
|
||||
|
||||
const myLanguages = React.useMemo(() => {
|
||||
return (
|
||||
langPrefs.contentLanguages
|
||||
|
@ -71,15 +83,109 @@ export function LanguageSettingsScreen(_: Props) {
|
|||
styles.container,
|
||||
isTabletOrDesktop && styles.desktopContainer,
|
||||
]}>
|
||||
<ViewHeader title="Language Settings" showOnDesktop />
|
||||
<ViewHeader title={_(msg`Language Settings`)} showOnDesktop />
|
||||
|
||||
<View style={{paddingTop: 20, paddingHorizontal: 20}}>
|
||||
{/* APP LANGUAGE */}
|
||||
<View style={{paddingBottom: 20}}>
|
||||
<Text type="title-sm" style={[pal.text, s.pb5]}>
|
||||
Primary Language
|
||||
<Trans>App Language</Trans>
|
||||
</Text>
|
||||
<Text style={[pal.text, s.pb10]}>
|
||||
Select your preferred language for translations in your feed.
|
||||
<Trans>
|
||||
Select your app language for the default text to display in the
|
||||
app
|
||||
</Trans>
|
||||
</Text>
|
||||
|
||||
<View style={{position: 'relative'}}>
|
||||
<RNPickerSelect
|
||||
value={langPrefs.appLanguage}
|
||||
onValueChange={onChangeAppLanguage}
|
||||
items={APP_LANGUAGES.filter(l => Boolean(l.code2)).map(l => ({
|
||||
label: l.name,
|
||||
value: l.code2,
|
||||
key: l.code2,
|
||||
}))}
|
||||
style={{
|
||||
inputAndroid: {
|
||||
backgroundColor: pal.viewLight.backgroundColor,
|
||||
color: pal.text.color,
|
||||
fontSize: 14,
|
||||
letterSpacing: 0.5,
|
||||
fontWeight: '500',
|
||||
paddingHorizontal: 14,
|
||||
paddingVertical: 8,
|
||||
borderRadius: 24,
|
||||
},
|
||||
inputIOS: {
|
||||
backgroundColor: pal.viewLight.backgroundColor,
|
||||
color: pal.text.color,
|
||||
fontSize: 14,
|
||||
letterSpacing: 0.5,
|
||||
fontWeight: '500',
|
||||
paddingHorizontal: 14,
|
||||
paddingVertical: 8,
|
||||
borderRadius: 24,
|
||||
},
|
||||
inputWeb: {
|
||||
// @ts-ignore web only
|
||||
cursor: 'pointer',
|
||||
'-moz-appearance': 'none',
|
||||
'-webkit-appearance': 'none',
|
||||
appearance: 'none',
|
||||
outline: 0,
|
||||
borderWidth: 0,
|
||||
backgroundColor: pal.viewLight.backgroundColor,
|
||||
color: pal.text.color,
|
||||
fontSize: 14,
|
||||
letterSpacing: 0.5,
|
||||
fontWeight: '500',
|
||||
paddingHorizontal: 14,
|
||||
paddingVertical: 8,
|
||||
borderRadius: 24,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<View
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 1,
|
||||
right: 1,
|
||||
bottom: 1,
|
||||
width: 40,
|
||||
backgroundColor: pal.viewLight.backgroundColor,
|
||||
borderRadius: 24,
|
||||
pointerEvents: 'none',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}>
|
||||
<FontAwesomeIcon
|
||||
icon="chevron-down"
|
||||
style={pal.text as FontAwesomeIconStyle}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={{
|
||||
height: 1,
|
||||
backgroundColor: pal.border.borderColor,
|
||||
marginBottom: 20,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* PRIMARY LANGUAGE */}
|
||||
<View style={{paddingBottom: 20}}>
|
||||
<Text type="title-sm" style={[pal.text, s.pb5]}>
|
||||
<Trans>Primary Language</Trans>
|
||||
</Text>
|
||||
<Text style={[pal.text, s.pb10]}>
|
||||
<Trans>
|
||||
Select your preferred language for translations in your feed.
|
||||
</Trans>
|
||||
</Text>
|
||||
|
||||
<View style={{position: 'relative'}}>
|
||||
|
@ -161,13 +267,16 @@ export function LanguageSettingsScreen(_: Props) {
|
|||
}}
|
||||
/>
|
||||
|
||||
{/* CONTENT LANGUAGES */}
|
||||
<View style={{paddingBottom: 20}}>
|
||||
<Text type="title-sm" style={[pal.text, s.pb5]}>
|
||||
Content Languages
|
||||
<Trans>Content Languages</Trans>
|
||||
</Text>
|
||||
<Text style={[pal.text, s.pb10]}>
|
||||
Select which languages you want your subscribed feeds to include. If
|
||||
none are selected, all languages will be shown.
|
||||
<Trans>
|
||||
Select which languages you want your subscribed feeds to include.
|
||||
If none are selected, all languages will be shown.
|
||||
</Trans>
|
||||
</Text>
|
||||
|
||||
<Button
|
||||
|
|
|
@ -15,6 +15,7 @@ import {SimpleViewHeader} from 'view/com/util/SimpleViewHeader'
|
|||
import {s} from 'lib/styles'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {Trans} from '@lingui/macro'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Lists'>
|
||||
export const ListsScreen = withAuthRequired(
|
||||
|
@ -56,10 +57,10 @@ export const ListsScreen = withAuthRequired(
|
|||
}>
|
||||
<View style={{flex: 1}}>
|
||||
<Text type="title-lg" style={[pal.text, {fontWeight: 'bold'}]}>
|
||||
User Lists
|
||||
<Trans>User Lists</Trans>
|
||||
</Text>
|
||||
<Text style={pal.textLight}>
|
||||
Public, shareable lists which can drive feeds.
|
||||
<Trans>Public, shareable lists which can drive feeds.</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
<View>
|
||||
|
@ -74,7 +75,7 @@ export const ListsScreen = withAuthRequired(
|
|||
}}>
|
||||
<FontAwesomeIcon icon="plus" color={pal.colors.text} />
|
||||
<Text type="button" style={pal.text}>
|
||||
New
|
||||
<Trans>New</Trans>
|
||||
</Text>
|
||||
</Button>
|
||||
</View>
|
||||
|
|
|
@ -17,11 +17,14 @@ import {useAnalytics} from 'lib/analytics/analytics'
|
|||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Moderation'>
|
||||
export const ModerationScreen = withAuthRequired(
|
||||
function Moderation({}: Props) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {screen, track} = useAnalytics()
|
||||
const {isTabletOrDesktop} = useWebMediaQueries()
|
||||
|
@ -47,7 +50,7 @@ export const ModerationScreen = withAuthRequired(
|
|||
isTabletOrDesktop ? styles.desktopContainer : pal.viewLight,
|
||||
]}
|
||||
testID="moderationScreen">
|
||||
<ViewHeader title="Moderation" showOnDesktop />
|
||||
<ViewHeader title={_(msg`Moderation`)} showOnDesktop />
|
||||
<View style={styles.spacer} />
|
||||
<TouchableOpacity
|
||||
testID="contentFilteringBtn"
|
||||
|
@ -63,7 +66,7 @@ export const ModerationScreen = withAuthRequired(
|
|||
/>
|
||||
</View>
|
||||
<Text type="lg" style={pal.text}>
|
||||
Content filtering
|
||||
<Trans>Content filtering</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<Link
|
||||
|
@ -77,7 +80,7 @@ export const ModerationScreen = withAuthRequired(
|
|||
/>
|
||||
</View>
|
||||
<Text type="lg" style={pal.text}>
|
||||
Moderation lists
|
||||
<Trans>Moderation lists</Trans>
|
||||
</Text>
|
||||
</Link>
|
||||
<Link
|
||||
|
@ -91,7 +94,7 @@ export const ModerationScreen = withAuthRequired(
|
|||
/>
|
||||
</View>
|
||||
<Text type="lg" style={pal.text}>
|
||||
Muted accounts
|
||||
<Trans>Muted accounts</Trans>
|
||||
</Text>
|
||||
</Link>
|
||||
<Link
|
||||
|
@ -105,7 +108,7 @@ export const ModerationScreen = withAuthRequired(
|
|||
/>
|
||||
</View>
|
||||
<Text type="lg" style={pal.text}>
|
||||
Blocked accounts
|
||||
<Trans>Blocked accounts</Trans>
|
||||
</Text>
|
||||
</Link>
|
||||
</CenteredView>
|
||||
|
|
|
@ -21,6 +21,8 @@ import {ErrorScreen} from '../com/util/error/ErrorScreen'
|
|||
import {ProfileCard} from 'view/com/profile/ProfileCard'
|
||||
import {logger} from '#/logger'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useMyBlockedAccountsQuery} from '#/state/queries/my-blocked-accounts'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
|
||||
|
@ -31,6 +33,7 @@ type Props = NativeStackScreenProps<
|
|||
export const ModerationBlockedAccounts = withAuthRequired(
|
||||
function ModerationBlockedAccountsImpl({}: Props) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {isTabletOrDesktop} = useWebMediaQueries()
|
||||
const {screen} = useAnalytics()
|
||||
|
@ -104,7 +107,7 @@ export const ModerationBlockedAccounts = withAuthRequired(
|
|||
pal.border,
|
||||
]}
|
||||
testID="blockedAccountsScreen">
|
||||
<ViewHeader title="Blocked Accounts" showOnDesktop />
|
||||
<ViewHeader title={_(msg`Blocked Accounts`)} showOnDesktop />
|
||||
<Text
|
||||
type="sm"
|
||||
style={[
|
||||
|
@ -112,9 +115,11 @@ export const ModerationBlockedAccounts = withAuthRequired(
|
|||
pal.text,
|
||||
isTabletOrDesktop && styles.descriptionDesktop,
|
||||
]}>
|
||||
Blocked accounts cannot reply in your threads, mention you, or
|
||||
otherwise interact with you. You will not see their content and they
|
||||
will be prevented from seeing yours.
|
||||
<Trans>
|
||||
Blocked accounts cannot reply in your threads, mention you, or
|
||||
otherwise interact with you. You will not see their content and they
|
||||
will be prevented from seeing yours.
|
||||
</Trans>
|
||||
</Text>
|
||||
{isEmpty ? (
|
||||
<View style={[pal.border, !isTabletOrDesktop && styles.flex1]}>
|
||||
|
@ -127,9 +132,11 @@ export const ModerationBlockedAccounts = withAuthRequired(
|
|||
) : (
|
||||
<View style={[styles.empty, pal.viewLight]}>
|
||||
<Text type="lg" style={[pal.text, styles.emptyText]}>
|
||||
You have not blocked any accounts yet. To block an account, go
|
||||
to their profile and selected "Block account" from the menu on
|
||||
their account.
|
||||
<Trans>
|
||||
You have not blocked any accounts yet. To block an account,
|
||||
go to their profile and selected "Block account" from the
|
||||
menu on their account.
|
||||
</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
|
|
|
@ -21,6 +21,8 @@ import {ErrorScreen} from '../com/util/error/ErrorScreen'
|
|||
import {ProfileCard} from 'view/com/profile/ProfileCard'
|
||||
import {logger} from '#/logger'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useMyMutedAccountsQuery} from '#/state/queries/my-muted-accounts'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
|
||||
|
@ -31,6 +33,7 @@ type Props = NativeStackScreenProps<
|
|||
export const ModerationMutedAccounts = withAuthRequired(
|
||||
function ModerationMutedAccountsImpl({}: Props) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {isTabletOrDesktop} = useWebMediaQueries()
|
||||
const {screen} = useAnalytics()
|
||||
|
@ -104,7 +107,7 @@ export const ModerationMutedAccounts = withAuthRequired(
|
|||
pal.border,
|
||||
]}
|
||||
testID="mutedAccountsScreen">
|
||||
<ViewHeader title="Muted Accounts" showOnDesktop />
|
||||
<ViewHeader title={_(msg`Muted Accounts`)} showOnDesktop />
|
||||
<Text
|
||||
type="sm"
|
||||
style={[
|
||||
|
@ -112,8 +115,10 @@ export const ModerationMutedAccounts = withAuthRequired(
|
|||
pal.text,
|
||||
isTabletOrDesktop && styles.descriptionDesktop,
|
||||
]}>
|
||||
Muted accounts have their posts removed from your feed and from your
|
||||
notifications. Mutes are completely private.
|
||||
<Trans>
|
||||
Muted accounts have their posts removed from your feed and from your
|
||||
notifications. Mutes are completely private.
|
||||
</Trans>
|
||||
</Text>
|
||||
{isEmpty ? (
|
||||
<View style={[pal.border, !isTabletOrDesktop && styles.flex1]}>
|
||||
|
@ -126,9 +131,11 @@ export const ModerationMutedAccounts = withAuthRequired(
|
|||
) : (
|
||||
<View style={[styles.empty, pal.viewLight]}>
|
||||
<Text type="lg" style={[pal.text, styles.emptyText]}>
|
||||
You have not muted any accounts yet. To mute an account, go to
|
||||
their profile and selected "Mute account" from the menu on
|
||||
their account.
|
||||
<Trans>
|
||||
You have not muted any accounts yet. To mute an account, go
|
||||
to their profile and selected "Mute account" from the menu
|
||||
on their account.
|
||||
</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
|
|
|
@ -12,9 +12,12 @@ import {NavigationProp} from 'lib/routes/types'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {s} from 'lib/styles'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
export const NotFoundScreen = () => {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
|
||||
|
@ -36,13 +39,15 @@ export const NotFoundScreen = () => {
|
|||
|
||||
return (
|
||||
<View testID="notFoundView" style={pal.view}>
|
||||
<ViewHeader title="Page not found" />
|
||||
<ViewHeader title={_(msg`Page not found`)} />
|
||||
<View style={styles.container}>
|
||||
<Text type="title-2xl" style={[pal.text, s.mb10]}>
|
||||
Page not found
|
||||
<Trans>Page not found</Trans>
|
||||
</Text>
|
||||
<Text type="md" style={[pal.text, s.mb10]}>
|
||||
We're sorry! We can't find the page you were looking for.
|
||||
<Trans>
|
||||
We're sorry! We can't find the page you were looking for.
|
||||
</Trans>
|
||||
</Text>
|
||||
<Button
|
||||
type="primary"
|
||||
|
|
|
@ -18,6 +18,8 @@ import {s, colors} from 'lib/styles'
|
|||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {logger} from '#/logger'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useUnreadNotifications} from '#/state/queries/notifications/unread'
|
||||
import {RQKEY as NOTIFS_RQKEY} from '#/state/queries/notifications/feed'
|
||||
import {listenSoftReset, emitSoftReset} from '#/state/events'
|
||||
|
@ -28,6 +30,7 @@ type Props = NativeStackScreenProps<
|
|||
>
|
||||
export const NotificationsScreen = withAuthRequired(
|
||||
function NotificationsScreenImpl({}: Props) {
|
||||
const {_} = useLingui()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const [onMainScroll, isScrolledDown, resetMainScroll] = useOnMainScroll()
|
||||
const scrollElRef = React.useRef<FlatList>(null)
|
||||
|
@ -83,7 +86,7 @@ export const NotificationsScreen = withAuthRequired(
|
|||
style={[pal.text, {fontWeight: 'bold'}]}
|
||||
text={
|
||||
<>
|
||||
Notifications{' '}
|
||||
<Trans>Notifications</Trans>{' '}
|
||||
{hasNew && (
|
||||
<View
|
||||
style={{
|
||||
|
@ -107,7 +110,7 @@ export const NotificationsScreen = withAuthRequired(
|
|||
|
||||
return (
|
||||
<View testID="notificationsScreen" style={s.hContentRegion}>
|
||||
<ViewHeader title="Notifications" canGoBack={false} />
|
||||
<ViewHeader title={_(msg`Notifications`)} canGoBack={false} />
|
||||
<Feed
|
||||
onScroll={onMainScroll}
|
||||
scrollElRef={scrollElRef}
|
||||
|
@ -116,7 +119,7 @@ export const NotificationsScreen = withAuthRequired(
|
|||
{(isScrolledDown || hasNew) && (
|
||||
<LoadLatestBtn
|
||||
onPress={onPressLoadLatest}
|
||||
label="Load new notifications"
|
||||
label={_(msg`Load new notifications`)}
|
||||
showIndicator={hasNew}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -7,12 +7,15 @@ import {ViewHeader} from '../com/util/ViewHeader'
|
|||
import {PostLikedBy as PostLikedByComponent} from '../com/post-thread/PostLikedBy'
|
||||
import {makeRecordUri} from 'lib/strings/url-helpers'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostLikedBy'>
|
||||
export const PostLikedByScreen = withAuthRequired(({route}: Props) => {
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {name, rkey} = route.params
|
||||
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
|
||||
const {_} = useLingui()
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
|
@ -22,7 +25,7 @@ export const PostLikedByScreen = withAuthRequired(({route}: Props) => {
|
|||
|
||||
return (
|
||||
<View>
|
||||
<ViewHeader title="Liked by" />
|
||||
<ViewHeader title={_(msg`Liked by`)} />
|
||||
<PostLikedByComponent uri={uri} />
|
||||
</View>
|
||||
)
|
||||
|
|
|
@ -7,12 +7,15 @@ import {ViewHeader} from '../com/util/ViewHeader'
|
|||
import {PostRepostedBy as PostRepostedByComponent} from '../com/post-thread/PostRepostedBy'
|
||||
import {makeRecordUri} from 'lib/strings/url-helpers'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostRepostedBy'>
|
||||
export const PostRepostedByScreen = withAuthRequired(({route}: Props) => {
|
||||
const {name, rkey} = route.params
|
||||
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {_} = useLingui()
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
|
@ -22,7 +25,7 @@ export const PostRepostedByScreen = withAuthRequired(({route}: Props) => {
|
|||
|
||||
return (
|
||||
<View>
|
||||
<ViewHeader title="Reposted by" />
|
||||
<ViewHeader title={_(msg`Reposted by`)} />
|
||||
<PostRepostedByComponent uri={uri} />
|
||||
</View>
|
||||
)
|
||||
|
|
|
@ -19,6 +19,8 @@ import {clamp} from 'lodash'
|
|||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
|
||||
import {ErrorMessage} from '../com/util/error/ErrorMessage'
|
||||
import {CenteredView} from '../com/util/Views'
|
||||
|
@ -29,6 +31,7 @@ export const PostThreadScreen = withAuthRequired(function PostThreadScreenImpl({
|
|||
route,
|
||||
}: Props) {
|
||||
const queryClient = useQueryClient()
|
||||
const {_} = useLingui()
|
||||
const {fabMinimalShellTransform} = useMinimalShellMode()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {openComposer} = useComposerControls()
|
||||
|
@ -74,7 +77,7 @@ export const PostThreadScreen = withAuthRequired(function PostThreadScreenImpl({
|
|||
|
||||
return (
|
||||
<View style={s.hContentRegion}>
|
||||
{isMobile && <ViewHeader title="Post" />}
|
||||
{isMobile && <ViewHeader title={_(msg`Post`)} />}
|
||||
<View style={s.flex1}>
|
||||
{uriError ? (
|
||||
<CenteredView>
|
||||
|
|
|
@ -92,7 +92,7 @@ export function PreferencesHomeFeed({navigation}: Props) {
|
|||
styles.container,
|
||||
isTabletOrDesktop && styles.desktopContainer,
|
||||
]}>
|
||||
<ViewHeader title="Home Feed Preferences" showOnDesktop />
|
||||
<ViewHeader title={_(msg`Home Feed Preferences`)} showOnDesktop />
|
||||
<View
|
||||
style={[
|
||||
styles.titleSection,
|
||||
|
@ -142,7 +142,7 @@ export function PreferencesHomeFeed({navigation}: Props) {
|
|||
</Text>
|
||||
<ToggleButton
|
||||
type="default-light"
|
||||
label="Followed users only"
|
||||
label={_(msg`Followed users only`)}
|
||||
isSelected={Boolean(
|
||||
variables?.hideRepliesByUnfollowed ??
|
||||
preferences?.feedViewPrefs?.hideRepliesByUnfollowed,
|
||||
|
@ -188,8 +188,8 @@ export function PreferencesHomeFeed({navigation}: Props) {
|
|||
label={
|
||||
variables?.hideReposts ??
|
||||
preferences?.feedViewPrefs?.hideReposts
|
||||
? 'No'
|
||||
: 'Yes'
|
||||
? _(msg`No`)
|
||||
: _(msg`Yes`)
|
||||
}
|
||||
isSelected={
|
||||
!(
|
||||
|
@ -223,8 +223,8 @@ export function PreferencesHomeFeed({navigation}: Props) {
|
|||
label={
|
||||
variables?.hideQuotePosts ??
|
||||
preferences?.feedViewPrefs?.hideQuotePosts
|
||||
? 'No'
|
||||
: 'Yes'
|
||||
? _(msg`No`)
|
||||
: _(msg`Yes`)
|
||||
}
|
||||
isSelected={
|
||||
!(
|
||||
|
@ -259,8 +259,8 @@ export function PreferencesHomeFeed({navigation}: Props) {
|
|||
label={
|
||||
variables?.lab_mergeFeedEnabled ??
|
||||
preferences?.feedViewPrefs?.lab_mergeFeedEnabled
|
||||
? 'Yes'
|
||||
: 'No'
|
||||
? _(msg`Yes`)
|
||||
: _(msg`No`)
|
||||
}
|
||||
isSelected={
|
||||
!!(
|
||||
|
|
|
@ -50,7 +50,7 @@ export function PreferencesThreads({navigation}: Props) {
|
|||
styles.container,
|
||||
isTabletOrDesktop && styles.desktopContainer,
|
||||
]}>
|
||||
<ViewHeader title="Thread Preferences" showOnDesktop />
|
||||
<ViewHeader title={_(msg`Thread Preferences`)} showOnDesktop />
|
||||
<View
|
||||
style={[
|
||||
styles.titleSection,
|
||||
|
|
|
@ -9,10 +9,13 @@ import {ScrollView} from 'view/com/util/Views'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {s} from 'lib/styles'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PrivacyPolicy'>
|
||||
export const PrivacyPolicyScreen = (_props: Props) => {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
|
||||
useFocusEffect(
|
||||
|
@ -23,16 +26,18 @@ export const PrivacyPolicyScreen = (_props: Props) => {
|
|||
|
||||
return (
|
||||
<View>
|
||||
<ViewHeader title="Privacy Policy" />
|
||||
<ViewHeader title={_(msg`Privacy Policy`)} />
|
||||
<ScrollView style={[s.hContentRegion, pal.view]}>
|
||||
<View style={[s.p20]}>
|
||||
<Text style={pal.text}>
|
||||
The Privacy Policy has been moved to{' '}
|
||||
<TextLink
|
||||
style={pal.link}
|
||||
href="https://blueskyweb.xyz/support/privacy-policy"
|
||||
text="blueskyweb.xyz/support/privacy-policy"
|
||||
/>
|
||||
<Trans>
|
||||
The Privacy Policy has been moved to{' '}
|
||||
<TextLink
|
||||
style={pal.link}
|
||||
href="https://blueskyweb.xyz/support/privacy-policy"
|
||||
text="blueskyweb.xyz/support/privacy-policy"
|
||||
/>
|
||||
</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
<View style={s.footerSpacer} />
|
||||
|
|
|
@ -269,7 +269,7 @@ export function ProfileFeedScreenInner({
|
|||
return [
|
||||
{
|
||||
testID: 'feedHeaderDropdownToggleSavedBtn',
|
||||
label: isSaved ? 'Remove from my feeds' : 'Add to my feeds',
|
||||
label: isSaved ? _(msg`Remove from my feeds`) : _(msg`Add to my feeds`),
|
||||
onPress: isSavePending || isRemovePending ? undefined : onToggleSaved,
|
||||
icon: isSaved
|
||||
? {
|
||||
|
@ -289,7 +289,7 @@ export function ProfileFeedScreenInner({
|
|||
},
|
||||
{
|
||||
testID: 'feedHeaderDropdownReportBtn',
|
||||
label: 'Report feed',
|
||||
label: _(msg`Report feed`),
|
||||
onPress: onPressReport,
|
||||
icon: {
|
||||
ios: {
|
||||
|
@ -301,7 +301,7 @@ export function ProfileFeedScreenInner({
|
|||
},
|
||||
{
|
||||
testID: 'feedHeaderDropdownShareBtn',
|
||||
label: 'Share link',
|
||||
label: _(msg`Share feed`),
|
||||
onPress: onPressShare,
|
||||
icon: {
|
||||
ios: {
|
||||
|
@ -319,6 +319,7 @@ export function ProfileFeedScreenInner({
|
|||
isSaved,
|
||||
isSavePending,
|
||||
isRemovePending,
|
||||
_,
|
||||
])
|
||||
|
||||
const renderHeader = useCallback(() => {
|
||||
|
|
|
@ -7,12 +7,15 @@ import {ViewHeader} from '../com/util/ViewHeader'
|
|||
import {PostLikedBy as PostLikedByComponent} from '../com/post-thread/PostLikedBy'
|
||||
import {makeRecordUri} from 'lib/strings/url-helpers'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileFeedLikedBy'>
|
||||
export const ProfileFeedLikedByScreen = withAuthRequired(({route}: Props) => {
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {name, rkey} = route.params
|
||||
const uri = makeRecordUri(name, 'app.bsky.feed.generator', rkey)
|
||||
const {_} = useLingui()
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
|
@ -22,7 +25,7 @@ export const ProfileFeedLikedByScreen = withAuthRequired(({route}: Props) => {
|
|||
|
||||
return (
|
||||
<View>
|
||||
<ViewHeader title="Liked by" />
|
||||
<ViewHeader title={_(msg`Liked by`)} />
|
||||
<PostLikedByComponent uri={uri} />
|
||||
</View>
|
||||
)
|
||||
|
|
|
@ -6,11 +6,14 @@ import {withAuthRequired} from 'view/com/auth/withAuthRequired'
|
|||
import {ViewHeader} from '../com/util/ViewHeader'
|
||||
import {ProfileFollowers as ProfileFollowersComponent} from '../com/profile/ProfileFollowers'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileFollowers'>
|
||||
export const ProfileFollowersScreen = withAuthRequired(({route}: Props) => {
|
||||
const {name} = route.params
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {_} = useLingui()
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
|
@ -20,7 +23,7 @@ export const ProfileFollowersScreen = withAuthRequired(({route}: Props) => {
|
|||
|
||||
return (
|
||||
<View>
|
||||
<ViewHeader title="Followers" />
|
||||
<ViewHeader title={_(msg`Followers`)} />
|
||||
<ProfileFollowersComponent name={name} />
|
||||
</View>
|
||||
)
|
||||
|
|
|
@ -6,11 +6,14 @@ import {withAuthRequired} from 'view/com/auth/withAuthRequired'
|
|||
import {ViewHeader} from '../com/util/ViewHeader'
|
||||
import {ProfileFollows as ProfileFollowsComponent} from '../com/profile/ProfileFollows'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileFollows'>
|
||||
export const ProfileFollowsScreen = withAuthRequired(({route}: Props) => {
|
||||
const {name} = route.params
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {_} = useLingui()
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
|
@ -20,7 +23,7 @@ export const ProfileFollowsScreen = withAuthRequired(({route}: Props) => {
|
|||
|
||||
return (
|
||||
<View>
|
||||
<ViewHeader title="Following" />
|
||||
<ViewHeader title={_(msg`Following`)} />
|
||||
<ProfileFollowsComponent name={name} />
|
||||
</View>
|
||||
)
|
||||
|
|
|
@ -268,9 +268,10 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
const onSubscribeMute = useCallback(() => {
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Mute these accounts?',
|
||||
message:
|
||||
'Muting is private. Muted accounts can interact with you, but you will not see their posts or receive notifications from them.',
|
||||
title: _(msg`Mute these accounts?`),
|
||||
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',
|
||||
async onPressConfirm() {
|
||||
try {
|
||||
|
@ -286,7 +287,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
closeModal()
|
||||
},
|
||||
})
|
||||
}, [openModal, closeModal, list, listMuteMutation])
|
||||
}, [openModal, closeModal, list, listMuteMutation, _])
|
||||
|
||||
const onUnsubscribeMute = useCallback(async () => {
|
||||
try {
|
||||
|
@ -302,9 +303,10 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
const onSubscribeBlock = useCallback(() => {
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Block these accounts?',
|
||||
message:
|
||||
'Blocking is public. Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.',
|
||||
title: _(msg`Block these accounts?`),
|
||||
message: _(
|
||||
msg`Blocking is public. Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.`,
|
||||
),
|
||||
confirmBtnText: 'Block this List',
|
||||
async onPressConfirm() {
|
||||
try {
|
||||
|
@ -320,7 +322,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
closeModal()
|
||||
},
|
||||
})
|
||||
}, [openModal, closeModal, list, listBlockMutation])
|
||||
}, [openModal, closeModal, list, listBlockMutation, _])
|
||||
|
||||
const onUnsubscribeBlock = useCallback(async () => {
|
||||
try {
|
||||
|
@ -343,8 +345,8 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
const onPressDelete = useCallback(() => {
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Delete List',
|
||||
message: 'Are you sure?',
|
||||
title: _(msg`Delete List`),
|
||||
message: _(msg`Are you sure?`),
|
||||
async onPressConfirm() {
|
||||
await listDeleteMutation.mutateAsync({uri: list.uri})
|
||||
Toast.show('List deleted')
|
||||
|
@ -355,7 +357,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
}
|
||||
},
|
||||
})
|
||||
}, [openModal, list, listDeleteMutation, navigation])
|
||||
}, [openModal, list, listDeleteMutation, navigation, _])
|
||||
|
||||
const onPressReport = useCallback(() => {
|
||||
openModal({
|
||||
|
@ -374,7 +376,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
let items: DropdownItem[] = [
|
||||
{
|
||||
testID: 'listHeaderDropdownShareBtn',
|
||||
label: 'Share',
|
||||
label: _(msg`Share`),
|
||||
onPress: onPressShare,
|
||||
icon: {
|
||||
ios: {
|
||||
|
@ -389,7 +391,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
items.push({label: 'separator'})
|
||||
items.push({
|
||||
testID: 'listHeaderDropdownEditBtn',
|
||||
label: 'Edit List Details',
|
||||
label: _(msg`Edit list details`),
|
||||
onPress: onPressEdit,
|
||||
icon: {
|
||||
ios: {
|
||||
|
@ -401,7 +403,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
})
|
||||
items.push({
|
||||
testID: 'listHeaderDropdownDeleteBtn',
|
||||
label: 'Delete List',
|
||||
label: _(msg`Delete List`),
|
||||
onPress: onPressDelete,
|
||||
icon: {
|
||||
ios: {
|
||||
|
@ -415,7 +417,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
items.push({label: 'separator'})
|
||||
items.push({
|
||||
testID: 'listHeaderDropdownReportBtn',
|
||||
label: 'Report List',
|
||||
label: _(msg`Report List`),
|
||||
onPress: onPressReport,
|
||||
icon: {
|
||||
ios: {
|
||||
|
@ -427,13 +429,13 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
})
|
||||
}
|
||||
return items
|
||||
}, [isOwner, onPressShare, onPressEdit, onPressDelete, onPressReport])
|
||||
}, [isOwner, onPressShare, onPressEdit, onPressDelete, onPressReport, _])
|
||||
|
||||
const subscribeDropdownItems: DropdownItem[] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
testID: 'subscribeDropdownMuteBtn',
|
||||
label: 'Mute accounts',
|
||||
label: _(msg`Mute accounts`),
|
||||
onPress: onSubscribeMute,
|
||||
icon: {
|
||||
ios: {
|
||||
|
@ -445,7 +447,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
},
|
||||
{
|
||||
testID: 'subscribeDropdownBlockBtn',
|
||||
label: 'Block accounts',
|
||||
label: _(msg`Block accounts`),
|
||||
onPress: onSubscribeBlock,
|
||||
icon: {
|
||||
ios: {
|
||||
|
@ -456,7 +458,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
|||
},
|
||||
},
|
||||
]
|
||||
}, [onSubscribeMute, onSubscribeBlock])
|
||||
}, [onSubscribeMute, onSubscribeBlock, _])
|
||||
|
||||
return (
|
||||
<ProfileSubpageHeader
|
||||
|
|
|
@ -9,7 +9,6 @@ import {
|
|||
import {useFocusEffect} from '@react-navigation/native'
|
||||
import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {track} from '#/lib/analytics/analytics'
|
||||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
|
@ -27,6 +26,8 @@ import {Haptics} from 'lib/haptics'
|
|||
import {TextLink} from 'view/com/util/Link'
|
||||
import {logger} from '#/logger'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {
|
||||
usePreferencesQuery,
|
||||
usePinFeedMutation,
|
||||
|
@ -52,6 +53,7 @@ const HITSLOP_BOTTOM = {
|
|||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'SavedFeeds'>
|
||||
export const SavedFeeds = withAuthRequired(function SavedFeedsImpl({}: Props) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {isMobile, isTabletOrDesktop} = useWebMediaQueries()
|
||||
const {screen} = useAnalytics()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
|
@ -71,11 +73,11 @@ export const SavedFeeds = withAuthRequired(function SavedFeedsImpl({}: Props) {
|
|||
pal.border,
|
||||
isTabletOrDesktop && styles.desktopContainer,
|
||||
]}>
|
||||
<ViewHeader title="Edit My Feeds" showOnDesktop showBorder />
|
||||
<ViewHeader title={_(msg`Edit My Feeds`)} showOnDesktop showBorder />
|
||||
<ScrollView style={s.flex1}>
|
||||
<View style={[pal.text, pal.border, styles.title]}>
|
||||
<Text type="title" style={pal.text}>
|
||||
Pinned Feeds
|
||||
<Trans>Pinned Feeds</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
{preferences?.feeds ? (
|
||||
|
@ -88,7 +90,7 @@ export const SavedFeeds = withAuthRequired(function SavedFeedsImpl({}: Props) {
|
|||
styles.empty,
|
||||
]}>
|
||||
<Text type="lg" style={[pal.text]}>
|
||||
You don't have any pinned feeds.
|
||||
<Trans>You don't have any pinned feeds.</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
) : (
|
||||
|
@ -101,7 +103,7 @@ export const SavedFeeds = withAuthRequired(function SavedFeedsImpl({}: Props) {
|
|||
)}
|
||||
<View style={[pal.text, pal.border, styles.title]}>
|
||||
<Text type="title" style={pal.text}>
|
||||
Saved Feeds
|
||||
<Trans>Saved Feeds</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
{preferences?.feeds ? (
|
||||
|
@ -114,7 +116,7 @@ export const SavedFeeds = withAuthRequired(function SavedFeedsImpl({}: Props) {
|
|||
styles.empty,
|
||||
]}>
|
||||
<Text type="lg" style={[pal.text]}>
|
||||
You don't have any saved feeds.
|
||||
<Trans>You don't have any saved feeds.</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
) : (
|
||||
|
@ -128,15 +130,17 @@ export const SavedFeeds = withAuthRequired(function SavedFeedsImpl({}: Props) {
|
|||
|
||||
<View style={styles.footerText}>
|
||||
<Text type="sm" style={pal.textLight}>
|
||||
Feeds are custom algorithms that users build with a little coding
|
||||
expertise.{' '}
|
||||
<TextLink
|
||||
type="sm"
|
||||
style={pal.link}
|
||||
href="https://github.com/bluesky-social/feed-generator"
|
||||
text="See this guide"
|
||||
/>{' '}
|
||||
for more information.
|
||||
<Trans>
|
||||
Feeds are custom algorithms that users build with a little coding
|
||||
expertise.{' '}
|
||||
<TextLink
|
||||
type="sm"
|
||||
style={pal.link}
|
||||
href="https://github.com/bluesky-social/feed-generator"
|
||||
text="See this guide"
|
||||
/>{' '}
|
||||
for more information.
|
||||
</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
<View style={{height: 100}} />
|
||||
|
|
|
@ -222,10 +222,10 @@ function SearchScreenPostResults({query}: {query: string}) {
|
|||
return results?.pages.flatMap(page => page.posts) || []
|
||||
}, [results])
|
||||
const items = React.useMemo(() => {
|
||||
let items: SearchResultSlice[] = []
|
||||
let temp: SearchResultSlice[] = []
|
||||
|
||||
for (const post of posts) {
|
||||
items.push({
|
||||
temp.push({
|
||||
type: 'post',
|
||||
key: post.uri,
|
||||
post,
|
||||
|
@ -233,13 +233,13 @@ function SearchScreenPostResults({query}: {query: string}) {
|
|||
}
|
||||
|
||||
if (isFetchingNextPage) {
|
||||
items.push({
|
||||
temp.push({
|
||||
type: 'loadingMore',
|
||||
key: 'loadingMore',
|
||||
})
|
||||
}
|
||||
|
||||
return items
|
||||
return temp
|
||||
}, [posts, isFetchingNextPage])
|
||||
|
||||
return error ? (
|
||||
|
@ -299,9 +299,9 @@ function SearchScreenUserResults({query}: {query: string}) {
|
|||
|
||||
React.useEffect(() => {
|
||||
async function getResults() {
|
||||
const results = await search({query, limit: 30})
|
||||
const searchResults = await search({query, limit: 30})
|
||||
|
||||
if (results) {
|
||||
if (searchResults) {
|
||||
setDataUpdatedAt(Date.now())
|
||||
setResults(results)
|
||||
setIsFetched(true)
|
||||
|
@ -314,7 +314,7 @@ function SearchScreenUserResults({query}: {query: string}) {
|
|||
setResults([])
|
||||
setIsFetched(false)
|
||||
}
|
||||
}, [query, setDataUpdatedAt, search])
|
||||
}, [query, setDataUpdatedAt, search, results])
|
||||
|
||||
return isFetched ? (
|
||||
<>
|
||||
|
|
|
@ -268,7 +268,7 @@ export const SettingsScreen = withAuthRequired(function Settings({}: Props) {
|
|||
|
||||
return (
|
||||
<View style={[s.hContentRegion]} testID="settingsScreen">
|
||||
<ViewHeader title="Settings" />
|
||||
<ViewHeader title={_(msg`Settings`)} />
|
||||
<ScrollView
|
||||
style={[s.hContentRegion]}
|
||||
contentContainerStyle={isMobile && pal.viewLight}
|
||||
|
@ -281,7 +281,7 @@ export const SettingsScreen = withAuthRequired(function Settings({}: Props) {
|
|||
</Text>
|
||||
<View style={[styles.infoLine]}>
|
||||
<Text type="lg-medium" style={pal.text}>
|
||||
Email:{' '}
|
||||
<Trans>Email:</Trans>{' '}
|
||||
</Text>
|
||||
{currentAccount.emailConfirmed && (
|
||||
<>
|
||||
|
|
|
@ -10,11 +10,14 @@ import {usePalette} from 'lib/hooks/usePalette'
|
|||
import {s} from 'lib/styles'
|
||||
import {HELP_DESK_URL} from 'lib/constants'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Support'>
|
||||
export const SupportScreen = (_props: Props) => {
|
||||
const pal = usePalette('default')
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {_} = useLingui()
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
|
@ -24,19 +27,21 @@ export const SupportScreen = (_props: Props) => {
|
|||
|
||||
return (
|
||||
<View>
|
||||
<ViewHeader title="Support" />
|
||||
<ViewHeader title={_(msg`Support`)} />
|
||||
<CenteredView>
|
||||
<Text type="title-xl" style={[pal.text, s.p20, s.pb5]}>
|
||||
Support
|
||||
<Trans>Support</Trans>
|
||||
</Text>
|
||||
<Text style={[pal.text, s.p20]}>
|
||||
The support form has been moved. If you need help, please
|
||||
<TextLink
|
||||
href={HELP_DESK_URL}
|
||||
text=" click here"
|
||||
style={pal.link}
|
||||
/>{' '}
|
||||
or visit {HELP_DESK_URL} to get in touch with us.
|
||||
<Trans>
|
||||
The support form has been moved. If you need help, please
|
||||
<TextLink
|
||||
href={HELP_DESK_URL}
|
||||
text=" click here"
|
||||
style={pal.link}
|
||||
/>{' '}
|
||||
or visit {HELP_DESK_URL} to get in touch with us.
|
||||
</Trans>
|
||||
</Text>
|
||||
</CenteredView>
|
||||
</View>
|
||||
|
|
|
@ -9,11 +9,14 @@ import {ScrollView} from 'view/com/util/Views'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {s} from 'lib/styles'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'TermsOfService'>
|
||||
export const TermsOfServiceScreen = (_props: Props) => {
|
||||
const pal = usePalette('default')
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {_} = useLingui()
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
|
@ -23,11 +26,11 @@ export const TermsOfServiceScreen = (_props: Props) => {
|
|||
|
||||
return (
|
||||
<View>
|
||||
<ViewHeader title="Terms of Service" />
|
||||
<ViewHeader title={_(msg`Terms of Service`)} />
|
||||
<ScrollView style={[s.hContentRegion, pal.view]}>
|
||||
<View style={[s.p20]}>
|
||||
<Text style={pal.text}>
|
||||
The Terms of Service have been moved to{' '}
|
||||
<Trans>The Terms of Service have been moved to</Trans>{' '}
|
||||
<TextLink
|
||||
style={pal.link}
|
||||
href="https://blueskyweb.xyz/support/tos"
|
||||
|
|
|
@ -247,7 +247,7 @@ export function DrawerContent() {
|
|||
/>
|
||||
)
|
||||
}
|
||||
label="Search"
|
||||
label={_(msg`Search`)}
|
||||
accessibilityLabel={_(msg`Search`)}
|
||||
accessibilityHint=""
|
||||
bold={isAtSearch}
|
||||
|
@ -269,7 +269,7 @@ export function DrawerContent() {
|
|||
/>
|
||||
)
|
||||
}
|
||||
label="Home"
|
||||
label={_(msg`Home`)}
|
||||
accessibilityLabel={_(msg`Home`)}
|
||||
accessibilityHint=""
|
||||
bold={isAtHome}
|
||||
|
@ -291,7 +291,7 @@ export function DrawerContent() {
|
|||
/>
|
||||
)
|
||||
}
|
||||
label="Notifications"
|
||||
label={_(msg`Notifications`)}
|
||||
accessibilityLabel={_(msg`Notifications`)}
|
||||
accessibilityHint={
|
||||
numUnreadNotifications === ''
|
||||
|
@ -318,7 +318,7 @@ export function DrawerContent() {
|
|||
/>
|
||||
)
|
||||
}
|
||||
label="Feeds"
|
||||
label={_(msg`Feeds`)}
|
||||
accessibilityLabel={_(msg`Feeds`)}
|
||||
accessibilityHint=""
|
||||
bold={isAtFeeds}
|
||||
|
@ -326,14 +326,14 @@ export function DrawerContent() {
|
|||
/>
|
||||
<MenuItem
|
||||
icon={<ListIcon strokeWidth={2} style={pal.text} size={26} />}
|
||||
label="Lists"
|
||||
label={_(msg`Lists`)}
|
||||
accessibilityLabel={_(msg`Lists`)}
|
||||
accessibilityHint=""
|
||||
onPress={onPressLists}
|
||||
/>
|
||||
<MenuItem
|
||||
icon={<HandIcon strokeWidth={5} style={pal.text} size={24} />}
|
||||
label="Moderation"
|
||||
label={_(msg`Moderation`)}
|
||||
accessibilityLabel={_(msg`Moderation`)}
|
||||
accessibilityHint=""
|
||||
onPress={onPressModeration}
|
||||
|
@ -354,7 +354,7 @@ export function DrawerContent() {
|
|||
/>
|
||||
)
|
||||
}
|
||||
label="Profile"
|
||||
label={_(msg`Profile`)}
|
||||
accessibilityLabel={_(msg`Profile`)}
|
||||
accessibilityHint=""
|
||||
onPress={onPressProfile}
|
||||
|
@ -367,7 +367,7 @@ export function DrawerContent() {
|
|||
strokeWidth={1.75}
|
||||
/>
|
||||
}
|
||||
label="Settings"
|
||||
label={_(msg`Settings`)}
|
||||
accessibilityLabel={_(msg`Settings`)}
|
||||
accessibilityHint=""
|
||||
onPress={onPressSettings}
|
||||
|
|
|
@ -4,10 +4,13 @@ import {useNavigationState} from '@react-navigation/native'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {TextLink} from 'view/com/util/Link'
|
||||
import {getCurrentRoute} from 'lib/routes/helpers'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {usePinnedFeedsInfos} from '#/state/queries/feed'
|
||||
|
||||
export function DesktopFeeds() {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const feeds = usePinnedFeedsInfos()
|
||||
|
||||
const route = useNavigationState(state => {
|
||||
|
@ -47,7 +50,7 @@ export function DesktopFeeds() {
|
|||
<TextLink
|
||||
type="lg"
|
||||
href="/feeds"
|
||||
text="More feeds"
|
||||
text={_(msg`More feeds`)}
|
||||
style={[pal.link]}
|
||||
/>
|
||||
</View>
|
||||
|
|
|
@ -52,6 +52,7 @@ function ProfileCard() {
|
|||
const {currentAccount} = useSession()
|
||||
const {isLoading, data: profile} = useProfileQuery({did: currentAccount!.did})
|
||||
const {isDesktop} = useWebMediaQueries()
|
||||
const {_} = useLingui()
|
||||
const size = 48
|
||||
|
||||
return !isLoading && profile ? (
|
||||
|
@ -61,7 +62,7 @@ function ProfileCard() {
|
|||
handle: currentAccount!.handle,
|
||||
})}
|
||||
style={[styles.profileCard, !isDesktop && styles.profileCardTablet]}
|
||||
title="My Profile"
|
||||
title={_(msg`My Profile`)}
|
||||
asAnchor>
|
||||
<UserAvatar avatar={profile.avatar} size={size} />
|
||||
</Link>
|
||||
|
@ -269,6 +270,7 @@ function ComposeBtn() {
|
|||
export function DesktopLeftNav() {
|
||||
const {currentAccount} = useSession()
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {isDesktop, isTablet} = useWebMediaQueries()
|
||||
const numUnread = useUnreadNotifications()
|
||||
|
||||
|
@ -292,7 +294,7 @@ export function DesktopLeftNav() {
|
|||
style={pal.text}
|
||||
/>
|
||||
}
|
||||
label="Home"
|
||||
label={_(msg`Home`)}
|
||||
/>
|
||||
<NavItem
|
||||
href="/search"
|
||||
|
@ -310,7 +312,7 @@ export function DesktopLeftNav() {
|
|||
style={pal.text}
|
||||
/>
|
||||
}
|
||||
label="Search"
|
||||
label={_(msg`Search`)}
|
||||
/>
|
||||
<NavItem
|
||||
href="/feeds"
|
||||
|
@ -328,7 +330,7 @@ export function DesktopLeftNav() {
|
|||
size={isDesktop ? 24 : 28}
|
||||
/>
|
||||
}
|
||||
label="Feeds"
|
||||
label={_(msg`Feeds`)}
|
||||
/>
|
||||
<NavItem
|
||||
href="/notifications"
|
||||
|
@ -347,7 +349,7 @@ export function DesktopLeftNav() {
|
|||
style={pal.text}
|
||||
/>
|
||||
}
|
||||
label="Notifications"
|
||||
label={_(msg`Notifications`)}
|
||||
/>
|
||||
<NavItem
|
||||
href="/lists"
|
||||
|
@ -365,7 +367,7 @@ export function DesktopLeftNav() {
|
|||
strokeWidth={3}
|
||||
/>
|
||||
}
|
||||
label="Lists"
|
||||
label={_(msg`Lists`)}
|
||||
/>
|
||||
<NavItem
|
||||
href="/moderation"
|
||||
|
@ -383,7 +385,7 @@ export function DesktopLeftNav() {
|
|||
size={isDesktop ? 20 : 26}
|
||||
/>
|
||||
}
|
||||
label="Moderation"
|
||||
label={_(msg`Moderation`)}
|
||||
/>
|
||||
<NavItem
|
||||
href={currentAccount ? makeProfileLink(currentAccount) : '/'}
|
||||
|
@ -419,7 +421,7 @@ export function DesktopLeftNav() {
|
|||
style={pal.text}
|
||||
/>
|
||||
}
|
||||
label="Settings"
|
||||
label={_(msg`Settings`)}
|
||||
/>
|
||||
<ComposeBtn />
|
||||
</View>
|
||||
|
|
|
@ -12,12 +12,15 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
|||
import {pluralize} from 'lib/strings/helpers'
|
||||
import {formatCount} from 'view/com/util/numeric/format'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useSession} from '#/state/session'
|
||||
import {useInviteCodesQuery} from '#/state/queries/invites'
|
||||
|
||||
export function DesktopRightNav() {
|
||||
const pal = usePalette('default')
|
||||
const palError = usePalette('error')
|
||||
const {_} = useLingui()
|
||||
const {isSandbox, hasSession, currentAccount} = useSession()
|
||||
|
||||
const {isTablet} = useWebMediaQueries()
|
||||
|
@ -45,7 +48,7 @@ export function DesktopRightNav() {
|
|||
email: currentAccount!.email,
|
||||
handle: currentAccount!.handle,
|
||||
})}
|
||||
text="Send feedback"
|
||||
text={_(msg`Feedback`)}
|
||||
/>
|
||||
<Text type="md" style={pal.textLight}>
|
||||
·
|
||||
|
@ -54,7 +57,7 @@ export function DesktopRightNav() {
|
|||
type="md"
|
||||
style={pal.link}
|
||||
href="https://blueskyweb.xyz/support/privacy-policy"
|
||||
text="Privacy"
|
||||
text={_(msg`Privacy`)}
|
||||
/>
|
||||
<Text type="md" style={pal.textLight}>
|
||||
·
|
||||
|
@ -63,7 +66,7 @@ export function DesktopRightNav() {
|
|||
type="md"
|
||||
style={pal.link}
|
||||
href="https://blueskyweb.xyz/support/tos"
|
||||
text="Terms"
|
||||
text={_(msg`Terms`)}
|
||||
/>
|
||||
<Text type="md" style={pal.textLight}>
|
||||
·
|
||||
|
@ -72,7 +75,7 @@ export function DesktopRightNav() {
|
|||
type="md"
|
||||
style={pal.link}
|
||||
href={HELP_DESK_URL}
|
||||
text="Help"
|
||||
text={_(msg`Help`)}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
|
Loading…
Reference in New Issue