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:
+11
-15
@@ -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>
|
||||||
|
|||||||
+10
-14
@@ -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
|
||||||
|
|||||||
+22
-3
@@ -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])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
+837
-278
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+837
-278
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+837
-278
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+837
-278
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+1177
-626
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>
|
||||||
|
|||||||
Reference in New Issue
Block a user