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