Merge remote-tracking branch 'origin/main' into app-1934
* origin/main: (31 commits) Nightly source-language update Fix inverted condition in moveIfNecessary (#10238) Disable viewport zoom in prod (#10233) Fix embed options border radius (#10237) Fix bskyembed skeleton padding (#10236) Fix server state query returning undefined (#10232) handle single-line text truncation (#10227) use dvh for `util_screen_outer` (#10230) Nightly source-language update bskyweb: update to golang v1.26 (#10208) Refactor chat list implementation (#10059) Rename moderation link in settings to "Moderation and content filters" (#10192) Disable keyboard preload on iOS to prevent keyboard flash (#10214) PostControls: restore likeCount testID (#10221) Remove direct dependency on `@expo/config-plugins` (#10152) Tweak embed styles (#10226) [APP-2065] yassify app embed (#10210) Check for undefined moderationOpts (#10224) Remove flex to fix view collapse (#10219) Nightly source-language update ...
This commit is contained in:
+3
-3
@@ -215,11 +215,11 @@ function InnerApp() {
|
||||
}
|
||||
|
||||
function App() {
|
||||
const [isReady, setReady] = useState(false)
|
||||
const [isReady, setIsReady] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
void Promise.all([initPersistedState(), Geo.resolve(), setupDeviceId]).then(
|
||||
() => setReady(true),
|
||||
() => setIsReady(true),
|
||||
)
|
||||
}, [])
|
||||
|
||||
@@ -235,7 +235,7 @@ function App() {
|
||||
<Geo.Provider>
|
||||
<AppConfigProvider>
|
||||
<A11yProvider>
|
||||
<KeyboardControllerProvider>
|
||||
<KeyboardControllerProvider preload={false}>
|
||||
<OnboardingProvider>
|
||||
<AnalyticsContext>
|
||||
<SessionProvider>
|
||||
|
||||
+2
-2
@@ -194,11 +194,11 @@ function InnerApp() {
|
||||
}
|
||||
|
||||
function App() {
|
||||
const [isReady, setReady] = useState(false)
|
||||
const [isReady, setIsReady] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
void Promise.all([initPersistedState(), Geo.resolve(), setupDeviceId]).then(
|
||||
() => setReady(true),
|
||||
() => setIsReady(true),
|
||||
)
|
||||
}, [])
|
||||
|
||||
|
||||
+22
-18
@@ -105,19 +105,18 @@ export function getConfigFromCache():
|
||||
)
|
||||
}
|
||||
let configPrefetchPromise: Promise<void> | undefined
|
||||
export async function prefetchConfig() {
|
||||
export function prefetchConfig() {
|
||||
if (configPrefetchPromise) {
|
||||
logger.debug(`prefetchAgeAssuranceConfig: already in progress`)
|
||||
return
|
||||
}
|
||||
|
||||
configPrefetchPromise = new Promise(async resolve => {
|
||||
configPrefetchPromise = (async () => {
|
||||
await cacheHydrationPromise
|
||||
const cached = getConfigFromCache()
|
||||
|
||||
if (cached) {
|
||||
logger.debug(`prefetchAgeAssuranceConfig: using cache`)
|
||||
resolve()
|
||||
} else {
|
||||
try {
|
||||
logger.debug(`prefetchAgeAssuranceConfig: resolving...`)
|
||||
@@ -126,15 +125,14 @@ export async function prefetchConfig() {
|
||||
configQueryKey,
|
||||
res,
|
||||
)
|
||||
} catch (e: any) {
|
||||
} catch (err) {
|
||||
const e = err as Error
|
||||
logger.warn(`prefetchAgeAssuranceConfig: failed`, {
|
||||
safeMessage: e.message,
|
||||
})
|
||||
} finally {
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
})
|
||||
})()
|
||||
}
|
||||
export async function refetchConfig() {
|
||||
logger.debug(`refetchConfig: fetching...`)
|
||||
@@ -185,7 +183,7 @@ export async function getServerState({agent}: {agent: AtpAgent}) {
|
||||
const geolocation = device.get(['mergedGeolocation'])
|
||||
if (!geolocation || !geolocation.countryCode) {
|
||||
logger.error(`getServerState: missing geolocation countryCode`)
|
||||
return
|
||||
return null
|
||||
}
|
||||
const {data} = await agent.app.bsky.ageassurance.getState({
|
||||
countryCode: geolocation.countryCode,
|
||||
@@ -227,8 +225,11 @@ export async function prefetchServerState({agent}: {agent: AtpAgent}) {
|
||||
try {
|
||||
logger.debug(`prefetchServerState: resolving...`)
|
||||
const res = await networkRetry(3, () => getServerState({agent}))
|
||||
qc.setQueryData<AppBskyAgeassuranceGetState.OutputSchema>(qk, res)
|
||||
} catch (e: any) {
|
||||
if (res) {
|
||||
qc.setQueryData<AppBskyAgeassuranceGetState.OutputSchema>(qk, res)
|
||||
}
|
||||
} catch (err) {
|
||||
const e = err as Error
|
||||
logger.warn(`prefetchServerState: failed`, {
|
||||
safeMessage: e.message,
|
||||
})
|
||||
@@ -239,16 +240,18 @@ export async function refetchServerState({agent}: {agent: AtpAgent}) {
|
||||
if (!did) return
|
||||
logger.debug(`refetchServerState: fetching...`)
|
||||
const res = await networkRetry(3, () => getServerState({agent}))
|
||||
qc.setQueryData<AppBskyAgeassuranceGetState.OutputSchema>(
|
||||
createServerStateQueryKey({did}),
|
||||
res,
|
||||
)
|
||||
if (res) {
|
||||
qc.setQueryData<AppBskyAgeassuranceGetState.OutputSchema>(
|
||||
createServerStateQueryKey({did}),
|
||||
res,
|
||||
)
|
||||
}
|
||||
return res
|
||||
}
|
||||
export function usePatchServerState() {
|
||||
const {currentAccount} = useSession()
|
||||
return useCallback(
|
||||
async (next: AppBskyAgeassuranceDefs.State) => {
|
||||
(next: AppBskyAgeassuranceDefs.State) => {
|
||||
if (!currentAccount) return
|
||||
const did = currentAccount.did
|
||||
const prev = getServerStateFromCache({did})
|
||||
@@ -313,7 +316,7 @@ export function useServerStateQuery() {
|
||||
// only refetch when needed
|
||||
if (isAssured || !isAArequired) return
|
||||
|
||||
refetch()
|
||||
void refetch()
|
||||
})
|
||||
}, [did, refetch, isAssured])
|
||||
|
||||
@@ -409,7 +412,8 @@ export async function prefetchOtherRequiredData({agent}: {agent: AtpAgent}) {
|
||||
logger.debug(`prefetchOtherRequiredData: resolving...`)
|
||||
const res = await networkRetry(3, () => getOtherRequiredData({agent}))
|
||||
qc.setQueryData<OtherRequiredData>(qk, res)
|
||||
} catch (e: any) {
|
||||
} catch (err) {
|
||||
const e = err as Error
|
||||
logger.warn(`prefetchOtherRequiredData: failed`, {
|
||||
safeMessage: e.message,
|
||||
})
|
||||
@@ -418,7 +422,7 @@ export async function prefetchOtherRequiredData({agent}: {agent: AtpAgent}) {
|
||||
export function usePatchOtherRequiredData() {
|
||||
const {currentAccount} = useSession()
|
||||
return useCallback(
|
||||
async (next: OtherRequiredData) => {
|
||||
(next: OtherRequiredData) => {
|
||||
if (!currentAccount) return
|
||||
const did = currentAccount.did
|
||||
const prev = getOtherRequiredDataFromCache({did})
|
||||
|
||||
@@ -85,7 +85,7 @@ function InnerProvider({children}: {children: React.ReactNode}) {
|
||||
|
||||
const handleAccessUpdate = useCallback(
|
||||
(s: AgeAssuranceState) => {
|
||||
getAndRegisterPushToken({
|
||||
void getAndRegisterPushToken({
|
||||
isAgeRestricted: s.access !== AgeAssuranceAccess.Full,
|
||||
})
|
||||
},
|
||||
|
||||
@@ -99,6 +99,7 @@ export function useOnAgeAssuranceAccessUpdate(
|
||||
|
||||
useEffect(() => {
|
||||
if (prevAccess !== state.access) {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||
setPrevAccess(state.access)
|
||||
cb(state)
|
||||
logger.debug(`useOnAgeAssuranceAccessUpdate`, {state})
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ export const atoms = {
|
||||
*/
|
||||
util_screen_outer: [
|
||||
web({
|
||||
minHeight: '100vh',
|
||||
minHeight: '100dvh',
|
||||
}),
|
||||
native({
|
||||
height: '100%',
|
||||
|
||||
@@ -10,6 +10,7 @@ export enum Features {
|
||||
ImportContactsSettingsDisable = 'import_contacts:settings:disable',
|
||||
LiveNowBetaDisable = 'live_now_beta:disable',
|
||||
ImageUploadsHighResolution = 'image_uploads:high_resolution',
|
||||
ImageUploadsBlobSize2mbEnabled = 'image_uploads:blob_size_2mb:enabled',
|
||||
GroupChatsEnable = 'group_chats:enable',
|
||||
DmsNewMessageComposerEnable = 'dms:new_message_composer:enable',
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import Animated, {
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
} from 'react-native-reanimated'
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||
import {useSift, type UseSiftReturn} from '@bsky.app/sift'
|
||||
import {
|
||||
facets,
|
||||
@@ -121,7 +120,6 @@ export function Composer({
|
||||
...rest
|
||||
}: ComposerProps) {
|
||||
const {theme: t, fonts} = useAlf()
|
||||
const insets = useSafeAreaInsets()
|
||||
|
||||
/*
|
||||
* Meat and potatoes
|
||||
@@ -140,7 +138,6 @@ export function Composer({
|
||||
offset: a.p_sm.padding,
|
||||
placement: autocompletePlacement,
|
||||
dynamicWidth: IS_WEB,
|
||||
insets,
|
||||
})
|
||||
|
||||
/*
|
||||
@@ -306,7 +303,13 @@ export function Composer({
|
||||
{IS_WEB && (
|
||||
<View
|
||||
pointerEvents="none"
|
||||
style={[a.absolute, a.inset_0, a.z_10, {overflow: 'hidden'}]}>
|
||||
style={[a.absolute, a.inset_0, a.z_10, {overflow: 'hidden'}]}
|
||||
ref={node => {
|
||||
if (IS_WEB && node) {
|
||||
// @ts-ignore web only a11y
|
||||
node.setAttribute('inert', '')
|
||||
}
|
||||
}}>
|
||||
<Animated.View
|
||||
style={[
|
||||
contentPaddingStyle,
|
||||
@@ -335,6 +338,8 @@ export function Composer({
|
||||
web({
|
||||
caretColor: textStyle.color ?? 'black',
|
||||
overscrollBehavior: 'none',
|
||||
scrollbarWidth: 'thin',
|
||||
scrollbarColor: `${t.palette.contrast_200} transparent`,
|
||||
}),
|
||||
]}
|
||||
{...rest}
|
||||
@@ -368,6 +373,7 @@ export function Composer({
|
||||
|
||||
{activeFacet && activeFacet.type !== 'url' && (
|
||||
<AutocompleteInner
|
||||
inverted={autocompletePlacement?.startsWith('top')}
|
||||
sift={sift}
|
||||
activeFacet={activeFacet}
|
||||
onDismiss={() => setActiveFacet(null)}
|
||||
@@ -382,10 +388,12 @@ export function Composer({
|
||||
*/
|
||||
|
||||
function AutocompleteInner({
|
||||
inverted,
|
||||
sift,
|
||||
activeFacet,
|
||||
onDismiss,
|
||||
}: {
|
||||
inverted?: boolean
|
||||
sift: UseSiftReturn
|
||||
activeFacet: TapperActiveFacet
|
||||
onDismiss: () => void
|
||||
@@ -410,7 +418,7 @@ function AutocompleteInner({
|
||||
|
||||
return items && items.length ? (
|
||||
<AutocompleteBase
|
||||
inverted={!IS_WEB}
|
||||
inverted={inverted}
|
||||
sift={sift}
|
||||
data={items}
|
||||
render={props => {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import {type StyleProp, View, type ViewStyle} from 'react-native'
|
||||
import {
|
||||
GlassView as ExpoGlassView,
|
||||
type GlassViewProps as ExpoGlassViewProps,
|
||||
isGlassEffectAPIAvailable,
|
||||
isLiquidGlassAvailable,
|
||||
} from 'expo-glass-effect'
|
||||
|
||||
import {useTheme} from '#/alf'
|
||||
|
||||
export const IS_GLASS_AVAILABLE =
|
||||
isLiquidGlassAvailable() && isGlassEffectAPIAvailable()
|
||||
|
||||
/**
|
||||
* Liquid Glass View that uses `expo-glass-effect`
|
||||
*
|
||||
* If unavailable, falls back to a regular `View`. Use `fallbackStyle` to customize the fallback appearance.
|
||||
*/
|
||||
export const GlassView = IS_GLASS_AVAILABLE ? InnerGlassView : FallbackView
|
||||
|
||||
export type GlassViewProps = ExpoGlassViewProps & {
|
||||
fallbackStyle?: StyleProp<ViewStyle>
|
||||
}
|
||||
|
||||
function InnerGlassView({
|
||||
fallbackStyle: _fallbackStyle,
|
||||
...props
|
||||
}: GlassViewProps) {
|
||||
const t = useTheme()
|
||||
return <ExpoGlassView colorScheme={t.scheme} {...props} />
|
||||
}
|
||||
|
||||
function FallbackView({fallbackStyle, style, ...props}: GlassViewProps) {
|
||||
return <View style={[fallbackStyle, style]} {...props} />
|
||||
}
|
||||
@@ -22,7 +22,13 @@ import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import type * as bsky from '#/types/bsky'
|
||||
|
||||
export function RecentChats({postUri}: {postUri: string}) {
|
||||
export function RecentChats({
|
||||
postUri,
|
||||
onBeforePress,
|
||||
}: {
|
||||
postUri: string
|
||||
onBeforePress?: () => void
|
||||
}) {
|
||||
const ax = useAnalytics()
|
||||
const control = useDialogContext()
|
||||
const {currentAccount} = useSession()
|
||||
@@ -32,6 +38,7 @@ export function RecentChats({postUri}: {postUri: string}) {
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
|
||||
const onSelectChat = (convoId: string) => {
|
||||
onBeforePress?.()
|
||||
control.close(() => {
|
||||
ax.metric('share:press:recentDm', {})
|
||||
navigation.navigate('MessagesConversation', {
|
||||
|
||||
@@ -5,12 +5,14 @@ import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {type NavigationProp} from '#/lib/routes/types'
|
||||
import {shareText, shareUrl} from '#/lib/sharing'
|
||||
import {toShareUrl} from '#/lib/strings/url-helpers'
|
||||
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
||||
import {precachePost} from '#/state/queries/post'
|
||||
import {useSession} from '#/state/session'
|
||||
import {atoms as a} from '#/alf'
|
||||
import {Admonition} from '#/components/Admonition'
|
||||
@@ -40,6 +42,7 @@ let ShareMenuItems = ({
|
||||
const sendViaChatControl = useDialogControl()
|
||||
const [devModeEnabled] = useDevMode()
|
||||
const aa = useAgeAssurance()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const postUri = post.uri
|
||||
const postAuthor = useProfileShadow(post.author)
|
||||
@@ -77,7 +80,12 @@ let ShareMenuItems = ({
|
||||
onShareProp()
|
||||
}
|
||||
|
||||
const onBeforeShareViaChat = () => {
|
||||
precachePost(queryClient, postUri, post)
|
||||
}
|
||||
|
||||
const onSelectChatToShareTo = (conversation: string) => {
|
||||
onBeforeShareViaChat()
|
||||
navigation.navigate('MessagesConversation', {
|
||||
conversation,
|
||||
embed: postUri,
|
||||
@@ -98,7 +106,10 @@ let ShareMenuItems = ({
|
||||
{hasSession && aa.state.access === aa.Access.Full && (
|
||||
<Menu.Group>
|
||||
<Menu.ContainerItem>
|
||||
<RecentChats postUri={postUri} />
|
||||
<RecentChats
|
||||
postUri={postUri}
|
||||
onBeforePress={onBeforeShareViaChat}
|
||||
/>
|
||||
</Menu.ContainerItem>
|
||||
<Menu.Item
|
||||
testID="postDropdownSendViaDMBtn"
|
||||
|
||||
@@ -303,7 +303,7 @@ let PostControls = ({
|
||||
isToggled={Boolean(post.viewer?.like)}
|
||||
hasBeenToggled={hasLikeIconBeenToggled}
|
||||
renderCount={({count}) => (
|
||||
<PostControlButtonText>
|
||||
<PostControlButtonText testID="likeCount">
|
||||
{formatPostStatCount(count)}
|
||||
</PostControlButtonText>
|
||||
)}
|
||||
|
||||
@@ -109,7 +109,7 @@ function EmbedDialogInner({
|
||||
<Trans>Embed post</Trans>
|
||||
</Text>
|
||||
<Text
|
||||
style={[a.text_md, t.atoms.text_contrast_medium, a.leading_normal]}>
|
||||
style={[a.text_md, t.atoms.text_contrast_medium, a.leading_snug]}>
|
||||
<Trans>
|
||||
Embed this post in your website. Simply copy the following snippet
|
||||
and paste it into the HTML code of your website.
|
||||
@@ -117,12 +117,7 @@ function EmbedDialogInner({
|
||||
</Text>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
a.border,
|
||||
t.atoms.border_contrast_low,
|
||||
a.rounded_sm,
|
||||
a.overflow_hidden,
|
||||
]}>
|
||||
style={[a.border, t.atoms.border_contrast_low, {borderRadius: 18}]}>
|
||||
<Button
|
||||
label={
|
||||
showCustomisation
|
||||
@@ -190,10 +185,9 @@ function EmbedDialogInner({
|
||||
<Button
|
||||
label={_(msg`Copy code`)}
|
||||
color="primary"
|
||||
variant="solid"
|
||||
size="large"
|
||||
onPress={() => {
|
||||
navigator.clipboard.writeText(snippet)
|
||||
void navigator.clipboard.writeText(snippet)
|
||||
setCopied(true)
|
||||
}}>
|
||||
{copied ? (
|
||||
|
||||
@@ -91,7 +91,8 @@ export function ChatEmptyPill() {
|
||||
onPressOut={onPressOut}>
|
||||
<Text
|
||||
style={[a.font_semi_bold, a.pointer_events_none]}
|
||||
selectable={false}>
|
||||
selectable={false}
|
||||
emoji>
|
||||
{prompts[promptIndex]}
|
||||
</Text>
|
||||
</AnimatedPressable>
|
||||
|
||||
@@ -3,3 +3,8 @@ import {createSinglePathSVG} from './TEMPLATE'
|
||||
export const PaperPlane_Stroke2_Corner0_Rounded = createSinglePathSVG({
|
||||
path: 'M3.374 3.22a1 1 0 0 1 1.073-.114l16 8a1 1 0 0 1 0 1.788l-16 8a1 1 0 0 1-1.417-1.136L4.97 12 3.03 4.243a1 1 0 0 1 .344-1.023ZM6.781 13l-1.284 5.133L17.764 12 5.497 5.867 6.781 11H9a1 1 0 1 1 0 2H6.78Z',
|
||||
})
|
||||
|
||||
export const PaperPlaneVertical_Filled_Stroke2_Corner1_Rounded =
|
||||
createSinglePathSVG({
|
||||
path: 'M10.655 3.718c.55-1.116 2.14-1.116 2.69 0l7.548 15.317c.578 1.172-.515 2.471-1.768 2.103L13 19.336V15a1 1 0 0 0-2 0v4.336l-6.124 1.802c-1.254.369-2.346-.93-1.769-2.103l7.548-15.317Z',
|
||||
})
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import {useCallback} from 'react'
|
||||
import {useCallback, useMemo} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {Image} from 'expo-image'
|
||||
import {type AppBskyActorDefs, type AppBskyEmbedExternal} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
import {
|
||||
type AppBskyActorDefs,
|
||||
type AppBskyEmbedExternal,
|
||||
moderateStatus,
|
||||
} from '@atproto/api'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
@@ -19,8 +21,10 @@ import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfoIcon} from '#/components/icons/CircleInfo'
|
||||
import {Globe_Stroke2_Corner0_Rounded} from '#/components/icons/Globe'
|
||||
import {Image_Stroke2_Corner0_Rounded as ImageIcon} from '#/components/icons/Image'
|
||||
import {SquareArrowTopRight_Stroke2_Corner0_Rounded as SquareArrowTopRightIcon} from '#/components/icons/SquareArrowTopRight'
|
||||
import {createStaticClick, SimpleInlineLinkText} from '#/components/Link'
|
||||
import * as Hider from '#/components/moderation/Hider'
|
||||
import {useGlobalReportDialogControl} from '#/components/moderation/ReportDialog'
|
||||
import * as ProfileCard from '#/components/ProfileCard'
|
||||
import {Text} from '#/components/Typography'
|
||||
@@ -64,7 +68,7 @@ function DialogInner({
|
||||
navigation: NavigationProp
|
||||
status: AppBskyActorDefs.StatusView
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
const {t: l} = useLingui()
|
||||
const control = Dialog.useDialogContext()
|
||||
|
||||
const onPressOpenProfile = useCallback(() => {
|
||||
@@ -77,7 +81,7 @@ function DialogInner({
|
||||
|
||||
return (
|
||||
<Dialog.ScrollableInner
|
||||
label={_(msg`${sanitizeHandle(profile.handle)} is live`)}
|
||||
label={l`${sanitizeHandle(profile.handle)} is live`}
|
||||
contentContainerStyle={[a.pt_0, a.px_0]}
|
||||
style={[web({maxWidth: 420}), a.overflow_hidden]}>
|
||||
<LiveStatus
|
||||
@@ -105,45 +109,56 @@ export function LiveStatus({
|
||||
onPressOpenProfile: () => void
|
||||
}) {
|
||||
const ax = useAnalytics()
|
||||
const {_} = useLingui()
|
||||
const {t: l} = useLingui()
|
||||
const t = useTheme()
|
||||
const queryClient = useQueryClient()
|
||||
const openLink = useOpenLink()
|
||||
const moderationOpts = useModerationOpts()
|
||||
const reportDialogControl = useGlobalReportDialogControl()
|
||||
const dialogContext = Dialog.useDialogContext()
|
||||
const moderation = useMemo(() => {
|
||||
if (!moderationOpts) return undefined
|
||||
return moderateStatus(profile, moderationOpts)
|
||||
}, [profile, moderationOpts])
|
||||
|
||||
return (
|
||||
<>
|
||||
{embed.external.thumb && (
|
||||
<View
|
||||
style={[
|
||||
t.atoms.bg_contrast_25,
|
||||
a.w_full,
|
||||
a.aspect_card,
|
||||
android([
|
||||
a.overflow_hidden,
|
||||
{
|
||||
borderTopLeftRadius: a.rounded_md.borderRadius,
|
||||
borderTopRightRadius: a.rounded_md.borderRadius,
|
||||
},
|
||||
]),
|
||||
]}>
|
||||
<Image
|
||||
source={embed.external.thumb}
|
||||
contentFit="cover"
|
||||
style={[a.absolute, a.inset_0]}
|
||||
accessibilityIgnoresInvertColors
|
||||
/>
|
||||
<LiveIndicator
|
||||
size="large"
|
||||
style={[
|
||||
a.absolute,
|
||||
{top: tokens.space.lg, left: tokens.space.lg},
|
||||
a.align_start,
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
<Hider.Outer modui={moderation?.ui('contentMedia')}>
|
||||
<Hider.Mask>
|
||||
<ModeratedImage />
|
||||
</Hider.Mask>
|
||||
<Hider.Content>
|
||||
<View
|
||||
style={[
|
||||
t.atoms.bg_contrast_25,
|
||||
a.w_full,
|
||||
a.aspect_card,
|
||||
android([
|
||||
a.overflow_hidden,
|
||||
{
|
||||
borderTopLeftRadius: a.rounded_md.borderRadius,
|
||||
borderTopRightRadius: a.rounded_md.borderRadius,
|
||||
},
|
||||
]),
|
||||
]}>
|
||||
<Image
|
||||
source={embed.external.thumb}
|
||||
contentFit="cover"
|
||||
style={[a.absolute, a.inset_0]}
|
||||
accessibilityIgnoresInvertColors
|
||||
/>
|
||||
<LiveIndicator
|
||||
size="large"
|
||||
style={[
|
||||
a.absolute,
|
||||
{top: tokens.space.lg, left: tokens.space.lg},
|
||||
a.align_start,
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
</Hider.Content>
|
||||
</Hider.Outer>
|
||||
)}
|
||||
<View
|
||||
style={[
|
||||
@@ -171,7 +186,7 @@ export function LiveStatus({
|
||||
</View>
|
||||
</View>
|
||||
<Button
|
||||
label={_(msg`Watch now`)}
|
||||
label={l`Watch now`}
|
||||
size={platform({native: 'large', web: 'small'})}
|
||||
color="primary"
|
||||
variant="solid"
|
||||
@@ -200,7 +215,7 @@ export function LiveStatus({
|
||||
/>
|
||||
</View>
|
||||
<Button
|
||||
label={_(msg`Open profile`)}
|
||||
label={l`Open profile`}
|
||||
size="small"
|
||||
color="secondary"
|
||||
variant="solid"
|
||||
@@ -231,7 +246,7 @@ export function LiveStatus({
|
||||
</View>
|
||||
{status && (
|
||||
<SimpleInlineLinkText
|
||||
label={_(msg`Report this livestream`)}
|
||||
label={l`Report this livestream`}
|
||||
{...createStaticClick(() => {
|
||||
function open() {
|
||||
reportDialogControl.open({
|
||||
@@ -256,3 +271,57 @@ export function LiveStatus({
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function ModeratedImage() {
|
||||
const t = useTheme()
|
||||
const {t: l} = useLingui()
|
||||
const hider = Hider.useHider()
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
a.p_lg,
|
||||
a.py_xl,
|
||||
a.align_center,
|
||||
a.justify_center,
|
||||
t.atoms.bg_contrast_25,
|
||||
]}>
|
||||
<View style={[a.align_center, a.gap_sm, {maxWidth: 200}]}>
|
||||
<ImageIcon size="lg" fill={t.atoms.text_contrast_medium.color} />
|
||||
<Text
|
||||
style={[
|
||||
a.italic,
|
||||
a.leading_snug,
|
||||
a.text_center,
|
||||
t.atoms.text_contrast_medium,
|
||||
]}>
|
||||
{hider.meta.allowOverride ? (
|
||||
<Trans comment="Image has been moderated and user has the option of showing it temporarily">
|
||||
Image is hidden due to your moderation settings.
|
||||
</Trans>
|
||||
) : (
|
||||
/*
|
||||
* In practice, if `allowOverride` is false, we won't even allow this
|
||||
* dialog to open. That is handled in
|
||||
* `#/features/liveNow/index.tsx`. But for clarity, I've included
|
||||
* this here.
|
||||
*/
|
||||
<Trans comment="Image has been moderated and is not visible to the user">
|
||||
Image is unavailable.
|
||||
</Trans>
|
||||
)}
|
||||
</Text>
|
||||
|
||||
{hider.meta.allowOverride && (
|
||||
<SimpleInlineLinkText
|
||||
label={l`Show anyway`}
|
||||
{...createStaticClick(() => {
|
||||
hider.setIsContentVisible(true)
|
||||
})}>
|
||||
<Trans>Show anyway</Trans>
|
||||
</SimpleInlineLinkText>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
AppBskyEmbedExternal,
|
||||
AtUri,
|
||||
ComAtprotoRepoPutRecord,
|
||||
moderateStatus,
|
||||
} from '@atproto/api'
|
||||
import {retry} from '@atproto/common-web'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
@@ -21,6 +22,7 @@ import {
|
||||
updateProfileShadow,
|
||||
useMaybeProfileShadow,
|
||||
} from '#/state/cache/profile-shadow'
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
import {useAgent, useSession} from '#/state/session'
|
||||
import {useTickEveryMinute} from '#/state/shell'
|
||||
import {useDialogContext} from '#/components/Dialog'
|
||||
@@ -37,6 +39,13 @@ export const DEFAULT_ALLOWED_DOMAINS = [
|
||||
'bluecast.app',
|
||||
]
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
status: '',
|
||||
isDisabled: false,
|
||||
isActive: false,
|
||||
record: {},
|
||||
} satisfies AppBskyActorDefs.StatusView
|
||||
|
||||
export type LiveNowConfig = {
|
||||
canGoLive: boolean
|
||||
currentAccountAllowedHosts: Set<string>
|
||||
@@ -87,10 +96,24 @@ export function useActorStatus(actor?: bsky.profile.AnyProfileView) {
|
||||
const shadowed = useMaybeProfileShadow(actor)
|
||||
const tick = useTickEveryMinute()
|
||||
const config = useLiveNowConfig()
|
||||
const moderationOpts = useModerationOpts()
|
||||
|
||||
const moderation = useMemo(() => {
|
||||
if (!actor || !('status' in actor && actor.status)) return undefined
|
||||
if (!moderationOpts) return undefined
|
||||
return moderateStatus(actor, moderationOpts)
|
||||
}, [actor, moderationOpts])
|
||||
|
||||
return useMemo(() => {
|
||||
void tick // revalidate every minute
|
||||
|
||||
/*
|
||||
* Do not even allow Live Now to show if filtered for `contentList`.
|
||||
*/
|
||||
if (moderation && moderation.ui('contentList').filter) {
|
||||
return DEFAULT_STATE
|
||||
}
|
||||
|
||||
if (shadowed && 'status' in shadowed && shadowed.status) {
|
||||
const isValid = isStatusValidForViewers(shadowed.status, config)
|
||||
const isDisabled = shadowed.status.isDisabled || false
|
||||
@@ -118,14 +141,9 @@ export function useActorStatus(actor?: bsky.profile.AnyProfileView) {
|
||||
record: shadowed.status.record,
|
||||
} satisfies AppBskyActorDefs.StatusView
|
||||
} else {
|
||||
return {
|
||||
status: '',
|
||||
isDisabled: false,
|
||||
isActive: false,
|
||||
record: {},
|
||||
} satisfies AppBskyActorDefs.StatusView
|
||||
return DEFAULT_STATE
|
||||
}
|
||||
}, [shadowed, config, tick])
|
||||
}, [shadowed, config, tick, moderation])
|
||||
}
|
||||
|
||||
export function isStatusStillActive(timeStr: string | undefined) {
|
||||
|
||||
@@ -53,6 +53,7 @@ interface PostOpts {
|
||||
|
||||
type FeatureFlags = {
|
||||
highResolutionImages?: boolean
|
||||
increasedBlobSizeLimit?: boolean
|
||||
}
|
||||
|
||||
export async function post(
|
||||
@@ -320,6 +321,7 @@ async function resolveMedia(
|
||||
logger.debug(`Compressing image #${i}`)
|
||||
const {path, width, height, mime} = await compressImage(image, {
|
||||
highResolution: featureFlags?.highResolutionImages,
|
||||
increasedBlobSizeLimit: featureFlags?.increasedBlobSizeLimit,
|
||||
})
|
||||
logger.debug(`Uploading image #${i}`)
|
||||
const res = await uploadBlob(agent, path, mime)
|
||||
|
||||
@@ -71,5 +71,6 @@ function useKeyboardShortcuts() {
|
||||
scopes: ['global'],
|
||||
preventDefault: true,
|
||||
description: l`Focus the search field`,
|
||||
useKey: true, // Support international and alternate keyboard layouts
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: an\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Aragonese\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ar_SA\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Arabic, Saudi Arabia\n"
|
||||
"Plural-Forms: nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5);\n"
|
||||
@@ -971,7 +971,7 @@ msgstr ""
|
||||
msgid "Add to lists"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/PostControls/BookmarkButton.tsx:142
|
||||
#: src/components/PostControls/BookmarkButton.tsx:144
|
||||
msgid "Add to saved posts"
|
||||
msgstr ""
|
||||
|
||||
@@ -1837,20 +1837,20 @@ msgstr ""
|
||||
msgid "Browse custom feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:627
|
||||
#: src/components/FeedInterstitials.tsx:534
|
||||
msgid "Browse more accounts"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:757
|
||||
#: src/components/FeedInterstitials.tsx:664
|
||||
msgid "Browse more feeds on the Explore page"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:738
|
||||
#: src/components/FeedInterstitials.tsx:741
|
||||
#: src/components/FeedInterstitials.tsx:645
|
||||
#: src/components/FeedInterstitials.tsx:648
|
||||
msgid "Browse more suggestions"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:766
|
||||
#: src/components/FeedInterstitials.tsx:673
|
||||
msgid "Browse more suggestions on the Explore page"
|
||||
msgstr ""
|
||||
|
||||
@@ -1894,7 +1894,7 @@ msgstr ""
|
||||
#: src/components/LabelingServiceCard/index.tsx:62
|
||||
#: src/components/moderation/ReportDialog/index.tsx:853
|
||||
#: src/screens/Search/components/StarterPackCard.tsx:107
|
||||
#: src/screens/Search/Explore.tsx:969
|
||||
#: src/screens/Search/Explore.tsx:970
|
||||
msgid "By {0}"
|
||||
msgstr ""
|
||||
|
||||
@@ -1988,9 +1988,9 @@ msgstr ""
|
||||
msgid "Cancel search"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/PostControls/index.tsx:109
|
||||
#: src/components/PostControls/index.tsx:140
|
||||
#: src/components/PostControls/index.tsx:168
|
||||
#: src/components/PostControls/index.tsx:110
|
||||
#: src/components/PostControls/index.tsx:141
|
||||
#: src/components/PostControls/index.tsx:169
|
||||
#: src/state/shell/composer/index.tsx:107
|
||||
msgid "Cannot interact with a blocked user"
|
||||
msgstr ""
|
||||
@@ -3262,7 +3262,7 @@ msgstr ""
|
||||
msgid "Discover new custom feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:451
|
||||
#: src/screens/Search/Explore.tsx:452
|
||||
msgid "Discover new feeds"
|
||||
msgstr ""
|
||||
|
||||
@@ -3299,7 +3299,7 @@ msgstr ""
|
||||
msgid "Dismiss this section"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:441
|
||||
#: src/components/FeedInterstitials.tsx:348
|
||||
msgid "Dismiss this suggestion"
|
||||
msgstr ""
|
||||
|
||||
@@ -4024,16 +4024,16 @@ msgstr ""
|
||||
msgid "Failed to load conversations"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:528
|
||||
#: src/screens/Search/Explore.tsx:573
|
||||
#: src/screens/Search/Explore.tsx:619
|
||||
#: src/screens/Search/Explore.tsx:529
|
||||
#: src/screens/Search/Explore.tsx:574
|
||||
#: src/screens/Search/Explore.tsx:620
|
||||
msgid "Failed to load feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:487
|
||||
#: src/screens/Search/Explore.tsx:542
|
||||
#: src/screens/Search/Explore.tsx:587
|
||||
#: src/screens/Search/Explore.tsx:633
|
||||
#: src/screens/Search/Explore.tsx:488
|
||||
#: src/screens/Search/Explore.tsx:543
|
||||
#: src/screens/Search/Explore.tsx:588
|
||||
#: src/screens/Search/Explore.tsx:634
|
||||
msgid "Failed to load feeds preferences"
|
||||
msgstr ""
|
||||
|
||||
@@ -4063,14 +4063,14 @@ msgstr ""
|
||||
msgid "Failed to load preference."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:480
|
||||
#: src/screens/Search/Explore.tsx:535
|
||||
#: src/screens/Search/Explore.tsx:580
|
||||
#: src/screens/Search/Explore.tsx:626
|
||||
#: src/screens/Search/Explore.tsx:481
|
||||
#: src/screens/Search/Explore.tsx:536
|
||||
#: src/screens/Search/Explore.tsx:581
|
||||
#: src/screens/Search/Explore.tsx:627
|
||||
msgid "Failed to load suggested feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:390
|
||||
#: src/screens/Search/Explore.tsx:391
|
||||
msgid "Failed to load suggested follows"
|
||||
msgstr ""
|
||||
|
||||
@@ -4132,7 +4132,7 @@ msgstr ""
|
||||
msgid "Failed to save settings. Please try again."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Settings/InterestsSettings.tsx:137
|
||||
#: src/screens/Settings/InterestsSettings.tsx:147
|
||||
msgctxt "toast"
|
||||
msgid "Failed to save your interests."
|
||||
msgstr ""
|
||||
@@ -5716,7 +5716,7 @@ msgstr ""
|
||||
|
||||
#. Accessibility label for the like button when the post has not been liked, verb form followed by number of likes and noun form
|
||||
#. placeholder {0}: post.likeCount || 0
|
||||
#: src/components/PostControls/index.tsx:284
|
||||
#: src/components/PostControls/index.tsx:287
|
||||
msgid "Like ({0, plural, one {# like} other {# likes}})"
|
||||
msgstr ""
|
||||
|
||||
@@ -5935,12 +5935,12 @@ msgstr ""
|
||||
msgid "Live link"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:88
|
||||
#: src/screens/Search/Explore.tsx:90
|
||||
msgid "Load more"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:517
|
||||
#: src/screens/Search/Explore.tsx:608
|
||||
#: src/screens/Search/Explore.tsx:518
|
||||
#: src/screens/Search/Explore.tsx:609
|
||||
msgid "Load more suggested feeds"
|
||||
msgstr ""
|
||||
|
||||
@@ -6117,10 +6117,12 @@ msgstr ""
|
||||
msgid "Message from server: {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:166
|
||||
#: src/screens/Messages/components/MessageInput.tsx:154
|
||||
msgid "Message input field"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:69
|
||||
#: src/screens/Messages/components/MessageInput.tsx:79
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:60
|
||||
msgid "Message is too long"
|
||||
@@ -6689,7 +6691,7 @@ msgid "No results"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: interestsDisplayNames[selectedInterest]
|
||||
#: src/screens/Search/Explore.tsx:826
|
||||
#: src/screens/Search/Explore.tsx:827
|
||||
msgid "No results for \"{0}\"."
|
||||
msgstr ""
|
||||
|
||||
@@ -6706,7 +6708,7 @@ msgstr ""
|
||||
msgid "No results found for “<0>{query}</0>”."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:830
|
||||
#: src/screens/Search/Explore.tsx:831
|
||||
msgid "No results."
|
||||
msgstr ""
|
||||
|
||||
@@ -6985,6 +6987,7 @@ msgstr ""
|
||||
msgid "Open drawer menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:143
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:180
|
||||
#: src/view/com/composer/Composer.tsx:1979
|
||||
msgid "Open emoji picker"
|
||||
@@ -7600,7 +7603,7 @@ msgctxt "action"
|
||||
msgid "Post"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/PostThread/index.tsx:552
|
||||
#: src/screens/PostThread/index.tsx:553
|
||||
msgctxt "description"
|
||||
msgid "Post"
|
||||
msgstr ""
|
||||
@@ -8127,7 +8130,7 @@ msgstr ""
|
||||
msgid "Remove from saved feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/PostControls/BookmarkButton.tsx:141
|
||||
#: src/components/PostControls/BookmarkButton.tsx:143
|
||||
#: src/screens/Bookmarks/index.tsx:262
|
||||
msgid "Remove from saved posts"
|
||||
msgstr ""
|
||||
@@ -8288,7 +8291,7 @@ msgstr ""
|
||||
|
||||
#. Accessibility label for the reply button, verb form followed by number of replies and noun form
|
||||
#. placeholder {0}: post.replyCount || 0
|
||||
#: src/components/PostControls/index.tsx:242
|
||||
#: src/components/PostControls/index.tsx:243
|
||||
msgid "Reply ({0, plural, one {# reply} other {# replies}})"
|
||||
msgstr ""
|
||||
|
||||
@@ -8774,23 +8777,19 @@ msgstr ""
|
||||
msgid "Search for \"{interestsDisplayName}\" (active)"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/components/AutocompleteResults.tsx:47
|
||||
#: src/screens/Search/components/AutocompleteResults.tsx:54
|
||||
msgid "Search for \"{searchText}\""
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/Search.tsx:129
|
||||
msgid "Search for “{tQuery}”"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/StarterPack/Wizard/index.tsx:552
|
||||
msgid "Search for feeds that you want to suggest to others."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:377
|
||||
#: src/screens/Search/Explore.tsx:378
|
||||
msgid "Search for more accounts"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:454
|
||||
#: src/screens/Search/Explore.tsx:455
|
||||
msgid "Search for more feeds"
|
||||
msgstr ""
|
||||
|
||||
@@ -8879,12 +8878,12 @@ msgstr ""
|
||||
msgid "See jobs at Bluesky"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:583
|
||||
#: src/components/FeedInterstitials.tsx:641
|
||||
#: src/components/FeedInterstitials.tsx:490
|
||||
#: src/components/FeedInterstitials.tsx:548
|
||||
msgid "See more"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:564
|
||||
#: src/components/FeedInterstitials.tsx:471
|
||||
msgid "See more suggested profiles"
|
||||
msgstr ""
|
||||
|
||||
@@ -9052,7 +9051,7 @@ msgid "Select your date of birth"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Onboarding/StepInterests/index.tsx:69
|
||||
#: src/screens/Settings/InterestsSettings.tsx:168
|
||||
#: src/screens/Settings/InterestsSettings.tsx:178
|
||||
msgid "Select your interests from the options below"
|
||||
msgstr ""
|
||||
|
||||
@@ -9092,6 +9091,7 @@ msgstr ""
|
||||
msgid "Send feedback"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:216
|
||||
#: src/screens/Messages/components/MessageInput.tsx:194
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:234
|
||||
msgid "Send message"
|
||||
@@ -9580,7 +9580,7 @@ msgstr ""
|
||||
msgid "Some of your verifications are invalid."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:720
|
||||
#: src/components/FeedInterstitials.tsx:627
|
||||
msgid "Some other feeds you might like"
|
||||
msgstr ""
|
||||
|
||||
@@ -9640,7 +9640,7 @@ msgstr ""
|
||||
msgid "Sorry, we're unable to load account suggestions at this time."
|
||||
msgstr ""
|
||||
|
||||
#: src/App.native.tsx:141
|
||||
#: src/App.native.tsx:140
|
||||
#: src/App.web.tsx:119
|
||||
msgid "Sorry! Your session expired. Please sign in again."
|
||||
msgstr ""
|
||||
@@ -9726,7 +9726,7 @@ msgstr ""
|
||||
msgid "Starter pack is invalid"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:663
|
||||
#: src/screens/Search/Explore.tsx:664
|
||||
#: src/view/screens/Profile.tsx:241
|
||||
msgid "Starter Packs"
|
||||
msgstr ""
|
||||
@@ -9842,12 +9842,12 @@ msgstr ""
|
||||
msgid "Suggested"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:374
|
||||
#: src/screens/Search/Explore.tsx:375
|
||||
msgid "Suggested accounts"
|
||||
msgstr ""
|
||||
|
||||
#. Accounts suggested to the user for them to follow
|
||||
#: src/components/FeedInterstitials.tsx:561
|
||||
#: src/components/FeedInterstitials.tsx:468
|
||||
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:180
|
||||
msgid "Suggested for you"
|
||||
msgstr ""
|
||||
@@ -10189,7 +10189,7 @@ msgstr ""
|
||||
msgid "There was an issue fetching notifications. Tap here to try again."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:1025
|
||||
#: src/screens/Search/Explore.tsx:1026
|
||||
#: src/view/com/posts/PostFeed.tsx:773
|
||||
msgid "There was an issue fetching posts. Tap here to try again."
|
||||
msgstr ""
|
||||
@@ -10892,7 +10892,7 @@ msgstr ""
|
||||
|
||||
#. Accessibility label for the like button when the post has been liked, verb followed by number of likes and noun
|
||||
#. placeholder {0}: post.likeCount || 0
|
||||
#: src/components/PostControls/index.tsx:276
|
||||
#: src/components/PostControls/index.tsx:279
|
||||
msgid "Unlike ({0, plural, one {# like} other {# likes}})"
|
||||
msgstr ""
|
||||
|
||||
@@ -11491,7 +11491,7 @@ msgid "View your default post interaction settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/home/HomeHeaderLayout.web.tsx:57
|
||||
#: src/view/com/home/HomeHeaderLayoutMobile.tsx:75
|
||||
#: src/view/com/home/HomeHeaderLayoutMobile.tsx:82
|
||||
msgid "View your feeds and explore more"
|
||||
msgstr ""
|
||||
|
||||
@@ -11561,7 +11561,7 @@ msgid "We apply the highest privacy standards, and never share or sell your cont
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:141
|
||||
msgid "We could not connect to the service that provides this custom feed. It may be temporarily unavailable and experiencing issues, or permanently unavailable."
|
||||
msgid "We could not connect to the service that provides this custom feed. It may be temporarily experiencing issues, or permanently unavailable."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:147
|
||||
@@ -11617,7 +11617,7 @@ msgstr ""
|
||||
msgid "We ran out of posts from your follows. Here's the latest from <0/>."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Settings/InterestsSettings.tsx:161
|
||||
#: src/screens/Settings/InterestsSettings.tsx:171
|
||||
msgid "We recommend selecting at least two interests."
|
||||
msgstr ""
|
||||
|
||||
@@ -11852,6 +11852,7 @@ msgstr ""
|
||||
msgid "Would you like to save this as a draft to edit later?"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:167
|
||||
#: src/screens/Messages/components/MessageInput.tsx:156
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:213
|
||||
msgid "Write a message"
|
||||
@@ -12482,11 +12483,11 @@ msgstr ""
|
||||
#: src/screens/Search/modules/ExploreInterestsCard.tsx:68
|
||||
#: src/screens/Settings/ContentAndMediaSettings.tsx:94
|
||||
#: src/screens/Settings/ContentAndMediaSettings.tsx:97
|
||||
#: src/screens/Settings/InterestsSettings.tsx:47
|
||||
#: src/screens/Settings/InterestsSettings.tsx:49
|
||||
msgid "Your interests"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Settings/InterestsSettings.tsx:128
|
||||
#: src/screens/Settings/InterestsSettings.tsx:138
|
||||
msgctxt "toast"
|
||||
msgid "Your interests have been updated!"
|
||||
msgstr ""
|
||||
@@ -12549,7 +12550,7 @@ msgstr ""
|
||||
msgid "Your report will be sent to <0>{0}</0>."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Settings/InterestsSettings.tsx:61
|
||||
#: src/screens/Settings/InterestsSettings.tsx:63
|
||||
msgid "Your selected interests help us serve you content you care about."
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ast\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Asturian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: az\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Azerbaijani\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -971,7 +971,7 @@ msgstr ""
|
||||
msgid "Add to lists"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/PostControls/BookmarkButton.tsx:142
|
||||
#: src/components/PostControls/BookmarkButton.tsx:144
|
||||
msgid "Add to saved posts"
|
||||
msgstr ""
|
||||
|
||||
@@ -1837,20 +1837,20 @@ msgstr ""
|
||||
msgid "Browse custom feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:627
|
||||
#: src/components/FeedInterstitials.tsx:534
|
||||
msgid "Browse more accounts"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:757
|
||||
#: src/components/FeedInterstitials.tsx:664
|
||||
msgid "Browse more feeds on the Explore page"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:738
|
||||
#: src/components/FeedInterstitials.tsx:741
|
||||
#: src/components/FeedInterstitials.tsx:645
|
||||
#: src/components/FeedInterstitials.tsx:648
|
||||
msgid "Browse more suggestions"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:766
|
||||
#: src/components/FeedInterstitials.tsx:673
|
||||
msgid "Browse more suggestions on the Explore page"
|
||||
msgstr ""
|
||||
|
||||
@@ -1894,7 +1894,7 @@ msgstr ""
|
||||
#: src/components/LabelingServiceCard/index.tsx:62
|
||||
#: src/components/moderation/ReportDialog/index.tsx:853
|
||||
#: src/screens/Search/components/StarterPackCard.tsx:107
|
||||
#: src/screens/Search/Explore.tsx:969
|
||||
#: src/screens/Search/Explore.tsx:970
|
||||
msgid "By {0}"
|
||||
msgstr ""
|
||||
|
||||
@@ -1988,9 +1988,9 @@ msgstr ""
|
||||
msgid "Cancel search"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/PostControls/index.tsx:109
|
||||
#: src/components/PostControls/index.tsx:140
|
||||
#: src/components/PostControls/index.tsx:168
|
||||
#: src/components/PostControls/index.tsx:110
|
||||
#: src/components/PostControls/index.tsx:141
|
||||
#: src/components/PostControls/index.tsx:169
|
||||
#: src/state/shell/composer/index.tsx:107
|
||||
msgid "Cannot interact with a blocked user"
|
||||
msgstr ""
|
||||
@@ -3262,7 +3262,7 @@ msgstr ""
|
||||
msgid "Discover new custom feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:451
|
||||
#: src/screens/Search/Explore.tsx:452
|
||||
msgid "Discover new feeds"
|
||||
msgstr ""
|
||||
|
||||
@@ -3299,7 +3299,7 @@ msgstr ""
|
||||
msgid "Dismiss this section"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:441
|
||||
#: src/components/FeedInterstitials.tsx:348
|
||||
msgid "Dismiss this suggestion"
|
||||
msgstr ""
|
||||
|
||||
@@ -4024,16 +4024,16 @@ msgstr ""
|
||||
msgid "Failed to load conversations"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:528
|
||||
#: src/screens/Search/Explore.tsx:573
|
||||
#: src/screens/Search/Explore.tsx:619
|
||||
#: src/screens/Search/Explore.tsx:529
|
||||
#: src/screens/Search/Explore.tsx:574
|
||||
#: src/screens/Search/Explore.tsx:620
|
||||
msgid "Failed to load feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:487
|
||||
#: src/screens/Search/Explore.tsx:542
|
||||
#: src/screens/Search/Explore.tsx:587
|
||||
#: src/screens/Search/Explore.tsx:633
|
||||
#: src/screens/Search/Explore.tsx:488
|
||||
#: src/screens/Search/Explore.tsx:543
|
||||
#: src/screens/Search/Explore.tsx:588
|
||||
#: src/screens/Search/Explore.tsx:634
|
||||
msgid "Failed to load feeds preferences"
|
||||
msgstr ""
|
||||
|
||||
@@ -4063,14 +4063,14 @@ msgstr ""
|
||||
msgid "Failed to load preference."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:480
|
||||
#: src/screens/Search/Explore.tsx:535
|
||||
#: src/screens/Search/Explore.tsx:580
|
||||
#: src/screens/Search/Explore.tsx:626
|
||||
#: src/screens/Search/Explore.tsx:481
|
||||
#: src/screens/Search/Explore.tsx:536
|
||||
#: src/screens/Search/Explore.tsx:581
|
||||
#: src/screens/Search/Explore.tsx:627
|
||||
msgid "Failed to load suggested feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:390
|
||||
#: src/screens/Search/Explore.tsx:391
|
||||
msgid "Failed to load suggested follows"
|
||||
msgstr ""
|
||||
|
||||
@@ -4132,7 +4132,7 @@ msgstr ""
|
||||
msgid "Failed to save settings. Please try again."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Settings/InterestsSettings.tsx:137
|
||||
#: src/screens/Settings/InterestsSettings.tsx:147
|
||||
msgctxt "toast"
|
||||
msgid "Failed to save your interests."
|
||||
msgstr ""
|
||||
@@ -5716,7 +5716,7 @@ msgstr ""
|
||||
|
||||
#. Accessibility label for the like button when the post has not been liked, verb form followed by number of likes and noun form
|
||||
#. placeholder {0}: post.likeCount || 0
|
||||
#: src/components/PostControls/index.tsx:284
|
||||
#: src/components/PostControls/index.tsx:287
|
||||
msgid "Like ({0, plural, one {# like} other {# likes}})"
|
||||
msgstr ""
|
||||
|
||||
@@ -5935,12 +5935,12 @@ msgstr ""
|
||||
msgid "Live link"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:88
|
||||
#: src/screens/Search/Explore.tsx:90
|
||||
msgid "Load more"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:517
|
||||
#: src/screens/Search/Explore.tsx:608
|
||||
#: src/screens/Search/Explore.tsx:518
|
||||
#: src/screens/Search/Explore.tsx:609
|
||||
msgid "Load more suggested feeds"
|
||||
msgstr ""
|
||||
|
||||
@@ -6117,10 +6117,12 @@ msgstr ""
|
||||
msgid "Message from server: {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:166
|
||||
#: src/screens/Messages/components/MessageInput.tsx:154
|
||||
msgid "Message input field"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:69
|
||||
#: src/screens/Messages/components/MessageInput.tsx:79
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:60
|
||||
msgid "Message is too long"
|
||||
@@ -6689,7 +6691,7 @@ msgid "No results"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: interestsDisplayNames[selectedInterest]
|
||||
#: src/screens/Search/Explore.tsx:826
|
||||
#: src/screens/Search/Explore.tsx:827
|
||||
msgid "No results for \"{0}\"."
|
||||
msgstr ""
|
||||
|
||||
@@ -6706,7 +6708,7 @@ msgstr ""
|
||||
msgid "No results found for “<0>{query}</0>”."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:830
|
||||
#: src/screens/Search/Explore.tsx:831
|
||||
msgid "No results."
|
||||
msgstr ""
|
||||
|
||||
@@ -6985,6 +6987,7 @@ msgstr ""
|
||||
msgid "Open drawer menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:143
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:180
|
||||
#: src/view/com/composer/Composer.tsx:1979
|
||||
msgid "Open emoji picker"
|
||||
@@ -7600,7 +7603,7 @@ msgctxt "action"
|
||||
msgid "Post"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/PostThread/index.tsx:552
|
||||
#: src/screens/PostThread/index.tsx:553
|
||||
msgctxt "description"
|
||||
msgid "Post"
|
||||
msgstr ""
|
||||
@@ -8127,7 +8130,7 @@ msgstr ""
|
||||
msgid "Remove from saved feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/PostControls/BookmarkButton.tsx:141
|
||||
#: src/components/PostControls/BookmarkButton.tsx:143
|
||||
#: src/screens/Bookmarks/index.tsx:262
|
||||
msgid "Remove from saved posts"
|
||||
msgstr ""
|
||||
@@ -8288,7 +8291,7 @@ msgstr ""
|
||||
|
||||
#. Accessibility label for the reply button, verb form followed by number of replies and noun form
|
||||
#. placeholder {0}: post.replyCount || 0
|
||||
#: src/components/PostControls/index.tsx:242
|
||||
#: src/components/PostControls/index.tsx:243
|
||||
msgid "Reply ({0, plural, one {# reply} other {# replies}})"
|
||||
msgstr ""
|
||||
|
||||
@@ -8774,23 +8777,19 @@ msgstr ""
|
||||
msgid "Search for \"{interestsDisplayName}\" (active)"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/components/AutocompleteResults.tsx:47
|
||||
#: src/screens/Search/components/AutocompleteResults.tsx:54
|
||||
msgid "Search for \"{searchText}\""
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/Search.tsx:129
|
||||
msgid "Search for “{tQuery}”"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/StarterPack/Wizard/index.tsx:552
|
||||
msgid "Search for feeds that you want to suggest to others."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:377
|
||||
#: src/screens/Search/Explore.tsx:378
|
||||
msgid "Search for more accounts"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:454
|
||||
#: src/screens/Search/Explore.tsx:455
|
||||
msgid "Search for more feeds"
|
||||
msgstr ""
|
||||
|
||||
@@ -8879,12 +8878,12 @@ msgstr ""
|
||||
msgid "See jobs at Bluesky"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:583
|
||||
#: src/components/FeedInterstitials.tsx:641
|
||||
#: src/components/FeedInterstitials.tsx:490
|
||||
#: src/components/FeedInterstitials.tsx:548
|
||||
msgid "See more"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:564
|
||||
#: src/components/FeedInterstitials.tsx:471
|
||||
msgid "See more suggested profiles"
|
||||
msgstr ""
|
||||
|
||||
@@ -9052,7 +9051,7 @@ msgid "Select your date of birth"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Onboarding/StepInterests/index.tsx:69
|
||||
#: src/screens/Settings/InterestsSettings.tsx:168
|
||||
#: src/screens/Settings/InterestsSettings.tsx:178
|
||||
msgid "Select your interests from the options below"
|
||||
msgstr ""
|
||||
|
||||
@@ -9092,6 +9091,7 @@ msgstr ""
|
||||
msgid "Send feedback"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:216
|
||||
#: src/screens/Messages/components/MessageInput.tsx:194
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:234
|
||||
msgid "Send message"
|
||||
@@ -9580,7 +9580,7 @@ msgstr ""
|
||||
msgid "Some of your verifications are invalid."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:720
|
||||
#: src/components/FeedInterstitials.tsx:627
|
||||
msgid "Some other feeds you might like"
|
||||
msgstr ""
|
||||
|
||||
@@ -9640,7 +9640,7 @@ msgstr ""
|
||||
msgid "Sorry, we're unable to load account suggestions at this time."
|
||||
msgstr ""
|
||||
|
||||
#: src/App.native.tsx:141
|
||||
#: src/App.native.tsx:140
|
||||
#: src/App.web.tsx:119
|
||||
msgid "Sorry! Your session expired. Please sign in again."
|
||||
msgstr ""
|
||||
@@ -9726,7 +9726,7 @@ msgstr ""
|
||||
msgid "Starter pack is invalid"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:663
|
||||
#: src/screens/Search/Explore.tsx:664
|
||||
#: src/view/screens/Profile.tsx:241
|
||||
msgid "Starter Packs"
|
||||
msgstr ""
|
||||
@@ -9842,12 +9842,12 @@ msgstr ""
|
||||
msgid "Suggested"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:374
|
||||
#: src/screens/Search/Explore.tsx:375
|
||||
msgid "Suggested accounts"
|
||||
msgstr ""
|
||||
|
||||
#. Accounts suggested to the user for them to follow
|
||||
#: src/components/FeedInterstitials.tsx:561
|
||||
#: src/components/FeedInterstitials.tsx:468
|
||||
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:180
|
||||
msgid "Suggested for you"
|
||||
msgstr ""
|
||||
@@ -10189,7 +10189,7 @@ msgstr ""
|
||||
msgid "There was an issue fetching notifications. Tap here to try again."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:1025
|
||||
#: src/screens/Search/Explore.tsx:1026
|
||||
#: src/view/com/posts/PostFeed.tsx:773
|
||||
msgid "There was an issue fetching posts. Tap here to try again."
|
||||
msgstr ""
|
||||
@@ -10892,7 +10892,7 @@ msgstr ""
|
||||
|
||||
#. Accessibility label for the like button when the post has been liked, verb followed by number of likes and noun
|
||||
#. placeholder {0}: post.likeCount || 0
|
||||
#: src/components/PostControls/index.tsx:276
|
||||
#: src/components/PostControls/index.tsx:279
|
||||
msgid "Unlike ({0, plural, one {# like} other {# likes}})"
|
||||
msgstr ""
|
||||
|
||||
@@ -11491,7 +11491,7 @@ msgid "View your default post interaction settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/home/HomeHeaderLayout.web.tsx:57
|
||||
#: src/view/com/home/HomeHeaderLayoutMobile.tsx:75
|
||||
#: src/view/com/home/HomeHeaderLayoutMobile.tsx:82
|
||||
msgid "View your feeds and explore more"
|
||||
msgstr ""
|
||||
|
||||
@@ -11561,7 +11561,7 @@ msgid "We apply the highest privacy standards, and never share or sell your cont
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:141
|
||||
msgid "We could not connect to the service that provides this custom feed. It may be temporarily unavailable and experiencing issues, or permanently unavailable."
|
||||
msgid "We could not connect to the service that provides this custom feed. It may be temporarily experiencing issues, or permanently unavailable."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:147
|
||||
@@ -11617,7 +11617,7 @@ msgstr ""
|
||||
msgid "We ran out of posts from your follows. Here's the latest from <0/>."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Settings/InterestsSettings.tsx:161
|
||||
#: src/screens/Settings/InterestsSettings.tsx:171
|
||||
msgid "We recommend selecting at least two interests."
|
||||
msgstr ""
|
||||
|
||||
@@ -11852,6 +11852,7 @@ msgstr ""
|
||||
msgid "Would you like to save this as a draft to edit later?"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:167
|
||||
#: src/screens/Messages/components/MessageInput.tsx:156
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:213
|
||||
msgid "Write a message"
|
||||
@@ -12482,11 +12483,11 @@ msgstr ""
|
||||
#: src/screens/Search/modules/ExploreInterestsCard.tsx:68
|
||||
#: src/screens/Settings/ContentAndMediaSettings.tsx:94
|
||||
#: src/screens/Settings/ContentAndMediaSettings.tsx:97
|
||||
#: src/screens/Settings/InterestsSettings.tsx:47
|
||||
#: src/screens/Settings/InterestsSettings.tsx:49
|
||||
msgid "Your interests"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Settings/InterestsSettings.tsx:128
|
||||
#: src/screens/Settings/InterestsSettings.tsx:138
|
||||
msgctxt "toast"
|
||||
msgid "Your interests have been updated!"
|
||||
msgstr ""
|
||||
@@ -12549,7 +12550,7 @@ msgstr ""
|
||||
msgid "Your report will be sent to <0>{0}</0>."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Settings/InterestsSettings.tsx:61
|
||||
#: src/screens/Settings/InterestsSettings.tsx:63
|
||||
msgid "Your selected interests help us serve you content you care about."
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: bn\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Bengali\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -971,7 +971,7 @@ msgstr ""
|
||||
msgid "Add to lists"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/PostControls/BookmarkButton.tsx:142
|
||||
#: src/components/PostControls/BookmarkButton.tsx:144
|
||||
msgid "Add to saved posts"
|
||||
msgstr ""
|
||||
|
||||
@@ -1837,20 +1837,20 @@ msgstr ""
|
||||
msgid "Browse custom feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:627
|
||||
#: src/components/FeedInterstitials.tsx:534
|
||||
msgid "Browse more accounts"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:757
|
||||
#: src/components/FeedInterstitials.tsx:664
|
||||
msgid "Browse more feeds on the Explore page"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:738
|
||||
#: src/components/FeedInterstitials.tsx:741
|
||||
#: src/components/FeedInterstitials.tsx:645
|
||||
#: src/components/FeedInterstitials.tsx:648
|
||||
msgid "Browse more suggestions"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:766
|
||||
#: src/components/FeedInterstitials.tsx:673
|
||||
msgid "Browse more suggestions on the Explore page"
|
||||
msgstr ""
|
||||
|
||||
@@ -1894,7 +1894,7 @@ msgstr ""
|
||||
#: src/components/LabelingServiceCard/index.tsx:62
|
||||
#: src/components/moderation/ReportDialog/index.tsx:853
|
||||
#: src/screens/Search/components/StarterPackCard.tsx:107
|
||||
#: src/screens/Search/Explore.tsx:969
|
||||
#: src/screens/Search/Explore.tsx:970
|
||||
msgid "By {0}"
|
||||
msgstr ""
|
||||
|
||||
@@ -1988,9 +1988,9 @@ msgstr ""
|
||||
msgid "Cancel search"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/PostControls/index.tsx:109
|
||||
#: src/components/PostControls/index.tsx:140
|
||||
#: src/components/PostControls/index.tsx:168
|
||||
#: src/components/PostControls/index.tsx:110
|
||||
#: src/components/PostControls/index.tsx:141
|
||||
#: src/components/PostControls/index.tsx:169
|
||||
#: src/state/shell/composer/index.tsx:107
|
||||
msgid "Cannot interact with a blocked user"
|
||||
msgstr ""
|
||||
@@ -3262,7 +3262,7 @@ msgstr ""
|
||||
msgid "Discover new custom feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:451
|
||||
#: src/screens/Search/Explore.tsx:452
|
||||
msgid "Discover new feeds"
|
||||
msgstr ""
|
||||
|
||||
@@ -3299,7 +3299,7 @@ msgstr ""
|
||||
msgid "Dismiss this section"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:441
|
||||
#: src/components/FeedInterstitials.tsx:348
|
||||
msgid "Dismiss this suggestion"
|
||||
msgstr ""
|
||||
|
||||
@@ -4024,16 +4024,16 @@ msgstr ""
|
||||
msgid "Failed to load conversations"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:528
|
||||
#: src/screens/Search/Explore.tsx:573
|
||||
#: src/screens/Search/Explore.tsx:619
|
||||
#: src/screens/Search/Explore.tsx:529
|
||||
#: src/screens/Search/Explore.tsx:574
|
||||
#: src/screens/Search/Explore.tsx:620
|
||||
msgid "Failed to load feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:487
|
||||
#: src/screens/Search/Explore.tsx:542
|
||||
#: src/screens/Search/Explore.tsx:587
|
||||
#: src/screens/Search/Explore.tsx:633
|
||||
#: src/screens/Search/Explore.tsx:488
|
||||
#: src/screens/Search/Explore.tsx:543
|
||||
#: src/screens/Search/Explore.tsx:588
|
||||
#: src/screens/Search/Explore.tsx:634
|
||||
msgid "Failed to load feeds preferences"
|
||||
msgstr ""
|
||||
|
||||
@@ -4063,14 +4063,14 @@ msgstr ""
|
||||
msgid "Failed to load preference."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:480
|
||||
#: src/screens/Search/Explore.tsx:535
|
||||
#: src/screens/Search/Explore.tsx:580
|
||||
#: src/screens/Search/Explore.tsx:626
|
||||
#: src/screens/Search/Explore.tsx:481
|
||||
#: src/screens/Search/Explore.tsx:536
|
||||
#: src/screens/Search/Explore.tsx:581
|
||||
#: src/screens/Search/Explore.tsx:627
|
||||
msgid "Failed to load suggested feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:390
|
||||
#: src/screens/Search/Explore.tsx:391
|
||||
msgid "Failed to load suggested follows"
|
||||
msgstr ""
|
||||
|
||||
@@ -4132,7 +4132,7 @@ msgstr ""
|
||||
msgid "Failed to save settings. Please try again."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Settings/InterestsSettings.tsx:137
|
||||
#: src/screens/Settings/InterestsSettings.tsx:147
|
||||
msgctxt "toast"
|
||||
msgid "Failed to save your interests."
|
||||
msgstr ""
|
||||
@@ -5716,7 +5716,7 @@ msgstr ""
|
||||
|
||||
#. Accessibility label for the like button when the post has not been liked, verb form followed by number of likes and noun form
|
||||
#. placeholder {0}: post.likeCount || 0
|
||||
#: src/components/PostControls/index.tsx:284
|
||||
#: src/components/PostControls/index.tsx:287
|
||||
msgid "Like ({0, plural, one {# like} other {# likes}})"
|
||||
msgstr ""
|
||||
|
||||
@@ -5935,12 +5935,12 @@ msgstr ""
|
||||
msgid "Live link"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:88
|
||||
#: src/screens/Search/Explore.tsx:90
|
||||
msgid "Load more"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:517
|
||||
#: src/screens/Search/Explore.tsx:608
|
||||
#: src/screens/Search/Explore.tsx:518
|
||||
#: src/screens/Search/Explore.tsx:609
|
||||
msgid "Load more suggested feeds"
|
||||
msgstr ""
|
||||
|
||||
@@ -6117,10 +6117,12 @@ msgstr ""
|
||||
msgid "Message from server: {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:166
|
||||
#: src/screens/Messages/components/MessageInput.tsx:154
|
||||
msgid "Message input field"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:69
|
||||
#: src/screens/Messages/components/MessageInput.tsx:79
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:60
|
||||
msgid "Message is too long"
|
||||
@@ -6689,7 +6691,7 @@ msgid "No results"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: interestsDisplayNames[selectedInterest]
|
||||
#: src/screens/Search/Explore.tsx:826
|
||||
#: src/screens/Search/Explore.tsx:827
|
||||
msgid "No results for \"{0}\"."
|
||||
msgstr ""
|
||||
|
||||
@@ -6706,7 +6708,7 @@ msgstr ""
|
||||
msgid "No results found for “<0>{query}</0>”."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:830
|
||||
#: src/screens/Search/Explore.tsx:831
|
||||
msgid "No results."
|
||||
msgstr ""
|
||||
|
||||
@@ -6985,6 +6987,7 @@ msgstr ""
|
||||
msgid "Open drawer menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:143
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:180
|
||||
#: src/view/com/composer/Composer.tsx:1979
|
||||
msgid "Open emoji picker"
|
||||
@@ -7600,7 +7603,7 @@ msgctxt "action"
|
||||
msgid "Post"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/PostThread/index.tsx:552
|
||||
#: src/screens/PostThread/index.tsx:553
|
||||
msgctxt "description"
|
||||
msgid "Post"
|
||||
msgstr ""
|
||||
@@ -8127,7 +8130,7 @@ msgstr ""
|
||||
msgid "Remove from saved feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/PostControls/BookmarkButton.tsx:141
|
||||
#: src/components/PostControls/BookmarkButton.tsx:143
|
||||
#: src/screens/Bookmarks/index.tsx:262
|
||||
msgid "Remove from saved posts"
|
||||
msgstr ""
|
||||
@@ -8288,7 +8291,7 @@ msgstr ""
|
||||
|
||||
#. Accessibility label for the reply button, verb form followed by number of replies and noun form
|
||||
#. placeholder {0}: post.replyCount || 0
|
||||
#: src/components/PostControls/index.tsx:242
|
||||
#: src/components/PostControls/index.tsx:243
|
||||
msgid "Reply ({0, plural, one {# reply} other {# replies}})"
|
||||
msgstr ""
|
||||
|
||||
@@ -8774,23 +8777,19 @@ msgstr ""
|
||||
msgid "Search for \"{interestsDisplayName}\" (active)"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/components/AutocompleteResults.tsx:47
|
||||
#: src/screens/Search/components/AutocompleteResults.tsx:54
|
||||
msgid "Search for \"{searchText}\""
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/Search.tsx:129
|
||||
msgid "Search for “{tQuery}”"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/StarterPack/Wizard/index.tsx:552
|
||||
msgid "Search for feeds that you want to suggest to others."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:377
|
||||
#: src/screens/Search/Explore.tsx:378
|
||||
msgid "Search for more accounts"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:454
|
||||
#: src/screens/Search/Explore.tsx:455
|
||||
msgid "Search for more feeds"
|
||||
msgstr ""
|
||||
|
||||
@@ -8879,12 +8878,12 @@ msgstr ""
|
||||
msgid "See jobs at Bluesky"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:583
|
||||
#: src/components/FeedInterstitials.tsx:641
|
||||
#: src/components/FeedInterstitials.tsx:490
|
||||
#: src/components/FeedInterstitials.tsx:548
|
||||
msgid "See more"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:564
|
||||
#: src/components/FeedInterstitials.tsx:471
|
||||
msgid "See more suggested profiles"
|
||||
msgstr ""
|
||||
|
||||
@@ -9052,7 +9051,7 @@ msgid "Select your date of birth"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Onboarding/StepInterests/index.tsx:69
|
||||
#: src/screens/Settings/InterestsSettings.tsx:168
|
||||
#: src/screens/Settings/InterestsSettings.tsx:178
|
||||
msgid "Select your interests from the options below"
|
||||
msgstr ""
|
||||
|
||||
@@ -9092,6 +9091,7 @@ msgstr ""
|
||||
msgid "Send feedback"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:216
|
||||
#: src/screens/Messages/components/MessageInput.tsx:194
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:234
|
||||
msgid "Send message"
|
||||
@@ -9580,7 +9580,7 @@ msgstr ""
|
||||
msgid "Some of your verifications are invalid."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:720
|
||||
#: src/components/FeedInterstitials.tsx:627
|
||||
msgid "Some other feeds you might like"
|
||||
msgstr ""
|
||||
|
||||
@@ -9640,7 +9640,7 @@ msgstr ""
|
||||
msgid "Sorry, we're unable to load account suggestions at this time."
|
||||
msgstr ""
|
||||
|
||||
#: src/App.native.tsx:141
|
||||
#: src/App.native.tsx:140
|
||||
#: src/App.web.tsx:119
|
||||
msgid "Sorry! Your session expired. Please sign in again."
|
||||
msgstr ""
|
||||
@@ -9726,7 +9726,7 @@ msgstr ""
|
||||
msgid "Starter pack is invalid"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:663
|
||||
#: src/screens/Search/Explore.tsx:664
|
||||
#: src/view/screens/Profile.tsx:241
|
||||
msgid "Starter Packs"
|
||||
msgstr ""
|
||||
@@ -9842,12 +9842,12 @@ msgstr ""
|
||||
msgid "Suggested"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:374
|
||||
#: src/screens/Search/Explore.tsx:375
|
||||
msgid "Suggested accounts"
|
||||
msgstr ""
|
||||
|
||||
#. Accounts suggested to the user for them to follow
|
||||
#: src/components/FeedInterstitials.tsx:561
|
||||
#: src/components/FeedInterstitials.tsx:468
|
||||
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:180
|
||||
msgid "Suggested for you"
|
||||
msgstr ""
|
||||
@@ -10189,7 +10189,7 @@ msgstr ""
|
||||
msgid "There was an issue fetching notifications. Tap here to try again."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:1025
|
||||
#: src/screens/Search/Explore.tsx:1026
|
||||
#: src/view/com/posts/PostFeed.tsx:773
|
||||
msgid "There was an issue fetching posts. Tap here to try again."
|
||||
msgstr ""
|
||||
@@ -10892,7 +10892,7 @@ msgstr ""
|
||||
|
||||
#. Accessibility label for the like button when the post has been liked, verb followed by number of likes and noun
|
||||
#. placeholder {0}: post.likeCount || 0
|
||||
#: src/components/PostControls/index.tsx:276
|
||||
#: src/components/PostControls/index.tsx:279
|
||||
msgid "Unlike ({0, plural, one {# like} other {# likes}})"
|
||||
msgstr ""
|
||||
|
||||
@@ -11491,7 +11491,7 @@ msgid "View your default post interaction settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/home/HomeHeaderLayout.web.tsx:57
|
||||
#: src/view/com/home/HomeHeaderLayoutMobile.tsx:75
|
||||
#: src/view/com/home/HomeHeaderLayoutMobile.tsx:82
|
||||
msgid "View your feeds and explore more"
|
||||
msgstr ""
|
||||
|
||||
@@ -11561,7 +11561,7 @@ msgid "We apply the highest privacy standards, and never share or sell your cont
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:141
|
||||
msgid "We could not connect to the service that provides this custom feed. It may be temporarily unavailable and experiencing issues, or permanently unavailable."
|
||||
msgid "We could not connect to the service that provides this custom feed. It may be temporarily experiencing issues, or permanently unavailable."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:147
|
||||
@@ -11617,7 +11617,7 @@ msgstr ""
|
||||
msgid "We ran out of posts from your follows. Here's the latest from <0/>."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Settings/InterestsSettings.tsx:161
|
||||
#: src/screens/Settings/InterestsSettings.tsx:171
|
||||
msgid "We recommend selecting at least two interests."
|
||||
msgstr ""
|
||||
|
||||
@@ -11852,6 +11852,7 @@ msgstr ""
|
||||
msgid "Would you like to save this as a draft to edit later?"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:167
|
||||
#: src/screens/Messages/components/MessageInput.tsx:156
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:213
|
||||
msgid "Write a message"
|
||||
@@ -12482,11 +12483,11 @@ msgstr ""
|
||||
#: src/screens/Search/modules/ExploreInterestsCard.tsx:68
|
||||
#: src/screens/Settings/ContentAndMediaSettings.tsx:94
|
||||
#: src/screens/Settings/ContentAndMediaSettings.tsx:97
|
||||
#: src/screens/Settings/InterestsSettings.tsx:47
|
||||
#: src/screens/Settings/InterestsSettings.tsx:49
|
||||
msgid "Your interests"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Settings/InterestsSettings.tsx:128
|
||||
#: src/screens/Settings/InterestsSettings.tsx:138
|
||||
msgctxt "toast"
|
||||
msgid "Your interests have been updated!"
|
||||
msgstr ""
|
||||
@@ -12549,7 +12550,7 @@ msgstr ""
|
||||
msgid "Your report will be sent to <0>{0}</0>."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Settings/InterestsSettings.tsx:61
|
||||
#: src/screens/Settings/InterestsSettings.tsx:63
|
||||
msgid "Your selected interests help us serve you content you care about."
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ca\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Catalan\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: cy\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Welsh\n"
|
||||
"Plural-Forms: nplurals=6; plural=(n == 0) ? 0 : ((n == 1) ? 1 : ((n == 2) ? 2 : ((n == 3) ? 3 : ((n == 6) ? 4 : 5))));\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: da\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Danish\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: de\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: German\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: el\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Greek\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: en_GB\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: English, United Kingdom\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
+230
-212
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: eo\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Esperanto\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: es\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Spanish\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: eu\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Basque\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -11562,7 +11562,7 @@ msgstr "Pribatutasun-estandar gorenak aplikatzen ditugu, eta ez dugu inoiz zure
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:141
|
||||
msgid "We could not connect to the service that provides this custom feed. It may be temporarily experiencing issues, or permanently unavailable."
|
||||
msgstr ""
|
||||
msgstr "Ezin izan dugu pertsonalizatutako feed hau eskaintzen duen zerbitzura konektatu. Baliteke aldi baterako arazoa izatea edo betirako ez erabilgarri egotea."
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:147
|
||||
msgid "We could not find this list. It was probably deleted."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: fi\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Finnish\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: fr\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: French\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: fy\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Frisian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ga\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Irish\n"
|
||||
"Plural-Forms: nplurals=5; plural=(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : 4);\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: gd\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Scottish Gaelic\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n>2 && n<20) ? 2 : 3;\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: gl\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Galician\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: hi\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Hindi\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: hu\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Hungarian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -11564,7 +11564,7 @@ msgstr "Eleget teszünk a legszorosabb adatvédelmi előírásoknak, és soha se
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:141
|
||||
msgid "We could not connect to the service that provides this custom feed. It may be temporarily experiencing issues, or permanently unavailable."
|
||||
msgstr ""
|
||||
msgstr "A hírfolyam szolgáltatójával történő kapcsolódás meghiúsult. Lehetséges, hogy átmenetileg nem elérhető vagy véglegesen megszűnt."
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:147
|
||||
msgid "We could not find this list. It was probably deleted."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ia\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Interlingua\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: id\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Indonesian\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: it\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Italian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -11562,7 +11562,7 @@ msgstr "Applichiamo i più elevati standard relativi alla privacy e non condivid
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:141
|
||||
msgid "We could not connect to the service that provides this custom feed. It may be temporarily experiencing issues, or permanently unavailable."
|
||||
msgstr ""
|
||||
msgstr "Non è stato possibile collegarsi al servizio che fornisce questo feed personalizzato. Potrebbe avere problemi temporanei oppure non essere disponibile in modo permanente."
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:147
|
||||
msgid "We could not find this list. It was probably deleted."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ja\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Japanese\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
@@ -11562,7 +11562,7 @@ msgstr "当社は最高のプライバシー基準を適用し、お客様の連
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:141
|
||||
msgid "We could not connect to the service that provides this custom feed. It may be temporarily experiencing issues, or permanently unavailable."
|
||||
msgstr ""
|
||||
msgstr "このカスタムフィードを提供しているサービスに接続できませんでした。一時的な問題が発生しているか、永久に利用できなくなっているのかもしれません。"
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:147
|
||||
msgid "We could not find this list. It was probably deleted."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: kab\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Kabyle\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -971,7 +971,7 @@ msgstr ""
|
||||
msgid "Add to lists"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/PostControls/BookmarkButton.tsx:142
|
||||
#: src/components/PostControls/BookmarkButton.tsx:144
|
||||
msgid "Add to saved posts"
|
||||
msgstr ""
|
||||
|
||||
@@ -1837,20 +1837,20 @@ msgstr ""
|
||||
msgid "Browse custom feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:627
|
||||
#: src/components/FeedInterstitials.tsx:534
|
||||
msgid "Browse more accounts"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:757
|
||||
#: src/components/FeedInterstitials.tsx:664
|
||||
msgid "Browse more feeds on the Explore page"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:738
|
||||
#: src/components/FeedInterstitials.tsx:741
|
||||
#: src/components/FeedInterstitials.tsx:645
|
||||
#: src/components/FeedInterstitials.tsx:648
|
||||
msgid "Browse more suggestions"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:766
|
||||
#: src/components/FeedInterstitials.tsx:673
|
||||
msgid "Browse more suggestions on the Explore page"
|
||||
msgstr ""
|
||||
|
||||
@@ -1894,7 +1894,7 @@ msgstr ""
|
||||
#: src/components/LabelingServiceCard/index.tsx:62
|
||||
#: src/components/moderation/ReportDialog/index.tsx:853
|
||||
#: src/screens/Search/components/StarterPackCard.tsx:107
|
||||
#: src/screens/Search/Explore.tsx:969
|
||||
#: src/screens/Search/Explore.tsx:970
|
||||
msgid "By {0}"
|
||||
msgstr ""
|
||||
|
||||
@@ -1988,9 +1988,9 @@ msgstr ""
|
||||
msgid "Cancel search"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/PostControls/index.tsx:109
|
||||
#: src/components/PostControls/index.tsx:140
|
||||
#: src/components/PostControls/index.tsx:168
|
||||
#: src/components/PostControls/index.tsx:110
|
||||
#: src/components/PostControls/index.tsx:141
|
||||
#: src/components/PostControls/index.tsx:169
|
||||
#: src/state/shell/composer/index.tsx:107
|
||||
msgid "Cannot interact with a blocked user"
|
||||
msgstr ""
|
||||
@@ -3262,7 +3262,7 @@ msgstr ""
|
||||
msgid "Discover new custom feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:451
|
||||
#: src/screens/Search/Explore.tsx:452
|
||||
msgid "Discover new feeds"
|
||||
msgstr ""
|
||||
|
||||
@@ -3299,7 +3299,7 @@ msgstr ""
|
||||
msgid "Dismiss this section"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:441
|
||||
#: src/components/FeedInterstitials.tsx:348
|
||||
msgid "Dismiss this suggestion"
|
||||
msgstr ""
|
||||
|
||||
@@ -4024,16 +4024,16 @@ msgstr ""
|
||||
msgid "Failed to load conversations"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:528
|
||||
#: src/screens/Search/Explore.tsx:573
|
||||
#: src/screens/Search/Explore.tsx:619
|
||||
#: src/screens/Search/Explore.tsx:529
|
||||
#: src/screens/Search/Explore.tsx:574
|
||||
#: src/screens/Search/Explore.tsx:620
|
||||
msgid "Failed to load feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:487
|
||||
#: src/screens/Search/Explore.tsx:542
|
||||
#: src/screens/Search/Explore.tsx:587
|
||||
#: src/screens/Search/Explore.tsx:633
|
||||
#: src/screens/Search/Explore.tsx:488
|
||||
#: src/screens/Search/Explore.tsx:543
|
||||
#: src/screens/Search/Explore.tsx:588
|
||||
#: src/screens/Search/Explore.tsx:634
|
||||
msgid "Failed to load feeds preferences"
|
||||
msgstr ""
|
||||
|
||||
@@ -4063,14 +4063,14 @@ msgstr ""
|
||||
msgid "Failed to load preference."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:480
|
||||
#: src/screens/Search/Explore.tsx:535
|
||||
#: src/screens/Search/Explore.tsx:580
|
||||
#: src/screens/Search/Explore.tsx:626
|
||||
#: src/screens/Search/Explore.tsx:481
|
||||
#: src/screens/Search/Explore.tsx:536
|
||||
#: src/screens/Search/Explore.tsx:581
|
||||
#: src/screens/Search/Explore.tsx:627
|
||||
msgid "Failed to load suggested feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:390
|
||||
#: src/screens/Search/Explore.tsx:391
|
||||
msgid "Failed to load suggested follows"
|
||||
msgstr ""
|
||||
|
||||
@@ -4132,7 +4132,7 @@ msgstr ""
|
||||
msgid "Failed to save settings. Please try again."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Settings/InterestsSettings.tsx:137
|
||||
#: src/screens/Settings/InterestsSettings.tsx:147
|
||||
msgctxt "toast"
|
||||
msgid "Failed to save your interests."
|
||||
msgstr ""
|
||||
@@ -5716,7 +5716,7 @@ msgstr ""
|
||||
|
||||
#. Accessibility label for the like button when the post has not been liked, verb form followed by number of likes and noun form
|
||||
#. placeholder {0}: post.likeCount || 0
|
||||
#: src/components/PostControls/index.tsx:284
|
||||
#: src/components/PostControls/index.tsx:287
|
||||
msgid "Like ({0, plural, one {# like} other {# likes}})"
|
||||
msgstr ""
|
||||
|
||||
@@ -5935,12 +5935,12 @@ msgstr ""
|
||||
msgid "Live link"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:88
|
||||
#: src/screens/Search/Explore.tsx:90
|
||||
msgid "Load more"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:517
|
||||
#: src/screens/Search/Explore.tsx:608
|
||||
#: src/screens/Search/Explore.tsx:518
|
||||
#: src/screens/Search/Explore.tsx:609
|
||||
msgid "Load more suggested feeds"
|
||||
msgstr ""
|
||||
|
||||
@@ -6117,10 +6117,12 @@ msgstr ""
|
||||
msgid "Message from server: {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:166
|
||||
#: src/screens/Messages/components/MessageInput.tsx:154
|
||||
msgid "Message input field"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:69
|
||||
#: src/screens/Messages/components/MessageInput.tsx:79
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:60
|
||||
msgid "Message is too long"
|
||||
@@ -6689,7 +6691,7 @@ msgid "No results"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: interestsDisplayNames[selectedInterest]
|
||||
#: src/screens/Search/Explore.tsx:826
|
||||
#: src/screens/Search/Explore.tsx:827
|
||||
msgid "No results for \"{0}\"."
|
||||
msgstr ""
|
||||
|
||||
@@ -6706,7 +6708,7 @@ msgstr ""
|
||||
msgid "No results found for “<0>{query}</0>”."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:830
|
||||
#: src/screens/Search/Explore.tsx:831
|
||||
msgid "No results."
|
||||
msgstr ""
|
||||
|
||||
@@ -6985,6 +6987,7 @@ msgstr ""
|
||||
msgid "Open drawer menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:143
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:180
|
||||
#: src/view/com/composer/Composer.tsx:1979
|
||||
msgid "Open emoji picker"
|
||||
@@ -7600,7 +7603,7 @@ msgctxt "action"
|
||||
msgid "Post"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/PostThread/index.tsx:552
|
||||
#: src/screens/PostThread/index.tsx:553
|
||||
msgctxt "description"
|
||||
msgid "Post"
|
||||
msgstr ""
|
||||
@@ -8127,7 +8130,7 @@ msgstr ""
|
||||
msgid "Remove from saved feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/PostControls/BookmarkButton.tsx:141
|
||||
#: src/components/PostControls/BookmarkButton.tsx:143
|
||||
#: src/screens/Bookmarks/index.tsx:262
|
||||
msgid "Remove from saved posts"
|
||||
msgstr ""
|
||||
@@ -8288,7 +8291,7 @@ msgstr ""
|
||||
|
||||
#. Accessibility label for the reply button, verb form followed by number of replies and noun form
|
||||
#. placeholder {0}: post.replyCount || 0
|
||||
#: src/components/PostControls/index.tsx:242
|
||||
#: src/components/PostControls/index.tsx:243
|
||||
msgid "Reply ({0, plural, one {# reply} other {# replies}})"
|
||||
msgstr ""
|
||||
|
||||
@@ -8774,23 +8777,19 @@ msgstr ""
|
||||
msgid "Search for \"{interestsDisplayName}\" (active)"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/components/AutocompleteResults.tsx:47
|
||||
#: src/screens/Search/components/AutocompleteResults.tsx:54
|
||||
msgid "Search for \"{searchText}\""
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/Search.tsx:129
|
||||
msgid "Search for “{tQuery}”"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/StarterPack/Wizard/index.tsx:552
|
||||
msgid "Search for feeds that you want to suggest to others."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:377
|
||||
#: src/screens/Search/Explore.tsx:378
|
||||
msgid "Search for more accounts"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:454
|
||||
#: src/screens/Search/Explore.tsx:455
|
||||
msgid "Search for more feeds"
|
||||
msgstr ""
|
||||
|
||||
@@ -8879,12 +8878,12 @@ msgstr ""
|
||||
msgid "See jobs at Bluesky"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:583
|
||||
#: src/components/FeedInterstitials.tsx:641
|
||||
#: src/components/FeedInterstitials.tsx:490
|
||||
#: src/components/FeedInterstitials.tsx:548
|
||||
msgid "See more"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:564
|
||||
#: src/components/FeedInterstitials.tsx:471
|
||||
msgid "See more suggested profiles"
|
||||
msgstr ""
|
||||
|
||||
@@ -9052,7 +9051,7 @@ msgid "Select your date of birth"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Onboarding/StepInterests/index.tsx:69
|
||||
#: src/screens/Settings/InterestsSettings.tsx:168
|
||||
#: src/screens/Settings/InterestsSettings.tsx:178
|
||||
msgid "Select your interests from the options below"
|
||||
msgstr ""
|
||||
|
||||
@@ -9092,6 +9091,7 @@ msgstr ""
|
||||
msgid "Send feedback"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:216
|
||||
#: src/screens/Messages/components/MessageInput.tsx:194
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:234
|
||||
msgid "Send message"
|
||||
@@ -9580,7 +9580,7 @@ msgstr ""
|
||||
msgid "Some of your verifications are invalid."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:720
|
||||
#: src/components/FeedInterstitials.tsx:627
|
||||
msgid "Some other feeds you might like"
|
||||
msgstr ""
|
||||
|
||||
@@ -9640,7 +9640,7 @@ msgstr ""
|
||||
msgid "Sorry, we're unable to load account suggestions at this time."
|
||||
msgstr ""
|
||||
|
||||
#: src/App.native.tsx:141
|
||||
#: src/App.native.tsx:140
|
||||
#: src/App.web.tsx:119
|
||||
msgid "Sorry! Your session expired. Please sign in again."
|
||||
msgstr ""
|
||||
@@ -9726,7 +9726,7 @@ msgstr ""
|
||||
msgid "Starter pack is invalid"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:663
|
||||
#: src/screens/Search/Explore.tsx:664
|
||||
#: src/view/screens/Profile.tsx:241
|
||||
msgid "Starter Packs"
|
||||
msgstr ""
|
||||
@@ -9842,12 +9842,12 @@ msgstr ""
|
||||
msgid "Suggested"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:374
|
||||
#: src/screens/Search/Explore.tsx:375
|
||||
msgid "Suggested accounts"
|
||||
msgstr ""
|
||||
|
||||
#. Accounts suggested to the user for them to follow
|
||||
#: src/components/FeedInterstitials.tsx:561
|
||||
#: src/components/FeedInterstitials.tsx:468
|
||||
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:180
|
||||
msgid "Suggested for you"
|
||||
msgstr ""
|
||||
@@ -10189,7 +10189,7 @@ msgstr ""
|
||||
msgid "There was an issue fetching notifications. Tap here to try again."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:1025
|
||||
#: src/screens/Search/Explore.tsx:1026
|
||||
#: src/view/com/posts/PostFeed.tsx:773
|
||||
msgid "There was an issue fetching posts. Tap here to try again."
|
||||
msgstr ""
|
||||
@@ -10892,7 +10892,7 @@ msgstr ""
|
||||
|
||||
#. Accessibility label for the like button when the post has been liked, verb followed by number of likes and noun
|
||||
#. placeholder {0}: post.likeCount || 0
|
||||
#: src/components/PostControls/index.tsx:276
|
||||
#: src/components/PostControls/index.tsx:279
|
||||
msgid "Unlike ({0, plural, one {# like} other {# likes}})"
|
||||
msgstr ""
|
||||
|
||||
@@ -11491,7 +11491,7 @@ msgid "View your default post interaction settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/home/HomeHeaderLayout.web.tsx:57
|
||||
#: src/view/com/home/HomeHeaderLayoutMobile.tsx:75
|
||||
#: src/view/com/home/HomeHeaderLayoutMobile.tsx:82
|
||||
msgid "View your feeds and explore more"
|
||||
msgstr ""
|
||||
|
||||
@@ -11561,7 +11561,7 @@ msgid "We apply the highest privacy standards, and never share or sell your cont
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:141
|
||||
msgid "We could not connect to the service that provides this custom feed. It may be temporarily unavailable and experiencing issues, or permanently unavailable."
|
||||
msgid "We could not connect to the service that provides this custom feed. It may be temporarily experiencing issues, or permanently unavailable."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:147
|
||||
@@ -11617,7 +11617,7 @@ msgstr ""
|
||||
msgid "We ran out of posts from your follows. Here's the latest from <0/>."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Settings/InterestsSettings.tsx:161
|
||||
#: src/screens/Settings/InterestsSettings.tsx:171
|
||||
msgid "We recommend selecting at least two interests."
|
||||
msgstr ""
|
||||
|
||||
@@ -11852,6 +11852,7 @@ msgstr ""
|
||||
msgid "Would you like to save this as a draft to edit later?"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:167
|
||||
#: src/screens/Messages/components/MessageInput.tsx:156
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:213
|
||||
msgid "Write a message"
|
||||
@@ -12482,11 +12483,11 @@ msgstr ""
|
||||
#: src/screens/Search/modules/ExploreInterestsCard.tsx:68
|
||||
#: src/screens/Settings/ContentAndMediaSettings.tsx:94
|
||||
#: src/screens/Settings/ContentAndMediaSettings.tsx:97
|
||||
#: src/screens/Settings/InterestsSettings.tsx:47
|
||||
#: src/screens/Settings/InterestsSettings.tsx:49
|
||||
msgid "Your interests"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Settings/InterestsSettings.tsx:128
|
||||
#: src/screens/Settings/InterestsSettings.tsx:138
|
||||
msgctxt "toast"
|
||||
msgid "Your interests have been updated!"
|
||||
msgstr ""
|
||||
@@ -12549,7 +12550,7 @@ msgstr ""
|
||||
msgid "Your report will be sent to <0>{0}</0>."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Settings/InterestsSettings.tsx:61
|
||||
#: src/screens/Settings/InterestsSettings.tsx:63
|
||||
msgid "Your selected interests help us serve you content you care about."
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: km\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Khmer\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ko\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Korean\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
@@ -11562,7 +11562,7 @@ msgstr "Bluesky는 최고 수준의 개인정보 보호 기준을 준수하며,
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:141
|
||||
msgid "We could not connect to the service that provides this custom feed. It may be temporarily experiencing issues, or permanently unavailable."
|
||||
msgstr ""
|
||||
msgstr "이 맞춤 피드를 제공하는 서비스에 연결할 수 없습니다. 일시적인 문제가 발생했거나 서비스가 영구적으로 중단되었을 수 있습니다."
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:147
|
||||
msgid "We could not find this list. It was probably deleted."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: lt\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Lithuanian\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%10==1 && (n%100>19 || n%100<11) ? 0 : (n%10>=2 && n%10<=9) && (n%100>19 || n%100<11) ? 1 : n%1!=0 ? 2: 3);\n"
|
||||
@@ -971,7 +971,7 @@ msgstr ""
|
||||
msgid "Add to lists"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/PostControls/BookmarkButton.tsx:142
|
||||
#: src/components/PostControls/BookmarkButton.tsx:144
|
||||
msgid "Add to saved posts"
|
||||
msgstr ""
|
||||
|
||||
@@ -1837,20 +1837,20 @@ msgstr ""
|
||||
msgid "Browse custom feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:627
|
||||
#: src/components/FeedInterstitials.tsx:534
|
||||
msgid "Browse more accounts"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:757
|
||||
#: src/components/FeedInterstitials.tsx:664
|
||||
msgid "Browse more feeds on the Explore page"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:738
|
||||
#: src/components/FeedInterstitials.tsx:741
|
||||
#: src/components/FeedInterstitials.tsx:645
|
||||
#: src/components/FeedInterstitials.tsx:648
|
||||
msgid "Browse more suggestions"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:766
|
||||
#: src/components/FeedInterstitials.tsx:673
|
||||
msgid "Browse more suggestions on the Explore page"
|
||||
msgstr ""
|
||||
|
||||
@@ -1894,7 +1894,7 @@ msgstr ""
|
||||
#: src/components/LabelingServiceCard/index.tsx:62
|
||||
#: src/components/moderation/ReportDialog/index.tsx:853
|
||||
#: src/screens/Search/components/StarterPackCard.tsx:107
|
||||
#: src/screens/Search/Explore.tsx:969
|
||||
#: src/screens/Search/Explore.tsx:970
|
||||
msgid "By {0}"
|
||||
msgstr ""
|
||||
|
||||
@@ -1988,9 +1988,9 @@ msgstr ""
|
||||
msgid "Cancel search"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/PostControls/index.tsx:109
|
||||
#: src/components/PostControls/index.tsx:140
|
||||
#: src/components/PostControls/index.tsx:168
|
||||
#: src/components/PostControls/index.tsx:110
|
||||
#: src/components/PostControls/index.tsx:141
|
||||
#: src/components/PostControls/index.tsx:169
|
||||
#: src/state/shell/composer/index.tsx:107
|
||||
msgid "Cannot interact with a blocked user"
|
||||
msgstr ""
|
||||
@@ -3262,7 +3262,7 @@ msgstr ""
|
||||
msgid "Discover new custom feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:451
|
||||
#: src/screens/Search/Explore.tsx:452
|
||||
msgid "Discover new feeds"
|
||||
msgstr ""
|
||||
|
||||
@@ -3299,7 +3299,7 @@ msgstr ""
|
||||
msgid "Dismiss this section"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:441
|
||||
#: src/components/FeedInterstitials.tsx:348
|
||||
msgid "Dismiss this suggestion"
|
||||
msgstr ""
|
||||
|
||||
@@ -4024,16 +4024,16 @@ msgstr ""
|
||||
msgid "Failed to load conversations"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:528
|
||||
#: src/screens/Search/Explore.tsx:573
|
||||
#: src/screens/Search/Explore.tsx:619
|
||||
#: src/screens/Search/Explore.tsx:529
|
||||
#: src/screens/Search/Explore.tsx:574
|
||||
#: src/screens/Search/Explore.tsx:620
|
||||
msgid "Failed to load feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:487
|
||||
#: src/screens/Search/Explore.tsx:542
|
||||
#: src/screens/Search/Explore.tsx:587
|
||||
#: src/screens/Search/Explore.tsx:633
|
||||
#: src/screens/Search/Explore.tsx:488
|
||||
#: src/screens/Search/Explore.tsx:543
|
||||
#: src/screens/Search/Explore.tsx:588
|
||||
#: src/screens/Search/Explore.tsx:634
|
||||
msgid "Failed to load feeds preferences"
|
||||
msgstr ""
|
||||
|
||||
@@ -4063,14 +4063,14 @@ msgstr ""
|
||||
msgid "Failed to load preference."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:480
|
||||
#: src/screens/Search/Explore.tsx:535
|
||||
#: src/screens/Search/Explore.tsx:580
|
||||
#: src/screens/Search/Explore.tsx:626
|
||||
#: src/screens/Search/Explore.tsx:481
|
||||
#: src/screens/Search/Explore.tsx:536
|
||||
#: src/screens/Search/Explore.tsx:581
|
||||
#: src/screens/Search/Explore.tsx:627
|
||||
msgid "Failed to load suggested feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:390
|
||||
#: src/screens/Search/Explore.tsx:391
|
||||
msgid "Failed to load suggested follows"
|
||||
msgstr ""
|
||||
|
||||
@@ -4132,7 +4132,7 @@ msgstr ""
|
||||
msgid "Failed to save settings. Please try again."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Settings/InterestsSettings.tsx:137
|
||||
#: src/screens/Settings/InterestsSettings.tsx:147
|
||||
msgctxt "toast"
|
||||
msgid "Failed to save your interests."
|
||||
msgstr ""
|
||||
@@ -5716,7 +5716,7 @@ msgstr ""
|
||||
|
||||
#. Accessibility label for the like button when the post has not been liked, verb form followed by number of likes and noun form
|
||||
#. placeholder {0}: post.likeCount || 0
|
||||
#: src/components/PostControls/index.tsx:284
|
||||
#: src/components/PostControls/index.tsx:287
|
||||
msgid "Like ({0, plural, one {# like} other {# likes}})"
|
||||
msgstr ""
|
||||
|
||||
@@ -5935,12 +5935,12 @@ msgstr ""
|
||||
msgid "Live link"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:88
|
||||
#: src/screens/Search/Explore.tsx:90
|
||||
msgid "Load more"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:517
|
||||
#: src/screens/Search/Explore.tsx:608
|
||||
#: src/screens/Search/Explore.tsx:518
|
||||
#: src/screens/Search/Explore.tsx:609
|
||||
msgid "Load more suggested feeds"
|
||||
msgstr ""
|
||||
|
||||
@@ -6117,10 +6117,12 @@ msgstr ""
|
||||
msgid "Message from server: {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:166
|
||||
#: src/screens/Messages/components/MessageInput.tsx:154
|
||||
msgid "Message input field"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:69
|
||||
#: src/screens/Messages/components/MessageInput.tsx:79
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:60
|
||||
msgid "Message is too long"
|
||||
@@ -6689,7 +6691,7 @@ msgid "No results"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: interestsDisplayNames[selectedInterest]
|
||||
#: src/screens/Search/Explore.tsx:826
|
||||
#: src/screens/Search/Explore.tsx:827
|
||||
msgid "No results for \"{0}\"."
|
||||
msgstr ""
|
||||
|
||||
@@ -6706,7 +6708,7 @@ msgstr ""
|
||||
msgid "No results found for “<0>{query}</0>”."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:830
|
||||
#: src/screens/Search/Explore.tsx:831
|
||||
msgid "No results."
|
||||
msgstr ""
|
||||
|
||||
@@ -6985,6 +6987,7 @@ msgstr ""
|
||||
msgid "Open drawer menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:143
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:180
|
||||
#: src/view/com/composer/Composer.tsx:1979
|
||||
msgid "Open emoji picker"
|
||||
@@ -7600,7 +7603,7 @@ msgctxt "action"
|
||||
msgid "Post"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/PostThread/index.tsx:552
|
||||
#: src/screens/PostThread/index.tsx:553
|
||||
msgctxt "description"
|
||||
msgid "Post"
|
||||
msgstr ""
|
||||
@@ -8127,7 +8130,7 @@ msgstr ""
|
||||
msgid "Remove from saved feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/PostControls/BookmarkButton.tsx:141
|
||||
#: src/components/PostControls/BookmarkButton.tsx:143
|
||||
#: src/screens/Bookmarks/index.tsx:262
|
||||
msgid "Remove from saved posts"
|
||||
msgstr ""
|
||||
@@ -8288,7 +8291,7 @@ msgstr ""
|
||||
|
||||
#. Accessibility label for the reply button, verb form followed by number of replies and noun form
|
||||
#. placeholder {0}: post.replyCount || 0
|
||||
#: src/components/PostControls/index.tsx:242
|
||||
#: src/components/PostControls/index.tsx:243
|
||||
msgid "Reply ({0, plural, one {# reply} other {# replies}})"
|
||||
msgstr ""
|
||||
|
||||
@@ -8774,23 +8777,19 @@ msgstr ""
|
||||
msgid "Search for \"{interestsDisplayName}\" (active)"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/components/AutocompleteResults.tsx:47
|
||||
#: src/screens/Search/components/AutocompleteResults.tsx:54
|
||||
msgid "Search for \"{searchText}\""
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/Search.tsx:129
|
||||
msgid "Search for “{tQuery}”"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/StarterPack/Wizard/index.tsx:552
|
||||
msgid "Search for feeds that you want to suggest to others."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:377
|
||||
#: src/screens/Search/Explore.tsx:378
|
||||
msgid "Search for more accounts"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:454
|
||||
#: src/screens/Search/Explore.tsx:455
|
||||
msgid "Search for more feeds"
|
||||
msgstr ""
|
||||
|
||||
@@ -8879,12 +8878,12 @@ msgstr ""
|
||||
msgid "See jobs at Bluesky"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:583
|
||||
#: src/components/FeedInterstitials.tsx:641
|
||||
#: src/components/FeedInterstitials.tsx:490
|
||||
#: src/components/FeedInterstitials.tsx:548
|
||||
msgid "See more"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:564
|
||||
#: src/components/FeedInterstitials.tsx:471
|
||||
msgid "See more suggested profiles"
|
||||
msgstr ""
|
||||
|
||||
@@ -9052,7 +9051,7 @@ msgid "Select your date of birth"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Onboarding/StepInterests/index.tsx:69
|
||||
#: src/screens/Settings/InterestsSettings.tsx:168
|
||||
#: src/screens/Settings/InterestsSettings.tsx:178
|
||||
msgid "Select your interests from the options below"
|
||||
msgstr ""
|
||||
|
||||
@@ -9092,6 +9091,7 @@ msgstr ""
|
||||
msgid "Send feedback"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:216
|
||||
#: src/screens/Messages/components/MessageInput.tsx:194
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:234
|
||||
msgid "Send message"
|
||||
@@ -9580,7 +9580,7 @@ msgstr ""
|
||||
msgid "Some of your verifications are invalid."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:720
|
||||
#: src/components/FeedInterstitials.tsx:627
|
||||
msgid "Some other feeds you might like"
|
||||
msgstr ""
|
||||
|
||||
@@ -9640,7 +9640,7 @@ msgstr ""
|
||||
msgid "Sorry, we're unable to load account suggestions at this time."
|
||||
msgstr ""
|
||||
|
||||
#: src/App.native.tsx:141
|
||||
#: src/App.native.tsx:140
|
||||
#: src/App.web.tsx:119
|
||||
msgid "Sorry! Your session expired. Please sign in again."
|
||||
msgstr ""
|
||||
@@ -9726,7 +9726,7 @@ msgstr ""
|
||||
msgid "Starter pack is invalid"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:663
|
||||
#: src/screens/Search/Explore.tsx:664
|
||||
#: src/view/screens/Profile.tsx:241
|
||||
msgid "Starter Packs"
|
||||
msgstr ""
|
||||
@@ -9842,12 +9842,12 @@ msgstr ""
|
||||
msgid "Suggested"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:374
|
||||
#: src/screens/Search/Explore.tsx:375
|
||||
msgid "Suggested accounts"
|
||||
msgstr ""
|
||||
|
||||
#. Accounts suggested to the user for them to follow
|
||||
#: src/components/FeedInterstitials.tsx:561
|
||||
#: src/components/FeedInterstitials.tsx:468
|
||||
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:180
|
||||
msgid "Suggested for you"
|
||||
msgstr ""
|
||||
@@ -10189,7 +10189,7 @@ msgstr ""
|
||||
msgid "There was an issue fetching notifications. Tap here to try again."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:1025
|
||||
#: src/screens/Search/Explore.tsx:1026
|
||||
#: src/view/com/posts/PostFeed.tsx:773
|
||||
msgid "There was an issue fetching posts. Tap here to try again."
|
||||
msgstr ""
|
||||
@@ -10892,7 +10892,7 @@ msgstr ""
|
||||
|
||||
#. Accessibility label for the like button when the post has been liked, verb followed by number of likes and noun
|
||||
#. placeholder {0}: post.likeCount || 0
|
||||
#: src/components/PostControls/index.tsx:276
|
||||
#: src/components/PostControls/index.tsx:279
|
||||
msgid "Unlike ({0, plural, one {# like} other {# likes}})"
|
||||
msgstr ""
|
||||
|
||||
@@ -11491,7 +11491,7 @@ msgid "View your default post interaction settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/home/HomeHeaderLayout.web.tsx:57
|
||||
#: src/view/com/home/HomeHeaderLayoutMobile.tsx:75
|
||||
#: src/view/com/home/HomeHeaderLayoutMobile.tsx:82
|
||||
msgid "View your feeds and explore more"
|
||||
msgstr ""
|
||||
|
||||
@@ -11561,7 +11561,7 @@ msgid "We apply the highest privacy standards, and never share or sell your cont
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:141
|
||||
msgid "We could not connect to the service that provides this custom feed. It may be temporarily unavailable and experiencing issues, or permanently unavailable."
|
||||
msgid "We could not connect to the service that provides this custom feed. It may be temporarily experiencing issues, or permanently unavailable."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:147
|
||||
@@ -11617,7 +11617,7 @@ msgstr ""
|
||||
msgid "We ran out of posts from your follows. Here's the latest from <0/>."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Settings/InterestsSettings.tsx:161
|
||||
#: src/screens/Settings/InterestsSettings.tsx:171
|
||||
msgid "We recommend selecting at least two interests."
|
||||
msgstr ""
|
||||
|
||||
@@ -11852,6 +11852,7 @@ msgstr ""
|
||||
msgid "Would you like to save this as a draft to edit later?"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:167
|
||||
#: src/screens/Messages/components/MessageInput.tsx:156
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:213
|
||||
msgid "Write a message"
|
||||
@@ -12482,11 +12483,11 @@ msgstr ""
|
||||
#: src/screens/Search/modules/ExploreInterestsCard.tsx:68
|
||||
#: src/screens/Settings/ContentAndMediaSettings.tsx:94
|
||||
#: src/screens/Settings/ContentAndMediaSettings.tsx:97
|
||||
#: src/screens/Settings/InterestsSettings.tsx:47
|
||||
#: src/screens/Settings/InterestsSettings.tsx:49
|
||||
msgid "Your interests"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Settings/InterestsSettings.tsx:128
|
||||
#: src/screens/Settings/InterestsSettings.tsx:138
|
||||
msgctxt "toast"
|
||||
msgid "Your interests have been updated!"
|
||||
msgstr ""
|
||||
@@ -12549,7 +12550,7 @@ msgstr ""
|
||||
msgid "Your report will be sent to <0>{0}</0>."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Settings/InterestsSettings.tsx:61
|
||||
#: src/screens/Settings/InterestsSettings.tsx:63
|
||||
msgid "Your selected interests help us serve you content you care about."
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ne\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Nepali\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: nl\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Dutch\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: pl\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Polish\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: pt\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Portuguese, Brazilian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: pt\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Portuguese\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -11562,7 +11562,7 @@ msgstr "Aplicamos os mais altos padrões de privacidade e nunca compartilhamos o
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:141
|
||||
msgid "We could not connect to the service that provides this custom feed. It may be temporarily experiencing issues, or permanently unavailable."
|
||||
msgstr ""
|
||||
msgstr "Não conseguimos conectar-nos ao serviço que fornece este feed personalizado. Pode estar temporariamente com problemas ou permanentemente indisponível."
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:147
|
||||
msgid "We could not find this list. It was probably deleted."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ro\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Romanian\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100>0 && n%100<20)) ? 1 : 2);\n"
|
||||
@@ -11562,7 +11562,7 @@ msgstr "Aplicăm cele mai înalte standarde de confidențialitate și nu distrib
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:141
|
||||
msgid "We could not connect to the service that provides this custom feed. It may be temporarily experiencing issues, or permanently unavailable."
|
||||
msgstr ""
|
||||
msgstr "Nu ne-am putut conecta la serviciul care oferă acest flux personalizat. Este posibil să întâmpine probleme temporar sau să fie permanent indisponibil."
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:147
|
||||
msgid "We could not find this list. It was probably deleted."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ru\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Russian\n"
|
||||
"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: sv\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-08 01:38\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Swedish\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -11562,7 +11562,7 @@ msgstr "Vi tillämpar de högsta standarderna för integritet och kommer aldrig
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:141
|
||||
msgid "We could not connect to the service that provides this custom feed. It may be temporarily experiencing issues, or permanently unavailable."
|
||||
msgstr ""
|
||||
msgstr "Vi kunde inte ansluta till tjänsten som tillhandahåller det här anpassade flödet. Det kan bero ett tillfälligt fel, eller så är den permanent otillgänglig."
|
||||
|
||||
#: src/view/com/feeds/MissingFeed.tsx:147
|
||||
msgid "We could not find this list. It was probably deleted."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: th\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Thai\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: tr\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Turkish\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: uk\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Ukrainian\n"
|
||||
"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: vi\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Vietnamese\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: zh\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Chinese Simplified\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
@@ -441,13 +441,13 @@ msgstr "{following} 位正在关注"
|
||||
|
||||
#: src/components/dialogs/SearchablePeopleList.tsx:414
|
||||
msgid "{handle} can't be messaged"
|
||||
msgstr "目前无法向 {handle} 发送私信"
|
||||
msgstr "目前无法向 {handle} 发送信息"
|
||||
|
||||
#: src/components/dms/InitiateChatFlow.tsx:805
|
||||
#: src/components/dms/InitiateChatFlow.tsx:858
|
||||
#: src/components/dms/InitiateChatFlow.tsx:897
|
||||
msgid "{handle} can’t be messaged"
|
||||
msgstr "目前无法向 {handle} 发送私信"
|
||||
msgstr "目前无法向 {handle} 发送信息"
|
||||
|
||||
#: src/features/liveNow/utils.ts:15
|
||||
msgid "{hours, plural, one {# hour {minutesString}} other {# hours {minutesString}}}"
|
||||
@@ -659,7 +659,7 @@ msgstr "你可以在 Bluesky 上找到一系列热门动态源,包括 News、B
|
||||
#. If last message does not contain text, fall back to "{user} reacted to {a message}"
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:209
|
||||
msgid "a message"
|
||||
msgstr "一条私信"
|
||||
msgstr "一条信息"
|
||||
|
||||
#: src/components/contacts/screens/PhoneInput.tsx:102
|
||||
msgid "A network error occurred. Please check your internet connection"
|
||||
@@ -715,7 +715,7 @@ msgstr "接受"
|
||||
|
||||
#: src/screens/Messages/components/RequestButtons.tsx:280
|
||||
msgid "Accept chat request"
|
||||
msgstr "接受私信请求"
|
||||
msgstr "接受对话请求"
|
||||
|
||||
#. Accept a chat request
|
||||
#: src/screens/Messages/components/RequestListItem.tsx:42
|
||||
@@ -1102,7 +1102,7 @@ msgstr "以下是你已保存的动态源。"
|
||||
#: src/screens/Settings/components/AddAppPasswordDialog.tsx:146
|
||||
#: src/screens/Settings/components/AddAppPasswordDialog.tsx:153
|
||||
msgid "Allow access to your direct messages"
|
||||
msgstr "允许读取你的私信"
|
||||
msgstr "允许读取你的私人信息"
|
||||
|
||||
#: src/components/dialogs/PostInteractionSettingsDialog.tsx:451
|
||||
msgid "Allow anyone to reply"
|
||||
@@ -1116,7 +1116,7 @@ msgstr "允许读取位置信息"
|
||||
#: src/screens/Messages/Settings.tsx:78
|
||||
#: src/screens/Messages/Settings.tsx:81
|
||||
msgid "Allow new messages from"
|
||||
msgstr "允许以下来源向你发送私信"
|
||||
msgstr "允许以下来源向你发起对话"
|
||||
|
||||
#: src/screens/Settings/ActivityPrivacySettings.tsx:53
|
||||
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:92
|
||||
@@ -1146,7 +1146,7 @@ msgstr "允许你的关注者回复"
|
||||
|
||||
#: src/screens/Settings/AppPasswords.tsx:201
|
||||
msgid "Allows access to direct messages"
|
||||
msgstr "允许读取私信"
|
||||
msgstr "允许读取私人信息"
|
||||
|
||||
#: src/screens/Login/ForgotPasswordForm.tsx:180
|
||||
#: src/screens/Settings/components/ChangePasswordDialog.tsx:237
|
||||
@@ -1290,11 +1290,11 @@ msgstr "不在这些选项中的问题"
|
||||
|
||||
#: src/components/dms/dialogs/NewChatDialog.tsx:41
|
||||
msgid "An issue occurred starting the chat"
|
||||
msgstr "发起私信时出现问题"
|
||||
msgstr "发起对话时出现问题"
|
||||
|
||||
#: src/components/dms/dialogs/ShareViaChatDialog.tsx:50
|
||||
msgid "An issue occurred while trying to open the chat"
|
||||
msgstr "开启私信时出现问题"
|
||||
msgstr "开启对话时出现问题"
|
||||
|
||||
#: src/components/hooks/useFollowMethods.ts:35
|
||||
#: src/components/hooks/useFollowMethods.ts:52
|
||||
@@ -1478,7 +1478,7 @@ msgstr "你确定要删除此应用密码“{0}”吗?"
|
||||
|
||||
#: src/components/dms/MessageContextMenu.tsx:206
|
||||
msgid "Are you sure you want to delete this message? The message will be deleted for you, but not for the other participant."
|
||||
msgstr "你确定要删除这条私信吗?此操作将仅为你删除私信,而不会为其他参与者删除。"
|
||||
msgstr "你确定要删除这条信息吗?此操作将仅为你删除信息,而不会为其他参与者删除。"
|
||||
|
||||
#: src/screens/StarterPack/StarterPackScreen.tsx:686
|
||||
msgid "Are you sure you want to delete this starter pack?"
|
||||
@@ -1587,7 +1587,7 @@ msgstr "返回"
|
||||
|
||||
#: src/screens/Messages/Inbox.tsx:262
|
||||
msgid "Back to Chats"
|
||||
msgstr "返回私信"
|
||||
msgstr "返回对话"
|
||||
|
||||
#: src/components/moderation/ReportDialog/utils/useReportOptions.ts:216
|
||||
msgid "Banned activities or security violations"
|
||||
@@ -1615,7 +1615,7 @@ msgstr "在创建新手包之前,你必须首先验证你的电子邮箱。"
|
||||
|
||||
#: src/screens/Messages/components/RequestButtons.tsx:266
|
||||
msgid "Before you can accept this chat request, you must first verify your email."
|
||||
msgstr "在你接受此私信请求之前,你必须首先验证你的电子邮箱。"
|
||||
msgstr "在你接受此对话邀请之前,你必须首先验证你的电子邮箱。"
|
||||
|
||||
#: src/components/activity-notifications/SubscribeProfileButton.tsx:59
|
||||
msgid "Before you can get notifications for {name}'s posts, you must first verify your email."
|
||||
@@ -2085,37 +2085,37 @@ msgstr "列表在创建后将是一个独立副本,任何对新手包的更改
|
||||
#: src/view/shell/desktop/LeftNav.tsx:611
|
||||
#: src/view/shell/Drawer.tsx:454
|
||||
msgid "Chat"
|
||||
msgstr "私信"
|
||||
msgstr "对话"
|
||||
|
||||
#: src/screens/Messages/components/RequestButtons.tsx:86
|
||||
#: src/screens/Messages/components/RequestButtons.tsx:335
|
||||
msgctxt "toast"
|
||||
msgid "Chat deleted"
|
||||
msgstr "已删除私信"
|
||||
msgstr "已删除对话"
|
||||
|
||||
#: src/lib/hooks/useNotificationHandler.ts:117
|
||||
msgid "Chat messages - silent"
|
||||
msgstr "私信 - 静音"
|
||||
msgstr "对话信息 - 静音"
|
||||
|
||||
#: src/lib/hooks/useNotificationHandler.ts:108
|
||||
msgid "Chat messages - sound"
|
||||
msgstr "私信 - 提示音"
|
||||
msgstr "对话信息 - 提示音"
|
||||
|
||||
#: src/components/dms/ConvoMenu.tsx:202
|
||||
msgctxt "toast"
|
||||
msgid "Chat muted"
|
||||
msgstr "已隐藏私信"
|
||||
msgstr "已隐藏对话"
|
||||
|
||||
#: src/Navigation.tsx:579
|
||||
#: src/screens/Messages/components/InboxPreview.tsx:23
|
||||
msgid "Chat request inbox"
|
||||
msgstr "私信请求收件箱"
|
||||
msgstr "对话邀请收件箱"
|
||||
|
||||
#: src/screens/Messages/components/InboxPreview.tsx:63
|
||||
#: src/screens/Messages/Inbox.tsx:57
|
||||
#: src/screens/Messages/Inbox.tsx:99
|
||||
msgid "Chat requests"
|
||||
msgstr "私信请求"
|
||||
msgstr "对话邀请"
|
||||
|
||||
#: src/components/dms/ConvoMenu.tsx:84
|
||||
#: src/Navigation.tsx:574
|
||||
@@ -2123,22 +2123,22 @@ msgstr "私信请求"
|
||||
#: src/screens/Messages/ChatList.tsx:86
|
||||
#: src/screens/Messages/ChatList.tsx:385
|
||||
msgid "Chat settings"
|
||||
msgstr "私信设置"
|
||||
msgstr "对话设置"
|
||||
|
||||
#: src/screens/Messages/Settings.tsx:70
|
||||
msgid "Chat Settings"
|
||||
msgstr "私信设置"
|
||||
msgstr "对话设置"
|
||||
|
||||
#: src/components/dms/ConvoMenu.tsx:204
|
||||
msgctxt "toast"
|
||||
msgid "Chat unmuted"
|
||||
msgstr "已取消隐藏私信"
|
||||
msgstr "已取消隐藏对话"
|
||||
|
||||
#: src/screens/Messages/ChatList.tsx:77
|
||||
#: src/screens/Messages/ChatList.tsx:401
|
||||
#: src/screens/Messages/ChatList.tsx:425
|
||||
msgid "Chats"
|
||||
msgstr "私信"
|
||||
msgstr "对话"
|
||||
|
||||
#: src/screens/Settings/components/DeleteAccountDialog.tsx:236
|
||||
msgid "Check <0>{currentEmail}</0> for an email with the confirmation code to enter below:"
|
||||
@@ -2267,7 +2267,7 @@ msgstr "点按以开启 {0} 的标签菜单"
|
||||
|
||||
#: src/components/dms/MessageItem.tsx:318
|
||||
msgid "Click to retry failed message"
|
||||
msgstr "点击以重试发送私信"
|
||||
msgstr "点击以重试发送信息"
|
||||
|
||||
#: src/components/dms/ChatEmptyPill.tsx:39
|
||||
msgid "Clip 🐴 clop 🐴"
|
||||
@@ -2726,7 +2726,7 @@ msgstr "复制新手包链接"
|
||||
#: src/components/dms/MessageContextMenu.tsx:150
|
||||
#: src/components/dms/MessageContextMenu.tsx:153
|
||||
msgid "Copy message text"
|
||||
msgstr "复制私信文字"
|
||||
msgstr "复制信息文本"
|
||||
|
||||
#: src/components/PostControls/ShareMenu/ShareMenuItems.web.tsx:145
|
||||
#: src/components/PostControls/ShareMenu/ShareMenuItems.web.tsx:148
|
||||
@@ -2897,7 +2897,7 @@ msgstr "再创建一个"
|
||||
|
||||
#: src/components/dms/InitiateChatFlow.tsx:445
|
||||
msgid "Create group chat"
|
||||
msgstr "创建群组私信"
|
||||
msgstr "创建群组对话"
|
||||
|
||||
#: src/components/dialogs/lists/CreateListFromStarterPackDialog.tsx:180
|
||||
#: src/components/dialogs/lists/CreateListFromStarterPackDialog.tsx:188
|
||||
@@ -3050,7 +3050,7 @@ msgstr "要删除应用密码吗?"
|
||||
#: src/screens/Messages/components/RequestButtons.tsx:349
|
||||
#: src/screens/Messages/components/RequestButtons.tsx:355
|
||||
msgid "Delete chat"
|
||||
msgstr "删除私信"
|
||||
msgstr "删除对话"
|
||||
|
||||
#: src/screens/Settings/Settings.tsx:472
|
||||
msgid "Delete chat declaration record"
|
||||
@@ -3082,11 +3082,11 @@ msgstr "删除列表"
|
||||
|
||||
#: src/components/dms/MessageContextMenu.tsx:204
|
||||
msgid "Delete message"
|
||||
msgstr "删除私信"
|
||||
msgstr "删除信息"
|
||||
|
||||
#: src/components/dms/MessageContextMenu.tsx:162
|
||||
msgid "Delete message for me"
|
||||
msgstr "仅为我删除私信"
|
||||
msgstr "仅为我删除信息"
|
||||
|
||||
#: src/screens/Settings/components/DeleteAccountDialog.tsx:316
|
||||
msgid "Delete my account"
|
||||
@@ -3387,7 +3387,7 @@ msgstr "完成"
|
||||
|
||||
#: src/components/dms/MessageItem.tsx:161
|
||||
msgid "Double tap or long press the message to add a reaction"
|
||||
msgstr "双击或长按私信以添加反应"
|
||||
msgstr "双击或长按信息以添加反应"
|
||||
|
||||
#: src/components/Dialog/index.tsx:402
|
||||
msgid "Double tap to close the dialog"
|
||||
@@ -3409,7 +3409,7 @@ msgstr "下载 CAR 文件"
|
||||
#: src/screens/Settings/components/ExportCarDialog.tsx:136
|
||||
#: src/screens/Settings/components/ExportCarDialog.tsx:141
|
||||
msgid "Download chat data"
|
||||
msgstr "下载私信数据"
|
||||
msgstr "下载对话数据"
|
||||
|
||||
#: src/view/com/lightbox/Lightbox.web.tsx:278
|
||||
#: src/view/com/lightbox/Lightbox.web.tsx:290
|
||||
@@ -3952,7 +3952,7 @@ msgstr "极端主义内容"
|
||||
#: src/screens/Messages/components/RequestButtons.tsx:249
|
||||
msgctxt "toast"
|
||||
msgid "Failed to accept chat"
|
||||
msgstr "无法接受私信请求"
|
||||
msgstr "无法接受对话邀请"
|
||||
|
||||
#: src/components/dms/ActionsWrapper.web.tsx:64
|
||||
#: src/components/dms/MessageContextMenu.tsx:103
|
||||
@@ -3984,11 +3984,11 @@ msgstr "无法创建新手包"
|
||||
#: src/screens/Messages/components/RequestButtons.tsx:320
|
||||
msgctxt "toast"
|
||||
msgid "Failed to delete chat"
|
||||
msgstr "无法删除私信"
|
||||
msgstr "无法删除对话"
|
||||
|
||||
#: src/components/dms/MessageContextMenu.tsx:85
|
||||
msgid "Failed to delete message"
|
||||
msgstr "无法删除私信"
|
||||
msgstr "无法删除信息"
|
||||
|
||||
#: src/components/PostControls/PostMenu/PostMenuItems.tsx:219
|
||||
msgid "Failed to delete post, please try again"
|
||||
@@ -4057,7 +4057,7 @@ msgstr "无法加载通知设置。"
|
||||
|
||||
#: src/screens/Messages/components/MessageListError.tsx:23
|
||||
msgid "Failed to load past messages"
|
||||
msgstr "无法加载过去的私信"
|
||||
msgstr "无法加载之前的信息"
|
||||
|
||||
#: src/screens/Settings/ActivityPrivacySettings.tsx:66
|
||||
msgid "Failed to load preference."
|
||||
@@ -4139,7 +4139,7 @@ msgstr "无法保存你的兴趣。"
|
||||
|
||||
#: src/components/dms/MessageItem.tsx:311
|
||||
msgid "Failed to send"
|
||||
msgstr "无法发送私信"
|
||||
msgstr "无法发送"
|
||||
|
||||
#: src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx:123
|
||||
#: src/components/dialogs/EmailDialog/screens/Verify.tsx:137
|
||||
@@ -6095,22 +6095,22 @@ msgstr "菜单"
|
||||
#. placeholder {0}: profile.handle
|
||||
#: src/components/dms/MessageProfileButton.tsx:99
|
||||
msgid "Message {0}"
|
||||
msgstr "传送私信给 {0}"
|
||||
msgstr "发送信息给 {0}"
|
||||
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:196
|
||||
msgid "Message deleted"
|
||||
msgstr "已删除私信"
|
||||
msgstr "已删除信息"
|
||||
|
||||
#: src/components/dms/MessageContextMenu.tsx:83
|
||||
msgctxt "toast"
|
||||
msgid "Message deleted"
|
||||
msgstr "已删除私信"
|
||||
msgstr "已删除信息"
|
||||
|
||||
#. placeholder {0}: sender?.handle ?? 'unknown'
|
||||
#. placeholder {1}: message.text
|
||||
#: src/components/dms/MessageContextMenu.tsx:131
|
||||
msgid "Message from @{0}: {1}"
|
||||
msgstr "来自 @{0} 的私信:{1}"
|
||||
msgstr "来自 @{0} 的信息:{1}"
|
||||
|
||||
#. placeholder {0}: rawError.message
|
||||
#: src/view/com/posts/PostFeedErrorMessage.tsx:209
|
||||
@@ -6120,21 +6120,21 @@ msgstr "来自服务器的信息:{0}"
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:166
|
||||
#: src/screens/Messages/components/MessageInput.tsx:154
|
||||
msgid "Message input field"
|
||||
msgstr "私信输入框"
|
||||
msgstr "信息输入框"
|
||||
|
||||
#: src/screens/Messages/components/MessageComposer.tsx:69
|
||||
#: src/screens/Messages/components/MessageInput.tsx:79
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:60
|
||||
msgid "Message is too long"
|
||||
msgstr "私信过长"
|
||||
msgstr "信息过长"
|
||||
|
||||
#: src/components/dms/MessageContextMenu.tsx:129
|
||||
msgid "Message options"
|
||||
msgstr "私信选项"
|
||||
msgstr "信息选项"
|
||||
|
||||
#: src/Navigation.tsx:828
|
||||
msgid "Messages"
|
||||
msgstr "私信"
|
||||
msgstr "信息"
|
||||
|
||||
#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:101
|
||||
msgctxt "Name of app icon variant"
|
||||
@@ -6433,7 +6433,7 @@ msgstr "来自 {firstAuthorName} 的新{postsCount, plural, one {帖文} other {
|
||||
#: src/screens/Messages/ChatList.tsx:408
|
||||
#: src/screens/Messages/ChatList.tsx:415
|
||||
msgid "New chat"
|
||||
msgstr "新私信"
|
||||
msgstr "新对话"
|
||||
|
||||
#: src/components/dialogs/EmailDialog/screens/Update.tsx:223
|
||||
msgid "New email address"
|
||||
@@ -6460,11 +6460,11 @@ msgstr "新关注者"
|
||||
#: src/components/dms/InitiateChatFlow.tsx:709
|
||||
#: src/components/dms/InitiateChatFlow.tsx:737
|
||||
msgid "New group chat"
|
||||
msgstr "创建群组私信"
|
||||
msgstr "创建群组对话"
|
||||
|
||||
#: src/components/dms/InitiateChatFlow.tsx:261
|
||||
msgid "New group chat with:"
|
||||
msgstr "与这些人开始群组私信:"
|
||||
msgstr "与这些人开始群组对话:"
|
||||
|
||||
#: src/screens/Settings/components/ChangeHandleDialog.tsx:228
|
||||
#: src/screens/Settings/components/ChangeHandleDialog.tsx:236
|
||||
@@ -6479,7 +6479,7 @@ msgstr "新列表"
|
||||
|
||||
#: src/components/dms/NewMessagesPill.tsx:92
|
||||
msgid "New messages"
|
||||
msgstr "新私信"
|
||||
msgstr "新信息"
|
||||
|
||||
#: src/screens/Login/SetNewPasswordForm.tsx:143
|
||||
#: src/screens/Settings/components/ChangePasswordDialog.tsx:204
|
||||
@@ -6627,7 +6627,7 @@ msgstr "目前还没有媒体内容"
|
||||
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:138
|
||||
msgid "No messages yet"
|
||||
msgstr "目前还没有私信"
|
||||
msgstr "目前还没有信息"
|
||||
|
||||
#: src/screens/Onboarding/StepFinished/ValuePropositionPager.shared.tsx:14
|
||||
msgid "No more doomscrolling junk-filled algorithms. Find feeds that work for you, not against you."
|
||||
@@ -7016,7 +7016,7 @@ msgstr "开启指向 {niceUrl} 的链接"
|
||||
|
||||
#: src/components/dms/ActionsWrapper.tsx:35
|
||||
msgid "Open message options"
|
||||
msgstr "开启私信选项"
|
||||
msgstr "开启信息选项"
|
||||
|
||||
#: src/screens/Settings/Settings.tsx:463
|
||||
msgid "Open moderation debug page"
|
||||
@@ -7240,7 +7240,7 @@ msgstr "我们的审核团队已收到你的举报。"
|
||||
|
||||
#: src/screens/Messages/components/ChatDisabled.tsx:35
|
||||
msgid "Our moderators have reviewed reports and decided to disable your access to chats on Bluesky."
|
||||
msgstr "内容审核服务提供方已收到相关举报,并决定停用你的 Bluesky 私信功能。"
|
||||
msgstr "内容审核服务提供方已收到相关举报,并决定停用你的 Bluesky 对话功能。"
|
||||
|
||||
#: src/components/dialogs/StarterPackDialog.tsx:384
|
||||
msgid "Owner"
|
||||
@@ -7540,7 +7540,7 @@ msgstr "请说明你为什么认为 {0} 错误应用了此标记"
|
||||
|
||||
#: src/screens/Messages/components/ChatDisabled.tsx:125
|
||||
msgid "Please explain why you think your chats were incorrectly disabled"
|
||||
msgstr "请说明你为什么认为你的私信被错误停用"
|
||||
msgstr "请说明你为什么认为你的对话被错误停用"
|
||||
|
||||
#: src/features/liveNow/components/GoLiveDisabledDialog.tsx:124
|
||||
msgid "Please explain why you think your Go Live access was incorrectly disabled."
|
||||
@@ -8037,7 +8037,7 @@ msgstr "拒绝"
|
||||
|
||||
#: src/screens/Messages/components/RequestButtons.tsx:125
|
||||
msgid "Reject chat request"
|
||||
msgstr "拒绝私信请求"
|
||||
msgstr "拒绝对话邀请"
|
||||
|
||||
#: src/screens/Messages/ChatList.tsx:291
|
||||
#: src/screens/Messages/Inbox.tsx:214
|
||||
@@ -8061,7 +8061,7 @@ msgstr "移除"
|
||||
|
||||
#: src/components/dms/ChatProfileTabs.tsx:153
|
||||
msgid "Remove {displayName} from group chat"
|
||||
msgstr "从群组私信中移除 {displayName}"
|
||||
msgstr "从群组对话中移除 {displayName}"
|
||||
|
||||
#: src/components/StarterPack/Wizard/WizardListCard.tsx:62
|
||||
msgid "Remove {displayName} from starter pack"
|
||||
@@ -8363,7 +8363,7 @@ msgstr "举报列表"
|
||||
|
||||
#: src/components/dms/MessageContextMenu.tsx:170
|
||||
msgid "Report message"
|
||||
msgstr "举报私信"
|
||||
msgstr "举报信息"
|
||||
|
||||
#: src/components/PostControls/PostMenu/PostMenuItems.tsx:756
|
||||
#: src/components/PostControls/PostMenu/PostMenuItems.tsx:758
|
||||
@@ -8399,7 +8399,7 @@ msgstr "举报这场直播"
|
||||
|
||||
#: src/components/moderation/ReportDialog/copy.ts:57
|
||||
msgid "Report this message"
|
||||
msgstr "举报这条私信"
|
||||
msgstr "举报这条信息"
|
||||
|
||||
#: src/components/moderation/ReportDialog/copy.ts:25
|
||||
msgid "Report this post"
|
||||
@@ -8985,7 +8985,7 @@ msgstr "选择 GIF “{0}”"
|
||||
|
||||
#: src/components/dms/InitiateChatFlow.tsx:645
|
||||
msgid "Select group chat members"
|
||||
msgstr "选择群组私信成员"
|
||||
msgstr "选择群组对话成员"
|
||||
|
||||
#: src/components/dialogs/MutedWords.tsx:151
|
||||
msgid "Select how long to mute this word for."
|
||||
@@ -9095,7 +9095,7 @@ msgstr "提交反馈"
|
||||
#: src/screens/Messages/components/MessageInput.tsx:194
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:234
|
||||
msgid "Send message"
|
||||
msgstr "发送私信"
|
||||
msgstr "发送信息"
|
||||
|
||||
#: src/components/PostControls/ShareMenu/RecentChats.tsx:121
|
||||
msgid "Send post to {name}"
|
||||
@@ -9103,7 +9103,7 @@ msgstr "发送帖文给 {name}"
|
||||
|
||||
#: src/components/dms/dialogs/ShareViaChatDialog.tsx:65
|
||||
msgid "Send post to..."
|
||||
msgstr "发送私信给……"
|
||||
msgstr "发送帖文给……"
|
||||
|
||||
#: src/components/moderation/ReportDialog/index.tsx:824
|
||||
msgid "Send report to {title}"
|
||||
@@ -9124,7 +9124,7 @@ msgstr "发送验证电子邮件"
|
||||
#: src/components/PostControls/ShareMenu/ShareMenuItems.web.tsx:105
|
||||
#: src/components/PostControls/ShareMenu/ShareMenuItems.web.tsx:111
|
||||
msgid "Send via direct message"
|
||||
msgstr "通过私信发送"
|
||||
msgstr "通过私人信息发送"
|
||||
|
||||
#: src/components/contacts/screens/PhoneInput.tsx:283
|
||||
msgid "Sent to our phone number verification provider Plivo"
|
||||
@@ -9681,7 +9681,7 @@ msgstr "当你和其他人开始聊天后,对话就会出现在这里。"
|
||||
|
||||
#: src/components/dms/dialogs/NewChatDialog.tsx:95
|
||||
msgid "Start a new chat"
|
||||
msgstr "开始一个新私信"
|
||||
msgstr "开始一个新对话"
|
||||
|
||||
#: src/screens/ProfileList/AboutSection.tsx:104
|
||||
#: src/screens/ProfileList/FeedSection.tsx:82
|
||||
@@ -9695,12 +9695,12 @@ msgstr "开始添加用户!"
|
||||
|
||||
#: src/components/dms/InitiateChatFlow.tsx:646
|
||||
msgid "Start chat"
|
||||
msgstr "开始私信"
|
||||
msgstr "开始对话"
|
||||
|
||||
#: src/components/dialogs/SearchablePeopleList.tsx:383
|
||||
#: src/components/dms/InitiateChatFlow.tsx:773
|
||||
msgid "Start chat with {displayName}"
|
||||
msgstr "与 {displayName} 开始私信"
|
||||
msgstr "与 {displayName} 开始对话"
|
||||
|
||||
#: src/Navigation.tsx:599
|
||||
#: src/Navigation.tsx:604
|
||||
@@ -10614,7 +10614,7 @@ msgstr "需要登出请<0>点击此处</0>。或者如果需要的话,你也
|
||||
|
||||
#: src/components/dms/ReportConversationPrompt.tsx:19
|
||||
msgid "To report a conversation, please report one of its messages via the conversation screen. This lets our moderators understand the context of your issue."
|
||||
msgstr "要举报对话,请在会话界面里选择一条私信并提交举报。这将有助于使内容审核服务提供方了解相关问题的前因后果。"
|
||||
msgstr "要举报对话,请在会话界面里选择一条信息并提交举报。这将有助于使内容审核服务提供方了解相关问题的前因后果。"
|
||||
|
||||
#: src/components/dms/DateDivider.tsx:45
|
||||
msgid "Today"
|
||||
@@ -11826,7 +11826,7 @@ msgstr "此直播为何需要受到审核?"
|
||||
|
||||
#: src/components/moderation/ReportDialog/copy.ts:58
|
||||
msgid "Why should this message be reviewed?"
|
||||
msgstr "此私信为何需要受到审核?"
|
||||
msgstr "此信息为何需要受到审核?"
|
||||
|
||||
#: src/components/moderation/ReportDialog/copy.ts:26
|
||||
msgid "Why should this post be reviewed?"
|
||||
@@ -11856,7 +11856,7 @@ msgstr "需要把现有内容保存为草稿,以便日后编辑吗?"
|
||||
#: src/screens/Messages/components/MessageInput.tsx:156
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:213
|
||||
msgid "Write a message"
|
||||
msgstr "撰写私信"
|
||||
msgstr "撰写信息"
|
||||
|
||||
#: src/view/screens/Profile.tsx:436
|
||||
#: src/view/screens/Profile.tsx:437
|
||||
@@ -11992,7 +11992,7 @@ msgstr "你也可以探索并关注新的自定义动态源。"
|
||||
|
||||
#: src/screens/Settings/components/ExportCarDialog.tsx:126
|
||||
msgid "You can also download your chat data as a \"JSONL\" file. This file only includes chat messages that you have sent and does not include chat messages that you have received."
|
||||
msgstr "你可以将你的私信数据导出为一个“JSONL”文件。此文件仅包含你发送的私信,不包含接收到的私信数据。"
|
||||
msgstr "你可以将你的对话数据导出为一个“JSONL”文件。此文件仅包含你发送的对话信息,不包含接收到的对话信息数据。"
|
||||
|
||||
#: src/components/contacts/screens/GetContacts.tsx:250
|
||||
msgid "You can always opt out and delete your data"
|
||||
@@ -12000,7 +12000,7 @@ msgstr "你可以随时选择退出并删除数据"
|
||||
|
||||
#: src/lib/hooks/useNotificationHandler.ts:104
|
||||
msgid "You can choose whether chat notifications have sound in the chat settings within the app"
|
||||
msgstr "你可以自定义应用内的私信通知是否播放提示音"
|
||||
msgstr "你可以自定义应用内的对话通知是否播放提示音"
|
||||
|
||||
#: src/screens/Messages/Settings.tsx:121
|
||||
msgid "You can continue ongoing conversations regardless of which setting you choose."
|
||||
@@ -12057,7 +12057,7 @@ msgstr "你没有关注任何同样关注 @{name} 的用户。"
|
||||
|
||||
#: src/screens/Messages/Inbox.tsx:244
|
||||
msgid "You don't have any chat requests at the moment."
|
||||
msgstr "你目前没有任何私信请求。"
|
||||
msgstr "你目前没有收到对话邀请。"
|
||||
|
||||
#: src/components/dialogs/PostInteractionSettingsDialog.tsx:589
|
||||
msgid "You don't have any lists yet."
|
||||
@@ -12418,7 +12418,7 @@ msgstr "你的浏览器不支持此视频格式,请试试其他浏览器。"
|
||||
|
||||
#: src/screens/Messages/components/ChatDisabled.tsx:32
|
||||
msgid "Your chats have been disabled"
|
||||
msgstr "你的私信功能已被停用"
|
||||
msgstr "你的对话功能已被停用"
|
||||
|
||||
#: src/components/dialogs/InAppBrowserConsent.tsx:70
|
||||
msgid "Your choice will be remembered for future links. You can change it at any time in settings."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: zh\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Chinese Traditional, Hong Kong\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: zh\n"
|
||||
"Project-Id-Version: 49a8cb746fbc2ae5707392ee41ddec4c\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-04-07 21:54\n"
|
||||
"PO-Revision-Date: 2026-04-08 18:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Chinese Traditional\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
@@ -5,16 +5,19 @@ import {
|
||||
moderateProfile,
|
||||
type ModerationDecision,
|
||||
} from '@atproto/api'
|
||||
import {ScrollEdgeEffectProvider} from '@bsky.app/expo-scroll-edge-effect'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
import {
|
||||
type RouteProp,
|
||||
useFocusEffect,
|
||||
useIsFocused,
|
||||
useNavigation,
|
||||
useRoute,
|
||||
} from '@react-navigation/native'
|
||||
import {type NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||
import {RemoveScrollBar} from 'react-remove-scroll-bar'
|
||||
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
import {
|
||||
@@ -30,7 +33,7 @@ import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
import {useProfileQuery} from '#/state/queries/profile'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {MessagesList} from '#/screens/Messages/components/MessagesList'
|
||||
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
|
||||
import {atoms as a, useTheme, web} from '#/alf'
|
||||
import {AgeRestrictedScreen} from '#/components/ageAssurance/AgeRestrictedScreen'
|
||||
import {useAgeAssuranceCopy} from '#/components/ageAssurance/useAgeAssuranceCopy'
|
||||
import {
|
||||
@@ -62,7 +65,6 @@ export function MessagesConversationScreen(props: Props) {
|
||||
}
|
||||
|
||||
export function MessagesConversationScreenInner({route}: Props) {
|
||||
const {gtMobile} = useBreakpoints()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
|
||||
const convoId = route.params.conversation
|
||||
@@ -71,25 +73,22 @@ export function MessagesConversationScreenInner({route}: Props) {
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
setCurrentConvoId(convoId)
|
||||
|
||||
if (IS_WEB && !gtMobile) {
|
||||
setMinimalShellMode(true)
|
||||
} else {
|
||||
setMinimalShellMode(false)
|
||||
}
|
||||
setMinimalShellMode(true)
|
||||
|
||||
return () => {
|
||||
setCurrentConvoId(undefined)
|
||||
setMinimalShellMode(false)
|
||||
}
|
||||
}, [gtMobile, convoId, setCurrentConvoId, setMinimalShellMode]),
|
||||
}, [convoId, setCurrentConvoId, setMinimalShellMode]),
|
||||
)
|
||||
|
||||
return (
|
||||
<Layout.Screen testID="convoScreen" style={web([{minHeight: 0}, a.flex_1])}>
|
||||
<ConvoProvider key={convoId} convoId={convoId}>
|
||||
<Inner />
|
||||
</ConvoProvider>
|
||||
<ScrollEdgeEffectProvider>
|
||||
<ConvoProvider key={convoId} convoId={convoId}>
|
||||
<Inner />
|
||||
</ConvoProvider>
|
||||
</ScrollEdgeEffectProvider>
|
||||
</Layout.Screen>
|
||||
)
|
||||
}
|
||||
@@ -98,6 +97,7 @@ function Inner() {
|
||||
const t = useTheme()
|
||||
const convoState = useConvo()
|
||||
const {_} = useLingui()
|
||||
const isFocused = useIsFocused()
|
||||
|
||||
const moderationOpts = useModerationOpts()
|
||||
const {data: recipientUnshadowed} = useProfileQuery({
|
||||
@@ -122,11 +122,13 @@ function Inner() {
|
||||
|
||||
// Any time that we re-render the `Initializing` state, we have to reset `hasScrolled` to false. After entering this
|
||||
// state, we know that we're resetting the list of messages and need to re-scroll to the bottom when they get added.
|
||||
useEffect(() => {
|
||||
const [prevState, setPrevState] = useState(convoState.status)
|
||||
if (prevState !== convoState.status) {
|
||||
setPrevState(convoState.status)
|
||||
if (convoState.status === ConvoStatus.Initializing) {
|
||||
setHasScrolled(false)
|
||||
}
|
||||
}, [convoState.status])
|
||||
}
|
||||
|
||||
if (convoState.status === ConvoStatus.Error) {
|
||||
return (
|
||||
@@ -150,6 +152,8 @@ function Inner() {
|
||||
|
||||
return (
|
||||
<Layout.Center style={[a.flex_1]}>
|
||||
{/* MessagesList does not use the body scroll */}
|
||||
{isFocused && IS_WEB && <RemoveScrollBar />}
|
||||
{!readyToShow &&
|
||||
(moderation ? (
|
||||
<MessagesListHeader moderation={moderation} profile={recipient} />
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import {Pressable, View} from 'react-native'
|
||||
import {
|
||||
useKeyboardHandler,
|
||||
useReanimatedKeyboardAnimation,
|
||||
} from 'react-native-keyboard-controller'
|
||||
import Animated, {
|
||||
Extrapolation,
|
||||
interpolate,
|
||||
runOnJS,
|
||||
useAnimatedStyle,
|
||||
} from 'react-native-reanimated'
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||
import {GlassContainer} from 'expo-glass-effect'
|
||||
import {LinearGradient} from 'expo-linear-gradient'
|
||||
import {ScrollEdgeEffect} from '@bsky.app/expo-scroll-edge-effect'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
import {countGraphemes} from 'unicode-segmenter/grapheme'
|
||||
|
||||
@@ -17,20 +31,24 @@ import {
|
||||
EmojiPicker,
|
||||
type EmojiPickerState,
|
||||
} from '#/view/com/composer/text-input/web/EmojiPicker'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {atoms as a, native, platform, tokens, useTheme, utils} from '#/alf'
|
||||
import {Composer, useComposerInternalApiRef} from '#/components/Composer'
|
||||
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
||||
import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmile} from '#/components/icons/Emoji'
|
||||
import {PaperPlane_Stroke2_Corner0_Rounded as PaperPlane} from '#/components/icons/PaperPlane'
|
||||
import {GlassView} from '#/components/GlassView'
|
||||
import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmileIcon} from '#/components/icons/Emoji'
|
||||
import {PaperPlaneVertical_Filled_Stroke2_Corner1_Rounded as PaperPlaneIcon} from '#/components/icons/PaperPlane'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {IS_WEB} from '#/env'
|
||||
import {IS_ANDROID, IS_LIQUID_GLASS, IS_NATIVE, IS_WEB} from '#/env'
|
||||
|
||||
const MIN_HEIGHT = 40
|
||||
|
||||
export function MessageComposer({
|
||||
textInputId,
|
||||
onSendMessage,
|
||||
hasEmbed,
|
||||
setEmbed,
|
||||
children,
|
||||
}: {
|
||||
textInputId?: string
|
||||
onSendMessage: (message: string) => void
|
||||
hasEmbed: boolean
|
||||
setEmbed: (embedUrl: string | undefined) => void
|
||||
@@ -48,16 +66,25 @@ export function MessageComposer({
|
||||
})
|
||||
const composerInternalApiRef = useComposerInternalApiRef()
|
||||
|
||||
const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState()
|
||||
const {
|
||||
state: hovered,
|
||||
onIn: onHoverIn,
|
||||
onOut: onHoverOut,
|
||||
} = useInteractionState()
|
||||
|
||||
const [text, setText] = useState(getDraft)
|
||||
useSaveMessageDraft(text)
|
||||
|
||||
// Android interactive dismiss sometimes doesn't blur the input
|
||||
const blur = () => {
|
||||
composerInternalApiRef.current?.input?.blur()
|
||||
}
|
||||
|
||||
useKeyboardHandler({
|
||||
onEnd: evt => {
|
||||
'worklet'
|
||||
if (IS_ANDROID && evt.progress === 0) {
|
||||
runOnJS(blur)()
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const submitDisabled = !editable || (!hasEmbed && text.trim().length === 0)
|
||||
|
||||
const openEmojiPicker = (pos: any) => {
|
||||
setEmojiPickerState({isOpen: true, pos})
|
||||
}
|
||||
@@ -65,10 +92,12 @@ export function MessageComposer({
|
||||
const onSubmit = () => {
|
||||
if (!editable) return
|
||||
if (!hasEmbed && text.trim() === '') return
|
||||
if (countGraphemes(text) > MAX_DM_GRAPHEME_LENGTH) {
|
||||
Toast.show(l`Message is too long`, {
|
||||
type: 'error',
|
||||
})
|
||||
const graphemeCount = countGraphemes(text)
|
||||
if (graphemeCount > MAX_DM_GRAPHEME_LENGTH) {
|
||||
Toast.show(
|
||||
l`Message is too long (${graphemeCount}/${MAX_DM_GRAPHEME_LENGTH})`,
|
||||
{type: 'error'},
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -91,152 +120,111 @@ export function MessageComposer({
|
||||
return () => {
|
||||
textInputWebEmitter.removeListener('emoji-inserted', onEmojiInserted)
|
||||
}
|
||||
}, [])
|
||||
}, [composerInternalApiRef])
|
||||
|
||||
return (
|
||||
<>
|
||||
<View style={[a.px_md, a.pb_sm, a.pt_xs]}>
|
||||
{children}
|
||||
<ComposerContainer>
|
||||
{children}
|
||||
|
||||
<View
|
||||
collapsable={false}
|
||||
ref={
|
||||
IS_WEB
|
||||
? undefined
|
||||
: node => {
|
||||
composerInternalApiRef.current?.setAutocompleteAnchor(node)
|
||||
}
|
||||
}
|
||||
// @ts-expect-error web only
|
||||
onMouseEnter={onHoverIn}
|
||||
onMouseLeave={onHoverOut}
|
||||
style={[a.w_full, a.flex_row, a.gap_sm]}>
|
||||
{IS_WEB && (
|
||||
<Pressable
|
||||
onPress={e => {
|
||||
e.currentTarget.measure((_fx, _fy, _width, _height, px, py) => {
|
||||
openEmojiPicker?.({
|
||||
top: py,
|
||||
left: px,
|
||||
right: px,
|
||||
bottom: py,
|
||||
nextFocusRef: {
|
||||
current: composerInternalApiRef.current?.input?.element,
|
||||
<View
|
||||
collapsable={false}
|
||||
ref={native(
|
||||
(node: View) =>
|
||||
void composerInternalApiRef.current?.setAutocompleteAnchor(node),
|
||||
)}>
|
||||
<GlassContainer
|
||||
style={[a.w_full, a.flex_row, a.gap_sm, a.align_end]}
|
||||
spacing={tokens.space.sm}>
|
||||
<GlassView
|
||||
isInteractive
|
||||
glassEffectStyle="regular"
|
||||
style={[a.flex_1, a.rounded_xl, {minHeight: MIN_HEIGHT}]}
|
||||
tintColor={t.palette.contrast_50}
|
||||
fallbackStyle={[t.atoms.bg_contrast_50]}>
|
||||
{IS_WEB && (
|
||||
<Pressable
|
||||
onPress={e => {
|
||||
e.currentTarget.measure(
|
||||
(_fx, _fy, _width, _height, px, py) => {
|
||||
// TODO: rip this horrible system out
|
||||
openEmojiPicker?.({
|
||||
top: py,
|
||||
left: px - 400,
|
||||
right: px - 400,
|
||||
bottom: py,
|
||||
nextFocusRef: {
|
||||
current:
|
||||
composerInternalApiRef.current?.input?.element,
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
})
|
||||
)
|
||||
}}
|
||||
style={[
|
||||
a.overflow_hidden,
|
||||
a.absolute,
|
||||
a.rounded_full,
|
||||
a.align_center,
|
||||
a.justify_center,
|
||||
a.z_30,
|
||||
{
|
||||
height: 20,
|
||||
width: 20,
|
||||
top: 10,
|
||||
right: 10,
|
||||
},
|
||||
]}
|
||||
accessibilityLabel={l`Open emoji picker`}
|
||||
accessibilityHint="">
|
||||
{state => (
|
||||
<EmojiSmileIcon
|
||||
size="md"
|
||||
style={
|
||||
state.hovered ||
|
||||
state.focused ||
|
||||
state.pressed ||
|
||||
emojiPickerState.isOpen
|
||||
? {color: t.palette.primary_500}
|
||||
: t.atoms.text_contrast_high
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Pressable>
|
||||
)}
|
||||
|
||||
<Composer
|
||||
nativeID={textInputId}
|
||||
label={l`Message input field`}
|
||||
placeholder={l`Message`}
|
||||
autocompletePlacement="top-start"
|
||||
internalApiRef={composerInternalApiRef}
|
||||
defaultValue={text}
|
||||
editable={editable}
|
||||
autoFocus={IS_WEB}
|
||||
maxRows={12}
|
||||
outerStyle={[a.flex_1]}
|
||||
contentTextStyle={[a.text_md, a.leading_snug]}
|
||||
contentPaddingStyle={{
|
||||
paddingLeft: 16,
|
||||
paddingTop: 10,
|
||||
paddingBottom: 10,
|
||||
paddingRight: 16 + platform({web: 20, default: 0}),
|
||||
}}
|
||||
style={[
|
||||
a.overflow_hidden,
|
||||
a.absolute,
|
||||
a.rounded_full,
|
||||
a.align_center,
|
||||
a.justify_center,
|
||||
a.z_30,
|
||||
{
|
||||
height: 30,
|
||||
width: 30,
|
||||
top: 8,
|
||||
left: 8,
|
||||
},
|
||||
]}
|
||||
accessibilityLabel={l`Open emoji picker`}
|
||||
accessibilityHint="">
|
||||
{state => (
|
||||
<View
|
||||
style={[
|
||||
a.absolute,
|
||||
a.inset_0,
|
||||
a.align_center,
|
||||
a.justify_center,
|
||||
{
|
||||
backgroundColor:
|
||||
state.hovered || state.focused || state.pressed
|
||||
? t.atoms.bg.backgroundColor
|
||||
: undefined,
|
||||
},
|
||||
]}>
|
||||
<EmojiSmile size="lg" />
|
||||
</View>
|
||||
)}
|
||||
</Pressable>
|
||||
)}
|
||||
|
||||
<Composer
|
||||
label={l`Message input field`}
|
||||
placeholder={l`Write a message`}
|
||||
autocompletePlacement="top-start"
|
||||
internalApiRef={composerInternalApiRef}
|
||||
defaultValue={text}
|
||||
editable={editable}
|
||||
autoFocus={IS_WEB}
|
||||
maxRows={12}
|
||||
outerStyle={[
|
||||
a.flex_1,
|
||||
t.atoms.bg_contrast_25,
|
||||
{
|
||||
borderWidth: 1,
|
||||
borderColor: 'transparent',
|
||||
borderRadius: 22,
|
||||
},
|
||||
editable &&
|
||||
hovered && {
|
||||
borderColor: t.atoms.border_contrast_medium.borderColor,
|
||||
},
|
||||
editable &&
|
||||
focused && {
|
||||
borderColor: t.palette.primary_500,
|
||||
},
|
||||
]}
|
||||
contentTextStyle={[a.text_md, a.leading_snug]}
|
||||
contentPaddingStyle={{
|
||||
paddingLeft: IS_WEB ? 30 + 12 : 12,
|
||||
paddingTop: 12,
|
||||
paddingBottom: 12,
|
||||
paddingRight: 12,
|
||||
}}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
onChange={setText}
|
||||
onFacetCommitted={facet => {
|
||||
if (facet.type === 'url' && isBskyPostUrl(facet.value)) {
|
||||
setEmbed(facet.value)
|
||||
}
|
||||
}}
|
||||
onRequestSubmit={req => {
|
||||
if (req.platform === 'web' && req.shiftKey) return
|
||||
req.nativeEvent.preventDefault()
|
||||
onSubmit()
|
||||
}}
|
||||
/>
|
||||
|
||||
{focused || text.length ? (
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={l`Send message`}
|
||||
accessibilityHint=""
|
||||
hitSlop={HITSLOP_10}
|
||||
style={[
|
||||
a.rounded_full,
|
||||
a.align_center,
|
||||
a.justify_center,
|
||||
a.self_end,
|
||||
a.z_30,
|
||||
{
|
||||
height: 44,
|
||||
width: 44,
|
||||
backgroundColor: t.palette.primary_500,
|
||||
},
|
||||
]}
|
||||
onPress={onSubmit}
|
||||
disabled={!editable}>
|
||||
<PaperPlane
|
||||
fill={t.palette.white}
|
||||
style={[a.relative, {left: 1}]}
|
||||
/>
|
||||
</Pressable>
|
||||
) : null}
|
||||
</View>
|
||||
onChange={setText}
|
||||
onFacetCommitted={facet => {
|
||||
if (facet.type === 'url' && isBskyPostUrl(facet.value)) {
|
||||
setEmbed(facet.value)
|
||||
}
|
||||
}}
|
||||
onRequestSubmit={req => {
|
||||
if (req.platform === 'web' && req.shiftKey) return
|
||||
req.nativeEvent.preventDefault()
|
||||
onSubmit()
|
||||
}}
|
||||
/>
|
||||
</GlassView>
|
||||
<SubmitButton onPress={onSubmit} disabled={submitDisabled} />
|
||||
</GlassContainer>
|
||||
</View>
|
||||
|
||||
{IS_WEB && (
|
||||
@@ -246,6 +234,114 @@ export function MessageComposer({
|
||||
close={() => setEmojiPickerState(prev => ({...prev, isOpen: false}))}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</ComposerContainer>
|
||||
)
|
||||
}
|
||||
|
||||
function SubmitButton({
|
||||
onPress,
|
||||
disabled,
|
||||
}: {
|
||||
onPress: () => void
|
||||
disabled: boolean
|
||||
}) {
|
||||
const {t: l} = useLingui()
|
||||
const t = useTheme()
|
||||
|
||||
return (
|
||||
<GlassView
|
||||
isInteractive
|
||||
glassEffectStyle="regular"
|
||||
style={[a.rounded_full]}
|
||||
tintColor={disabled ? t.palette.contrast_100 : t.palette.primary_500}
|
||||
fallbackStyle={{
|
||||
backgroundColor: disabled
|
||||
? t.palette.contrast_100
|
||||
: t.palette.primary_500,
|
||||
}}>
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={l`Send message`}
|
||||
accessibilityHint=""
|
||||
hitSlop={HITSLOP_10}
|
||||
style={[
|
||||
a.rounded_full,
|
||||
a.align_center,
|
||||
a.justify_center,
|
||||
{height: MIN_HEIGHT, width: MIN_HEIGHT},
|
||||
]}
|
||||
onPress={onPress}
|
||||
disabled={disabled}>
|
||||
<PaperPlaneIcon size="md" fill={t.palette.white} style={[a.mb_2xs]} />
|
||||
</Pressable>
|
||||
</GlassView>
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: remove export when MessageInput is deleted
|
||||
export function ComposerContainer({children}: {children: React.ReactNode}) {
|
||||
const {bottom: bottomInset} = useSafeAreaInsets()
|
||||
const {progress} = useReanimatedKeyboardAnimation()
|
||||
const t = useTheme()
|
||||
|
||||
const animatedContainerStyle = useAnimatedStyle(() => ({
|
||||
paddingHorizontal: interpolate(
|
||||
progress.get(),
|
||||
[0, 1],
|
||||
[bottomInset, tokens.space.sm],
|
||||
{
|
||||
extrapolateRight: Extrapolation.CLAMP,
|
||||
extrapolateLeft: Extrapolation.CLAMP,
|
||||
},
|
||||
),
|
||||
}))
|
||||
|
||||
if (IS_LIQUID_GLASS) {
|
||||
return (
|
||||
<ScrollEdgeEffect edge="bottom">
|
||||
<Animated.View style={[a.w_full, animatedContainerStyle, a.pb_lg]}>
|
||||
{children}
|
||||
</Animated.View>
|
||||
</ScrollEdgeEffect>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
<LinearGradient
|
||||
style={platform({
|
||||
native: [a.pt_sm, a.px_lg, , a.pb_lg, a.w_full],
|
||||
web: [
|
||||
a.pt_xs,
|
||||
a.pl_lg,
|
||||
a.pb_lg,
|
||||
// prevent overlap with the scrollbar, which looks ugly
|
||||
a.pr_xs, // xs + md = lg
|
||||
{width: `calc(100% - ${tokens.space.md}px)` as '100%'},
|
||||
],
|
||||
})}
|
||||
key={t.name} // android does not update when you change the colors. sigh.
|
||||
start={[0.5, 0]}
|
||||
end={[0.5, 1]}
|
||||
colors={[
|
||||
utils.alpha(t.atoms.bg.backgroundColor, 0),
|
||||
utils.alpha(t.atoms.bg.backgroundColor, 0.8),
|
||||
t.atoms.bg.backgroundColor,
|
||||
]}>
|
||||
{children}
|
||||
</LinearGradient>
|
||||
{/* covers the gap between the keyboard and the input during keyboard animation */}
|
||||
{IS_NATIVE && (
|
||||
<View
|
||||
style={[
|
||||
t.atoms.bg,
|
||||
a.absolute,
|
||||
a.left_0,
|
||||
a.right_0,
|
||||
{top: '100%', height: bottomInset + 1, marginTop: -1},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import {useCallback, useState} from 'react'
|
||||
import {Pressable, TextInput, useWindowDimensions, View} from 'react-native'
|
||||
import {Pressable, TextInput, useWindowDimensions} from 'react-native'
|
||||
import {
|
||||
useFocusedInputHandler,
|
||||
useKeyboardHandler,
|
||||
useReanimatedKeyboardAnimation,
|
||||
} from 'react-native-keyboard-controller'
|
||||
import Animated, {
|
||||
measure,
|
||||
runOnJS,
|
||||
useAnimatedProps,
|
||||
useAnimatedRef,
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
} from 'react-native-reanimated'
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||
import {GlassContainer} from 'expo-glass-effect'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {countGraphemes} from 'unicode-segmenter/grapheme'
|
||||
@@ -24,21 +27,26 @@ import {
|
||||
useSaveMessageDraft,
|
||||
} from '#/state/messages/message-drafts'
|
||||
import {type EmojiPickerPosition} from '#/view/com/composer/text-input/web/EmojiPicker'
|
||||
import {android, atoms as a, useTheme} from '#/alf'
|
||||
import {useSharedInputStyles} from '#/components/forms/TextField'
|
||||
import {PaperPlane_Stroke2_Corner0_Rounded as PaperPlane} from '#/components/icons/PaperPlane'
|
||||
import {atoms as a, platform, tokens, useTheme} from '#/alf'
|
||||
import {GlassView} from '#/components/GlassView'
|
||||
import {PaperPlaneVertical_Filled_Stroke2_Corner1_Rounded as PaperPlaneIcon} from '#/components/icons/PaperPlane'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {IS_IOS, IS_WEB} from '#/env'
|
||||
import {IS_ANDROID, IS_IOS, IS_WEB} from '#/env'
|
||||
import {ComposerContainer} from './MessageComposer'
|
||||
import {useExtractEmbedFromFacets} from './MessageInputEmbed'
|
||||
|
||||
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput)
|
||||
|
||||
const MIN_HEIGHT = 40
|
||||
|
||||
export function MessageInput({
|
||||
textInputId,
|
||||
onSendMessage,
|
||||
hasEmbed,
|
||||
setEmbed,
|
||||
children,
|
||||
}: {
|
||||
textInputId?: string
|
||||
onSendMessage: (message: string) => void
|
||||
hasEmbed: boolean
|
||||
setEmbed: (embedUrl: string | undefined) => void
|
||||
@@ -57,8 +65,6 @@ export function MessageInput({
|
||||
const maxHeight = useSharedValue<undefined | number>(undefined)
|
||||
const isInputScrollable = useSharedValue(false)
|
||||
|
||||
const inputStyles = useSharedInputStyles()
|
||||
const [isFocused, setIsFocused] = useState(false)
|
||||
const [message, setMessage] = useState(getDraft)
|
||||
const inputRef = useAnimatedRef<TextInput>()
|
||||
const [shouldEnforceClear, setShouldEnforceClear] = useState(false)
|
||||
@@ -133,78 +139,114 @@ export function MessageInput({
|
||||
scrollEnabled: isInputScrollable.get(),
|
||||
}))
|
||||
|
||||
const submitDisabled = needsEmailVerification || message.trim().length === 0
|
||||
|
||||
const blur = useCallback(() => {
|
||||
inputRef.current?.blur()
|
||||
}, [inputRef])
|
||||
|
||||
useKeyboardHandler({
|
||||
onEnd: evt => {
|
||||
'worklet'
|
||||
// small hack: interactive dismiss on Android sometimes doesn't blur the input
|
||||
if (IS_ANDROID && evt.progress === 0) {
|
||||
runOnJS(blur)()
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<View style={[a.px_md, a.pb_sm, a.pt_xs]}>
|
||||
<ComposerContainer>
|
||||
{children}
|
||||
<View
|
||||
style={[
|
||||
a.w_full,
|
||||
a.flex_row,
|
||||
t.atoms.bg_contrast_25,
|
||||
{
|
||||
padding: a.p_sm.padding - 2,
|
||||
paddingLeft: a.p_md.padding - 2,
|
||||
borderWidth: 1,
|
||||
borderRadius: 23,
|
||||
borderColor: 'transparent',
|
||||
},
|
||||
isFocused && inputStyles.chromeFocus,
|
||||
]}>
|
||||
<AnimatedTextInput
|
||||
accessibilityLabel={_(msg`Message input field`)}
|
||||
accessibilityHint={_(msg`Type your message here`)}
|
||||
placeholder={_(msg`Write a message`)}
|
||||
placeholderTextColor={t.palette.contrast_500}
|
||||
value={message}
|
||||
onChange={evt => {
|
||||
// bit of a hack: iOS automatically accepts autocomplete suggestions when you tap anywhere on the screen
|
||||
// including the button we just pressed - and this overrides clearing the input! so we watch for the
|
||||
// next change and double make sure the input is cleared. It should *always* send an onChange event after
|
||||
// clearing via setMessage('') that happens in onSubmit()
|
||||
// -sfn
|
||||
if (IS_IOS && shouldEnforceClear) {
|
||||
setShouldEnforceClear(false)
|
||||
setMessage('')
|
||||
return
|
||||
}
|
||||
const text = evt.nativeEvent.text
|
||||
setMessage(text)
|
||||
}}
|
||||
multiline={true}
|
||||
style={[
|
||||
a.flex_1,
|
||||
a.text_md,
|
||||
a.px_sm,
|
||||
t.atoms.text,
|
||||
android({paddingTop: 0}),
|
||||
{paddingBottom: IS_IOS ? 5 : 0},
|
||||
animatedStyle,
|
||||
]}
|
||||
keyboardAppearance={t.scheme}
|
||||
submitBehavior="newline"
|
||||
onFocus={() => setIsFocused(true)}
|
||||
onBlur={() => setIsFocused(false)}
|
||||
ref={inputRef}
|
||||
hitSlop={HITSLOP_10}
|
||||
animatedProps={animatedProps}
|
||||
editable={!needsEmailVerification}
|
||||
/>
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Send message`)}
|
||||
accessibilityHint=""
|
||||
hitSlop={HITSLOP_10}
|
||||
style={[
|
||||
a.rounded_full,
|
||||
a.align_center,
|
||||
a.justify_center,
|
||||
{height: 30, width: 30, backgroundColor: t.palette.primary_500},
|
||||
]}
|
||||
onPress={onSubmit}
|
||||
disabled={needsEmailVerification}>
|
||||
<PaperPlane fill={t.palette.white} style={[a.relative, {left: 1}]} />
|
||||
</Pressable>
|
||||
</View>
|
||||
</View>
|
||||
<GlassContainer
|
||||
style={[a.flex_row, a.align_end, a.gap_sm]}
|
||||
spacing={tokens.space.xs}>
|
||||
<GlassView
|
||||
isInteractive
|
||||
glassEffectStyle="regular"
|
||||
style={[a.flex_1, a.rounded_xl, {minHeight: MIN_HEIGHT}]}
|
||||
tintColor={t.palette.contrast_50}
|
||||
fallbackStyle={[t.atoms.bg_contrast_50]}>
|
||||
<AnimatedTextInput
|
||||
nativeID={textInputId}
|
||||
accessibilityLabel={_(msg`Message input field`)}
|
||||
accessibilityHint={_(msg`Type your message here`)}
|
||||
placeholder={_(msg`Message`)}
|
||||
placeholderTextColor={t.palette.contrast_500}
|
||||
value={message}
|
||||
onChange={evt => {
|
||||
// bit of a hack: iOS automatically accepts autocomplete suggestions when you tap anywhere on the screen
|
||||
// including the button we just pressed - and this overrides clearing the input! so we watch for the
|
||||
// next change and double make sure the input is cleared. It should *always* send an onChange event after
|
||||
// clearing via setMessage('') that happens in onSubmit()
|
||||
// -sfn
|
||||
if (IS_IOS && shouldEnforceClear) {
|
||||
setShouldEnforceClear(false)
|
||||
setMessage('')
|
||||
return
|
||||
}
|
||||
const text = evt.nativeEvent.text
|
||||
setMessage(text)
|
||||
}}
|
||||
multiline={true}
|
||||
style={[
|
||||
{flexBasis: 'auto', minHeight: MIN_HEIGHT},
|
||||
a.flex_shrink_0,
|
||||
a.flex_grow,
|
||||
a.text_md,
|
||||
a.px_lg,
|
||||
t.atoms.text,
|
||||
platform({
|
||||
android: {paddingTop: 2, paddingBottom: 3},
|
||||
ios: {paddingTop: 10, paddingBottom: 5},
|
||||
}),
|
||||
animatedStyle,
|
||||
]}
|
||||
verticalAlign="middle"
|
||||
keyboardAppearance={t.scheme}
|
||||
submitBehavior="newline"
|
||||
ref={inputRef}
|
||||
hitSlop={HITSLOP_10}
|
||||
animatedProps={animatedProps}
|
||||
editable={!needsEmailVerification}
|
||||
/>
|
||||
</GlassView>
|
||||
<GlassView
|
||||
isInteractive
|
||||
glassEffectStyle="regular"
|
||||
style={[a.rounded_full]}
|
||||
tintColor={
|
||||
submitDisabled ? t.palette.contrast_100 : t.palette.primary_500
|
||||
}
|
||||
fallbackStyle={{
|
||||
backgroundColor: submitDisabled
|
||||
? t.palette.contrast_100
|
||||
: t.palette.primary_500,
|
||||
}}>
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Send message`)}
|
||||
accessibilityHint=""
|
||||
hitSlop={HITSLOP_10}
|
||||
style={[
|
||||
a.rounded_full,
|
||||
a.align_center,
|
||||
a.justify_center,
|
||||
{
|
||||
height: MIN_HEIGHT,
|
||||
width: MIN_HEIGHT,
|
||||
},
|
||||
]}
|
||||
onPress={onSubmit}
|
||||
disabled={submitDisabled}>
|
||||
<PaperPlaneIcon
|
||||
size="md"
|
||||
fill={t.palette.white}
|
||||
style={[a.mb_2xs]}
|
||||
/>
|
||||
</Pressable>
|
||||
</GlassView>
|
||||
</GlassContainer>
|
||||
</ComposerContainer>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
import {useCallback, useEffect, useRef, useState} from 'react'
|
||||
import {type LayoutChangeEvent, View} from 'react-native'
|
||||
import {useKeyboardHandler} from 'react-native-keyboard-controller'
|
||||
import {useCallback, useEffect, useId, useRef, useState} from 'react'
|
||||
import {type LayoutChangeEvent, type ScrollViewProps, View} from 'react-native'
|
||||
import {
|
||||
KeyboardChatScrollView,
|
||||
type KeyboardChatScrollViewProps,
|
||||
KeyboardGestureArea,
|
||||
} from 'react-native-keyboard-controller'
|
||||
import Animated, {
|
||||
runOnJS,
|
||||
scrollTo,
|
||||
type ScrollEvent,
|
||||
type SharedValue,
|
||||
useAnimatedRef,
|
||||
useAnimatedStyle,
|
||||
useDerivedValue,
|
||||
useSharedValue,
|
||||
} from 'react-native-reanimated'
|
||||
import {type ReanimatedScrollEvent} from 'react-native-reanimated/lib/typescript/hook/commonTypes'
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||
import {
|
||||
type $Typed,
|
||||
type AppBskyEmbedRecord,
|
||||
AppBskyRichtextFacet,
|
||||
RichText,
|
||||
} from '@atproto/api'
|
||||
import {useScrollEdgeEffectRef} from '@bsky.app/expo-scroll-edge-effect'
|
||||
|
||||
import {useHideBottomBarBorderForScreen} from '#/lib/hooks/useHideBottomBarBorder'
|
||||
import {mergeRefs} from '#/lib/merge-refs'
|
||||
import {ScrollProvider} from '#/lib/ScrollContext'
|
||||
import {shortenLinks, stripInvalidMentions} from '#/lib/strings/rich-text-manip'
|
||||
import {
|
||||
@@ -36,7 +42,6 @@ import {
|
||||
} from '#/state/messages/convo/types'
|
||||
import {useGetPost} from '#/state/queries/post'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {useShellLayout} from '#/state/shell/shell-layout'
|
||||
import {
|
||||
EmojiPicker,
|
||||
type EmojiPickerState,
|
||||
@@ -46,15 +51,17 @@ import {ChatDisabled} from '#/screens/Messages/components/ChatDisabled'
|
||||
import {MessageComposer} from '#/screens/Messages/components/MessageComposer'
|
||||
import {MessageInput} from '#/screens/Messages/components/MessageInput'
|
||||
import {MessageListError} from '#/screens/Messages/components/MessageListError'
|
||||
import {atoms as a, platform, tokens, useTheme, web} from '#/alf'
|
||||
import {ChatEmptyPill} from '#/components/dms/ChatEmptyPill'
|
||||
import {MessageItem} from '#/components/dms/MessageItem'
|
||||
import {NewMessagesPill} from '#/components/dms/NewMessagesPill'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_NATIVE, IS_WEB} from '#/env'
|
||||
import {IS_ANDROID, IS_NATIVE, IS_WEB} from '#/env'
|
||||
import {ChatStatusInfo} from './ChatStatusInfo'
|
||||
import {MessageInputEmbed, useMessageEmbed} from './MessageInputEmbed'
|
||||
import {KeyboardStickyView} from './vendor/KeyboardStickyView'
|
||||
|
||||
function MaybeLoader({isLoading}: {isLoading: boolean}) {
|
||||
return (
|
||||
@@ -108,9 +115,9 @@ export function MessagesList({
|
||||
const agent = useAgent()
|
||||
const getPost = useGetPost()
|
||||
const {embedUri, setEmbed} = useMessageEmbed()
|
||||
const t = useTheme()
|
||||
|
||||
useHideBottomBarBorderForScreen()
|
||||
|
||||
const textInputId = 'chat-input-' + useId()
|
||||
const flatListRef = useAnimatedRef<ListMethods>()
|
||||
|
||||
const [newMessagesPill, setNewMessagesPill] = useState({
|
||||
@@ -123,6 +130,18 @@ export function MessagesList({
|
||||
pos: {top: 0, left: 0, right: 0, bottom: 0, nextFocusRef: null},
|
||||
})
|
||||
|
||||
const inputHeightUI = useSharedValue(0)
|
||||
const [inputHeightJS, setInputHeightJS] = useState(0)
|
||||
|
||||
const onInputLayout = useCallback(
|
||||
(event: LayoutChangeEvent) => {
|
||||
const inputHeight = event.nativeEvent.layout.height
|
||||
inputHeightUI.set(inputHeight)
|
||||
setInputHeightJS(inputHeight)
|
||||
},
|
||||
[inputHeightUI],
|
||||
)
|
||||
|
||||
// We need to keep track of when the scroll offset is at the bottom of the list to know when to scroll as new items
|
||||
// are added to the list. For example, if the user is scrolled up to 1iew older messages, we don't want to scroll to
|
||||
// the bottom.
|
||||
@@ -226,12 +245,12 @@ export function MessagesList({
|
||||
|
||||
const onStartReached = useCallback(() => {
|
||||
if (hasScrolled && prevContentHeight.current > layoutHeight.get()) {
|
||||
convoState.fetchMessageHistory()
|
||||
void convoState.fetchMessageHistory()
|
||||
}
|
||||
}, [convoState, hasScrolled, layoutHeight])
|
||||
|
||||
const onScroll = useCallback(
|
||||
(e: ReanimatedScrollEvent) => {
|
||||
(e: ScrollEvent) => {
|
||||
'worklet'
|
||||
layoutHeight.set(e.layoutMeasurement.height)
|
||||
const bottomOffset = e.contentOffset.y + e.layoutMeasurement.height
|
||||
@@ -256,56 +275,8 @@ export function MessagesList({
|
||||
)
|
||||
|
||||
// -- Keyboard animation handling
|
||||
const {footerHeight} = useShellLayout()
|
||||
|
||||
const keyboardHeight = useSharedValue(0)
|
||||
const keyboardIsOpening = useSharedValue(false)
|
||||
|
||||
// In some cases - like when the emoji piker opens - we don't want to animate the scroll in the list onLayout event.
|
||||
// We use this value to keep track of when we want to disable the animation.
|
||||
const layoutScrollWithoutAnimation = useSharedValue(false)
|
||||
|
||||
useKeyboardHandler(
|
||||
{
|
||||
onStart: e => {
|
||||
'worklet'
|
||||
// Immediate updates - like opening the emoji picker - will have a duration of zero. In those cases, we should
|
||||
// just update the height here instead of having the `onMove` event do it (that event will not fire!)
|
||||
if (e.duration === 0) {
|
||||
layoutScrollWithoutAnimation.set(true)
|
||||
keyboardHeight.set(e.height)
|
||||
} else {
|
||||
keyboardIsOpening.set(true)
|
||||
}
|
||||
},
|
||||
onMove: e => {
|
||||
'worklet'
|
||||
keyboardHeight.set(e.height)
|
||||
if (e.height > footerHeight.get()) {
|
||||
scrollTo(flatListRef, 0, 1e7, false)
|
||||
}
|
||||
},
|
||||
onEnd: e => {
|
||||
'worklet'
|
||||
keyboardHeight.set(e.height)
|
||||
if (e.height > footerHeight.get()) {
|
||||
scrollTo(flatListRef, 0, 1e7, false)
|
||||
}
|
||||
keyboardIsOpening.set(false)
|
||||
},
|
||||
},
|
||||
[footerHeight],
|
||||
)
|
||||
|
||||
const animatedListStyle = useAnimatedStyle(() => ({
|
||||
marginBottom: Math.max(keyboardHeight.get(), footerHeight.get()),
|
||||
}))
|
||||
|
||||
const animatedStickyViewStyle = useAnimatedStyle(() => ({
|
||||
transform: [
|
||||
{translateY: -Math.max(keyboardHeight.get(), footerHeight.get())},
|
||||
],
|
||||
}))
|
||||
const {bottom: bottomInset} = useSafeAreaInsets()
|
||||
|
||||
// -- Message sending
|
||||
const onSendMessage = useCallback(
|
||||
@@ -387,26 +358,6 @@ export function MessagesList({
|
||||
[agent, convoState, embedUri, getPost, hasScrolled, setHasScrolled],
|
||||
)
|
||||
|
||||
// -- List layout changes (opening emoji keyboard, etc.)
|
||||
const onListLayout = useCallback(
|
||||
(e: LayoutChangeEvent) => {
|
||||
layoutHeight.set(e.nativeEvent.layout.height)
|
||||
|
||||
if (IS_WEB || !keyboardIsOpening.get()) {
|
||||
flatListRef.current?.scrollToEnd({
|
||||
animated: !layoutScrollWithoutAnimation.get(),
|
||||
})
|
||||
layoutScrollWithoutAnimation.set(false)
|
||||
}
|
||||
},
|
||||
[
|
||||
flatListRef,
|
||||
keyboardIsOpening,
|
||||
layoutScrollWithoutAnimation,
|
||||
layoutHeight,
|
||||
],
|
||||
)
|
||||
|
||||
const scrollToEndOnPress = useCallback(() => {
|
||||
flatListRef.current?.scrollToOffset({
|
||||
offset: prevContentHeight.current,
|
||||
@@ -418,66 +369,100 @@ export function MessagesList({
|
||||
setEmojiPickerState({isOpen: true, pos})
|
||||
}, [])
|
||||
|
||||
const renderScrollComponent = useCallback(
|
||||
(props: ScrollViewProps) => (
|
||||
<ChatScrollComponent {...props} inputHeight={inputHeightUI} />
|
||||
),
|
||||
[inputHeightUI],
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Custom scroll provider so that we can use the `onScroll` event in our custom List implementation */}
|
||||
<ScrollProvider onScroll={onScroll}>
|
||||
<List
|
||||
ref={flatListRef}
|
||||
data={convoState.items}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={keyExtractor}
|
||||
disableFullWindowScroll={true}
|
||||
disableVirtualization={true}
|
||||
style={animatedListStyle}
|
||||
// The extra two items account for the header and the footer components
|
||||
initialNumToRender={IS_NATIVE ? 32 : 62}
|
||||
maxToRenderPerBatch={IS_WEB ? 32 : 62}
|
||||
keyboardDismissMode="on-drag"
|
||||
keyboardShouldPersistTaps="handled"
|
||||
maintainVisibleContentPosition={{
|
||||
minIndexForVisible: 0,
|
||||
}}
|
||||
removeClippedSubviews={false}
|
||||
sideBorders={false}
|
||||
onContentSizeChange={onContentSizeChange}
|
||||
onLayout={onListLayout}
|
||||
onStartReached={onStartReached}
|
||||
onScrollToIndexFailed={onScrollToIndexFailed}
|
||||
scrollEventThrottle={100}
|
||||
ListHeaderComponent={
|
||||
<MaybeLoader isLoading={convoState.isFetchingHistory} />
|
||||
}
|
||||
/>
|
||||
</ScrollProvider>
|
||||
<Animated.View style={animatedStickyViewStyle}>
|
||||
{convoState.status === ConvoStatus.Disabled ? (
|
||||
<ChatDisabled />
|
||||
) : blocked ? (
|
||||
footer
|
||||
) : (
|
||||
<ConversationFooter
|
||||
convoState={convoState}
|
||||
hasAcceptOverride={hasAcceptOverride}>
|
||||
{ax.features.enabled(ax.features.DmsNewMessageComposerEnable) ? (
|
||||
<MessageComposer
|
||||
onSendMessage={onSendMessage}
|
||||
hasEmbed={!!embedUri}
|
||||
setEmbed={setEmbed}>
|
||||
<MessageInputEmbed embedUri={embedUri} setEmbed={setEmbed} />
|
||||
</MessageComposer>
|
||||
) : (
|
||||
<MessageInput
|
||||
onSendMessage={onSendMessage}
|
||||
hasEmbed={!!embedUri}
|
||||
setEmbed={setEmbed}
|
||||
openEmojiPicker={onOpenEmojiPicker}>
|
||||
<MessageInputEmbed embedUri={embedUri} setEmbed={setEmbed} />
|
||||
</MessageInput>
|
||||
<KeyboardGestureArea
|
||||
interpolator="ios"
|
||||
// HACKFIX: https://github.com/kirillzyusko/react-native-keyboard-controller/issues/1419
|
||||
offset={Math.round(inputHeightJS)}
|
||||
textInputNativeID={textInputId}
|
||||
style={[a.flex_1]}>
|
||||
{/* Custom scroll provider so that we can use the `onScroll` event in our custom List implementation */}
|
||||
<ScrollProvider onScroll={onScroll}>
|
||||
<List
|
||||
ref={flatListRef}
|
||||
data={convoState.items}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={keyExtractor}
|
||||
disableFullWindowScroll={true}
|
||||
disableVirtualization={true}
|
||||
// The extra two items account for the header and the footer components
|
||||
initialNumToRender={IS_NATIVE ? 32 : 62}
|
||||
maxToRenderPerBatch={IS_WEB ? 32 : 62}
|
||||
keyboardDismissMode="interactive"
|
||||
keyboardShouldPersistTaps="handled"
|
||||
maintainVisibleContentPosition={{minIndexForVisible: 0}}
|
||||
removeClippedSubviews={false}
|
||||
sideBorders={false}
|
||||
onContentSizeChange={onContentSizeChange}
|
||||
onStartReached={onStartReached}
|
||||
onScrollToIndexFailed={onScrollToIndexFailed}
|
||||
showsVerticalScrollIndicator={!IS_ANDROID}
|
||||
scrollEventThrottle={100}
|
||||
ListHeaderComponent={
|
||||
<MaybeLoader isLoading={convoState.isFetchingHistory} />
|
||||
}
|
||||
// native only (prop is not supported on web)
|
||||
renderScrollComponent={renderScrollComponent}
|
||||
// pushes up the content under the input on web (renderScrollComponent handles it on native)
|
||||
ListFooterComponent={web(
|
||||
<WebInputSpacer inputHeight={inputHeightJS} />,
|
||||
)}
|
||||
</ConversationFooter>
|
||||
)}
|
||||
</Animated.View>
|
||||
style={web({
|
||||
scrollbarWidth: 'thin',
|
||||
scrollbarColor: `${t.palette.contrast_100} transparent`,
|
||||
scrollbarGutter: 'stable both-edges',
|
||||
})}
|
||||
/>
|
||||
</ScrollProvider>
|
||||
<KeyboardStickyView
|
||||
style={[a.absolute, a.bottom_0, a.left_0, a.right_0]}
|
||||
onLayout={onInputLayout}
|
||||
minimumOffset={bottomInset}
|
||||
offset={{
|
||||
closed: platform({
|
||||
ios: tokens.space.lg, // hide bottom padding when closed
|
||||
default: 0,
|
||||
}),
|
||||
opened: 0,
|
||||
}}>
|
||||
{convoState.status === ConvoStatus.Disabled ? (
|
||||
<ChatDisabled />
|
||||
) : blocked ? (
|
||||
footer
|
||||
) : (
|
||||
<ConversationFooter
|
||||
convoState={convoState}
|
||||
hasAcceptOverride={hasAcceptOverride}>
|
||||
{ax.features.enabled(ax.features.DmsNewMessageComposerEnable) ? (
|
||||
<MessageComposer
|
||||
textInputId={textInputId}
|
||||
onSendMessage={onSendMessage}
|
||||
hasEmbed={!!embedUri}
|
||||
setEmbed={setEmbed}>
|
||||
<MessageInputEmbed embedUri={embedUri} setEmbed={setEmbed} />
|
||||
</MessageComposer>
|
||||
) : (
|
||||
<MessageInput
|
||||
textInputId={textInputId}
|
||||
onSendMessage={onSendMessage}
|
||||
hasEmbed={!!embedUri}
|
||||
setEmbed={setEmbed}
|
||||
openEmojiPicker={onOpenEmojiPicker}>
|
||||
<MessageInputEmbed embedUri={embedUri} setEmbed={setEmbed} />
|
||||
</MessageInput>
|
||||
)}
|
||||
</ConversationFooter>
|
||||
)}
|
||||
</KeyboardStickyView>
|
||||
</KeyboardGestureArea>
|
||||
|
||||
{IS_WEB && (
|
||||
<EmojiPicker
|
||||
@@ -492,6 +477,53 @@ export function MessagesList({
|
||||
)
|
||||
}
|
||||
|
||||
/** Note: native only */
|
||||
function ChatScrollComponent({
|
||||
ref,
|
||||
inputHeight,
|
||||
...props
|
||||
}: ScrollViewProps & {
|
||||
ref?: React.RefObject<KeyboardChatScrollViewProps>
|
||||
inputHeight: SharedValue<number>
|
||||
}) {
|
||||
const scrollEdgeRef = useScrollEdgeEffectRef()
|
||||
const {bottom: bottomInset} = useSafeAreaInsets()
|
||||
|
||||
const offset = platform({
|
||||
ios: bottomInset - tokens.space.lg,
|
||||
android: bottomInset,
|
||||
default: 0,
|
||||
})
|
||||
|
||||
const inputOffset = platform({
|
||||
ios: bottomInset - tokens.space.lg,
|
||||
android: bottomInset,
|
||||
default: 0,
|
||||
})
|
||||
|
||||
const extraContentPadding = useDerivedValue(
|
||||
() => inputHeight.get() + inputOffset,
|
||||
)
|
||||
|
||||
return (
|
||||
<KeyboardChatScrollView
|
||||
ref={mergeRefs([scrollEdgeRef, ref])}
|
||||
automaticallyAdjustContentInsets={false}
|
||||
keyboardDismissMode="interactive"
|
||||
keyboardLiftBehavior="always"
|
||||
extraContentPadding={extraContentPadding}
|
||||
offset={offset}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function WebInputSpacer({inputHeight}: {inputHeight: number}) {
|
||||
if (!IS_WEB) return null
|
||||
|
||||
return <Animated.View style={{height: inputHeight}} />
|
||||
}
|
||||
|
||||
type FooterState = 'loading' | 'new-chat' | 'request' | 'standard'
|
||||
|
||||
function getFooterState(
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import {
|
||||
type KeyboardStickyViewProps,
|
||||
useReanimatedKeyboardAnimation,
|
||||
} from 'react-native-keyboard-controller'
|
||||
import Animated, {useAnimatedStyle} from 'react-native-reanimated'
|
||||
|
||||
// Vendored from https://github.com/kirillzyusko/react-native-keyboard-controller/blob/main/src/components/KeyboardStickyView/index.tsx
|
||||
// Converted to Reanimated to support `minimumOffset` clamping.
|
||||
export function KeyboardStickyView({
|
||||
children,
|
||||
offset: {closed = 0, opened = 0} = {},
|
||||
style,
|
||||
enabled = true,
|
||||
minimumOffset,
|
||||
...props
|
||||
}: KeyboardStickyViewProps & {
|
||||
/**
|
||||
* Stop the stickyview going lower than this (i.e. bottom safe area)
|
||||
*/
|
||||
minimumOffset?: number
|
||||
}) {
|
||||
const {height, progress} = useReanimatedKeyboardAnimation()
|
||||
|
||||
const animatedStyle = useAnimatedStyle(() => {
|
||||
const offset = closed + (opened - closed) * progress.get()
|
||||
let translateY: number
|
||||
|
||||
if (enabled) {
|
||||
let h = height.get()
|
||||
if (minimumOffset != null) {
|
||||
h = Math.min(h, -minimumOffset)
|
||||
}
|
||||
translateY = h + offset
|
||||
} else {
|
||||
translateY = closed
|
||||
}
|
||||
|
||||
return {
|
||||
transform: [{translateY}],
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<Animated.View style={[animatedStyle, style]} {...props}>
|
||||
{children}
|
||||
</Animated.View>
|
||||
)
|
||||
}
|
||||
@@ -2,9 +2,7 @@ import {useState} from 'react'
|
||||
import {Alert, LayoutAnimation, Linking, Pressable, View} from 'react-native'
|
||||
import {useReducedMotion} from 'react-native-reanimated'
|
||||
import {type AppBskyActorDefs, moderateProfile} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
import {type NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||
|
||||
@@ -73,7 +71,7 @@ import {useActivitySubscriptionsNudged} from '#/storage/hooks/activity-subscript
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Settings'>
|
||||
export function SettingsScreen({}: Props) {
|
||||
const ax = useAnalytics()
|
||||
const {_} = useLingui()
|
||||
const {t: l} = useLingui()
|
||||
const reducedMotion = useReducedMotion()
|
||||
const {logoutEveryAccount} = useSessionApi()
|
||||
const {accounts, currentAccount} = useSession()
|
||||
@@ -121,10 +119,8 @@ export function SettingsScreen({}: Props) {
|
||||
{accounts.length > 1 ? (
|
||||
<>
|
||||
<SettingsList.PressableItem
|
||||
label={_(msg`Switch account`)}
|
||||
accessibilityHint={_(
|
||||
msg`Shows other accounts you can switch to`,
|
||||
)}
|
||||
label={l`Switch account`}
|
||||
accessibilityHint={l`Shows other accounts you can switch to`}
|
||||
onPress={() => {
|
||||
if (!reducedMotion) {
|
||||
LayoutAnimation.configureNext(
|
||||
@@ -161,7 +157,9 @@ export function SettingsScreen({}: Props) {
|
||||
p => p.did === account.did,
|
||||
)}
|
||||
pendingDid={pendingDid}
|
||||
onPressSwitchAccount={onPressSwitchAccount}
|
||||
onPressSwitchAccount={(account, logContext) =>
|
||||
void onPressSwitchAccount(account, logContext)
|
||||
}
|
||||
/>
|
||||
))}
|
||||
<AddAccountRow />
|
||||
@@ -172,7 +170,7 @@ export function SettingsScreen({}: Props) {
|
||||
<AddAccountRow />
|
||||
)}
|
||||
<SettingsList.Divider />
|
||||
<SettingsList.LinkItem to="/settings/account" label={_(msg`Account`)}>
|
||||
<SettingsList.LinkItem to="/settings/account" label={l`Account`}>
|
||||
<SettingsList.ItemIcon icon={PersonIcon} />
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Account</Trans>
|
||||
@@ -180,21 +178,23 @@ export function SettingsScreen({}: Props) {
|
||||
</SettingsList.LinkItem>
|
||||
<SettingsList.LinkItem
|
||||
to="/settings/privacy-and-security"
|
||||
label={_(msg`Privacy and security`)}>
|
||||
label={l`Privacy and security`}>
|
||||
<SettingsList.ItemIcon icon={LockIcon} />
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Privacy and security</Trans>
|
||||
</SettingsList.ItemText>
|
||||
</SettingsList.LinkItem>
|
||||
<SettingsList.LinkItem to="/moderation" label={_(msg`Moderation`)}>
|
||||
<SettingsList.LinkItem
|
||||
to="/moderation"
|
||||
label={l`Moderation and content filters`}>
|
||||
<SettingsList.ItemIcon icon={HandIcon} />
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Moderation</Trans>
|
||||
<Trans>Moderation and content filters</Trans>
|
||||
</SettingsList.ItemText>
|
||||
</SettingsList.LinkItem>
|
||||
<SettingsList.LinkItem
|
||||
to="/settings/notifications"
|
||||
label={_(msg`Notifications`)}>
|
||||
label={l`Notifications`}>
|
||||
<SettingsList.ItemIcon icon={NotificationIcon} />
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Notifications</Trans>
|
||||
@@ -202,7 +202,7 @@ export function SettingsScreen({}: Props) {
|
||||
</SettingsList.LinkItem>
|
||||
<SettingsList.LinkItem
|
||||
to="/settings/content-and-media"
|
||||
label={_(msg`Content and media`)}>
|
||||
label={l`Content and media`}>
|
||||
<SettingsList.ItemIcon icon={WindowIcon} />
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Content and media</Trans>
|
||||
@@ -213,7 +213,7 @@ export function SettingsScreen({}: Props) {
|
||||
!ax.features.enabled(ax.features.ImportContactsSettingsDisable) && (
|
||||
<SettingsList.LinkItem
|
||||
to="/settings/find-contacts"
|
||||
label={_(msg`Find friends from contacts`)}>
|
||||
label={l`Find friends from contacts`}>
|
||||
<SettingsList.ItemIcon icon={ContactsIcon} />
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Find friends from contacts</Trans>
|
||||
@@ -222,7 +222,7 @@ export function SettingsScreen({}: Props) {
|
||||
)}
|
||||
<SettingsList.LinkItem
|
||||
to="/settings/appearance"
|
||||
label={_(msg`Appearance`)}>
|
||||
label={l`Appearance`}>
|
||||
<SettingsList.ItemIcon icon={PaintRollerIcon} />
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Appearance</Trans>
|
||||
@@ -230,31 +230,29 @@ export function SettingsScreen({}: Props) {
|
||||
</SettingsList.LinkItem>
|
||||
<SettingsList.LinkItem
|
||||
to="/settings/accessibility"
|
||||
label={_(msg`Accessibility`)}>
|
||||
label={l`Accessibility`}>
|
||||
<SettingsList.ItemIcon icon={AccessibilityIcon} />
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Accessibility</Trans>
|
||||
</SettingsList.ItemText>
|
||||
</SettingsList.LinkItem>
|
||||
<SettingsList.LinkItem
|
||||
to="/settings/language"
|
||||
label={_(msg`Languages`)}>
|
||||
<SettingsList.LinkItem to="/settings/language" label={l`Languages`}>
|
||||
<SettingsList.ItemIcon icon={EarthIcon} />
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Languages</Trans>
|
||||
</SettingsList.ItemText>
|
||||
</SettingsList.LinkItem>
|
||||
<SettingsList.PressableItem
|
||||
onPress={() => Linking.openURL(HELP_DESK_URL)}
|
||||
label={_(msg`Help`)}
|
||||
accessibilityHint={_(msg`Opens helpdesk in browser`)}>
|
||||
onPress={() => void Linking.openURL(HELP_DESK_URL)}
|
||||
label={l`Help`}
|
||||
accessibilityHint={l`Opens helpdesk in browser`}>
|
||||
<SettingsList.ItemIcon icon={CircleQuestionIcon} />
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Help</Trans>
|
||||
</SettingsList.ItemText>
|
||||
<SettingsList.Chevron />
|
||||
</SettingsList.PressableItem>
|
||||
<SettingsList.LinkItem to="/settings/about" label={_(msg`About`)}>
|
||||
<SettingsList.LinkItem to="/settings/about" label={l`About`}>
|
||||
<SettingsList.ItemIcon icon={BubbleInfoIcon} />
|
||||
<SettingsList.ItemText>
|
||||
<Trans>About</Trans>
|
||||
@@ -264,7 +262,7 @@ export function SettingsScreen({}: Props) {
|
||||
<SettingsList.PressableItem
|
||||
destructive
|
||||
onPress={() => signOutPromptControl.open()}
|
||||
label={_(msg`Sign out`)}>
|
||||
label={l`Sign out`}>
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Sign out</Trans>
|
||||
</SettingsList.ItemText>
|
||||
@@ -281,7 +279,7 @@ export function SettingsScreen({}: Props) {
|
||||
}
|
||||
setShowDevOptions(d => !d)
|
||||
}}
|
||||
label={_(msg`Developer options`)}>
|
||||
label={l`Developer options`}>
|
||||
<SettingsList.ItemIcon icon={CodeBracketsIcon} />
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Developer options</Trans>
|
||||
@@ -295,11 +293,11 @@ export function SettingsScreen({}: Props) {
|
||||
|
||||
<Prompt.Basic
|
||||
control={signOutPromptControl}
|
||||
title={_(msg`Sign out?`)}
|
||||
description={_(msg`You will be signed out of all your accounts.`)}
|
||||
title={l`Sign out?`}
|
||||
description={l`You will be signed out of all your accounts.`}
|
||||
onConfirm={() => logoutEveryAccount('Settings')}
|
||||
confirmButtonCta={_(msg`Sign out`)}
|
||||
cancelButtonCta={_(msg`Cancel`)}
|
||||
confirmButtonCta={l`Sign out`}
|
||||
cancelButtonCta={l`Cancel`}
|
||||
confirmButtonColor="negative"
|
||||
/>
|
||||
|
||||
@@ -376,7 +374,7 @@ function ProfilePreview({
|
||||
}
|
||||
|
||||
function DevOptions() {
|
||||
const {_} = useLingui()
|
||||
const {t: l} = useLingui()
|
||||
const agent = useAgent()
|
||||
const [override, setOverride] = useStorage(device, [
|
||||
'policyUpdateDebugOverride',
|
||||
@@ -392,26 +390,26 @@ function DevOptions() {
|
||||
} = useApplyPullRequestOTAUpdate()
|
||||
const [actyNotifNudged, setActyNotifNudged] = useActivitySubscriptionsNudged()
|
||||
|
||||
const resetOnboarding = async () => {
|
||||
const resetOnboarding = () => {
|
||||
navigation.navigate('Home')
|
||||
onboardingDispatch({type: 'start'})
|
||||
Toast.show(_(msg`Onboarding reset`))
|
||||
Toast.show(l`Onboarding reset`)
|
||||
}
|
||||
|
||||
const clearAllStorage = async () => {
|
||||
await clearStorage()
|
||||
Toast.show(_(msg`Storage cleared, you need to restart the app now.`))
|
||||
Toast.show(l`Storage cleared, you need to restart the app now.`)
|
||||
}
|
||||
|
||||
const onPressUnsnoozeReminder = () => {
|
||||
const lastEmailConfirm = new Date()
|
||||
// wind back 3 days
|
||||
lastEmailConfirm.setDate(lastEmailConfirm.getDate() - 3)
|
||||
persisted.write('reminders', {
|
||||
void persisted.write('reminders', {
|
||||
...persisted.get('reminders'),
|
||||
lastEmailConfirm: lastEmailConfirm.toISOString(),
|
||||
})
|
||||
Toast.show(_(msg`You probably want to restart the app now.`))
|
||||
Toast.show(l`You probably want to restart the app now.`)
|
||||
}
|
||||
|
||||
const onPressActySubsUnNudge = () => {
|
||||
@@ -431,7 +429,7 @@ function DevOptions() {
|
||||
style: 'default',
|
||||
text: 'Apply',
|
||||
onPress: (channel?: string) => {
|
||||
tryApplyUpdate(channel ?? '')
|
||||
void tryApplyUpdate(channel ?? '')
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -446,42 +444,42 @@ function DevOptions() {
|
||||
<>
|
||||
<SettingsList.PressableItem
|
||||
onPress={() => navigation.navigate('Log')}
|
||||
label={_(msg`Open system log`)}>
|
||||
label={l`Open system log`}>
|
||||
<SettingsList.ItemText>
|
||||
<Trans>System log</Trans>
|
||||
</SettingsList.ItemText>
|
||||
</SettingsList.PressableItem>
|
||||
<SettingsList.PressableItem
|
||||
onPress={() => navigation.navigate('Debug')}
|
||||
label={_(msg`Open storybook page`)}>
|
||||
label={l`Open storybook page`}>
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Storybook</Trans>
|
||||
</SettingsList.ItemText>
|
||||
</SettingsList.PressableItem>
|
||||
<SettingsList.PressableItem
|
||||
onPress={() => navigation.navigate('DebugMod')}
|
||||
label={_(msg`Open moderation debug page`)}>
|
||||
label={l`Open moderation debug page`}>
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Debug Moderation</Trans>
|
||||
</SettingsList.ItemText>
|
||||
</SettingsList.PressableItem>
|
||||
<SettingsList.PressableItem
|
||||
onPress={() => deleteChatDeclarationRecord()}
|
||||
label={_(msg`Open storybook page`)}>
|
||||
label={l`Open storybook page`}>
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Delete chat declaration record</Trans>
|
||||
</SettingsList.ItemText>
|
||||
</SettingsList.PressableItem>
|
||||
<SettingsList.PressableItem
|
||||
onPress={() => resetOnboarding()}
|
||||
label={_(msg`Reset onboarding state`)}>
|
||||
onPress={() => void resetOnboarding()}
|
||||
label={l`Reset onboarding state`}>
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Reset onboarding state</Trans>
|
||||
</SettingsList.ItemText>
|
||||
</SettingsList.PressableItem>
|
||||
<SettingsList.PressableItem
|
||||
onPress={onPressUnsnoozeReminder}
|
||||
label={_(msg`Unsnooze email reminder`)}>
|
||||
label={l`Unsnooze email reminder`}>
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Unsnooze email reminder</Trans>
|
||||
</SettingsList.ItemText>
|
||||
@@ -489,15 +487,15 @@ function DevOptions() {
|
||||
{actyNotifNudged && (
|
||||
<SettingsList.PressableItem
|
||||
onPress={onPressActySubsUnNudge}
|
||||
label={_(msg`Reset activity subscription nudge`)}>
|
||||
label={l`Reset activity subscription nudge`}>
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Reset activity subscription nudge</Trans>
|
||||
</SettingsList.ItemText>
|
||||
</SettingsList.PressableItem>
|
||||
)}
|
||||
<SettingsList.PressableItem
|
||||
onPress={() => clearAllStorage()}
|
||||
label={_(msg`Clear all storage data`)}>
|
||||
onPress={() => void clearAllStorage()}
|
||||
label={l`Clear all storage data`}>
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Clear all storage data (restart after this)</Trans>
|
||||
</SettingsList.ItemText>
|
||||
@@ -505,7 +503,7 @@ function DevOptions() {
|
||||
{IS_IOS ? (
|
||||
<SettingsList.PressableItem
|
||||
onPress={onPressApplyOta}
|
||||
label={_(msg`Apply Pull Request`)}>
|
||||
label={l`Apply Pull Request`}>
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Apply Pull Request</Trans>
|
||||
</SettingsList.ItemText>
|
||||
@@ -513,8 +511,8 @@ function DevOptions() {
|
||||
) : null}
|
||||
{IS_NATIVE && isCurrentlyRunningPullRequestDeployment ? (
|
||||
<SettingsList.PressableItem
|
||||
onPress={revertToEmbedded}
|
||||
label={_(msg`Unapply Pull Request`)}>
|
||||
onPress={() => void revertToEmbedded()}
|
||||
label={l`Unapply Pull Request`}>
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Unapply Pull Request {currentChannel}</Trans>
|
||||
</SettingsList.ItemText>
|
||||
@@ -543,7 +541,7 @@ function DevOptions() {
|
||||
<Button
|
||||
onPress={() => {
|
||||
device.set([PolicyUpdate202508], false)
|
||||
agent.bskyAppRemoveNuxs([PolicyUpdate202508])
|
||||
void agent.bskyAppRemoveNuxs([PolicyUpdate202508])
|
||||
Toast.show(`Done`, {
|
||||
type: 'info',
|
||||
})
|
||||
@@ -562,7 +560,7 @@ function DevOptions() {
|
||||
}
|
||||
|
||||
function AddAccountRow() {
|
||||
const {_} = useLingui()
|
||||
const {t: l} = useLingui()
|
||||
const {setShowLoggedOut} = useLoggedOutViewControls()
|
||||
const closeEverything = useCloseAllActiveElements()
|
||||
|
||||
@@ -574,7 +572,7 @@ function AddAccountRow() {
|
||||
return (
|
||||
<SettingsList.PressableItem
|
||||
onPress={onAddAnotherAccount}
|
||||
label={_(msg`Add another account`)}>
|
||||
label={l`Add another account`}>
|
||||
<SettingsList.ItemIcon icon={PersonPlusIcon} />
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Add another account</Trans>
|
||||
@@ -597,7 +595,7 @@ function AccountRow({
|
||||
logContext: 'Settings',
|
||||
) => void
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
const {t: l} = useLingui()
|
||||
const t = useTheme()
|
||||
|
||||
const moderationOpts = useModerationOpts()
|
||||
@@ -614,7 +612,7 @@ function AccountRow({
|
||||
<View style={[a.relative]}>
|
||||
<SettingsList.PressableItem
|
||||
onPress={onSwitchAccount}
|
||||
label={_(msg`Switch account`)}>
|
||||
label={l`Switch account`}>
|
||||
{moderationOpts && profile ? (
|
||||
<UserAvatar
|
||||
size={28}
|
||||
@@ -636,7 +634,7 @@ function AccountRow({
|
||||
</SettingsList.PressableItem>
|
||||
{!pendingDid && (
|
||||
<Menu.Root>
|
||||
<Menu.Trigger label={_(msg`Account options`)}>
|
||||
<Menu.Trigger label={l`Account options`}>
|
||||
{({props, state}) => (
|
||||
<Pressable
|
||||
{...props}
|
||||
@@ -653,7 +651,7 @@ function AccountRow({
|
||||
</Menu.Trigger>
|
||||
<Menu.Outer showCancel>
|
||||
<Menu.Item
|
||||
label={_(msg`Remove account`)}
|
||||
label={l`Remove account`}
|
||||
onPress={() => removePromptControl.open()}>
|
||||
<Menu.ItemText>
|
||||
<Trans>Remove account</Trans>
|
||||
@@ -666,15 +664,13 @@ function AccountRow({
|
||||
|
||||
<Prompt.Basic
|
||||
control={removePromptControl}
|
||||
title={_(msg`Remove from quick access?`)}
|
||||
description={_(
|
||||
msg`This will remove @${account.handle} from the quick access list.`,
|
||||
)}
|
||||
title={l`Remove from quick access?`}
|
||||
description={l`This will remove @${account.handle} from the quick access list.`}
|
||||
onConfirm={() => {
|
||||
removeAccount(account)
|
||||
Toast.show(_(msg`Account removed from quick access`))
|
||||
Toast.show(l`Account removed from quick access`)
|
||||
}}
|
||||
confirmButtonCta={_(msg`Remove`)}
|
||||
confirmButtonCta={l`Remove`}
|
||||
confirmButtonColor="negative"
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -48,9 +48,9 @@ export function CaptchaWebView({
|
||||
return
|
||||
}
|
||||
onSuccess(code)
|
||||
} catch (e: unknown) {
|
||||
} catch (e) {
|
||||
// We don't actually want to record an error here, because this will happen quite a bit. We will only be able to
|
||||
// get hte href of the iframe if it's on our domain, so all the hcaptcha requests will throw here, although it's
|
||||
// get the href of the iframe if it's on our domain, so all the hcaptcha requests will throw here, although it's
|
||||
// harmless. Our other indicators of time-to-complete and back press should be more reliable in catching issues.
|
||||
}
|
||||
}, [stateParam, onSuccess, onError])
|
||||
|
||||
@@ -34,7 +34,7 @@ export function StepCaptchaNative() {
|
||||
const [ready, setReady] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
;(async () => {
|
||||
void (async () => {
|
||||
logger.debug('trying to generate attestation token...')
|
||||
try {
|
||||
if (IS_IOS) {
|
||||
@@ -48,7 +48,8 @@ export function StepCaptchaNative() {
|
||||
setToken(token)
|
||||
setPayload(base64UrlEncode(payload))
|
||||
}
|
||||
} catch (e: any) {
|
||||
} catch (err) {
|
||||
const e = err as Error
|
||||
logger.error(e)
|
||||
} finally {
|
||||
setReady(true)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import {useEffect, useRef, useState} from 'react'
|
||||
import {type TextInput, View} from 'react-native'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Plural, Trans} from '@lingui/react/macro'
|
||||
import {Plural, Trans, useLingui} from '@lingui/react/macro'
|
||||
import * as EmailValidator from 'email-validator'
|
||||
import type tldts from 'tldts'
|
||||
|
||||
@@ -60,7 +58,7 @@ export function StepInfo({
|
||||
refetchServer: () => void
|
||||
isLoadingStarterPack: boolean
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
const {t: l} = useLingui()
|
||||
const ax = useAnalytics()
|
||||
const {state, dispatch} = useSignupContext()
|
||||
const preemptivelyCompleteActivePolicyUpdate =
|
||||
@@ -94,12 +92,12 @@ export function StepInfo({
|
||||
const tldtsRef = useRef<typeof tldts>(undefined)
|
||||
useEffect(() => {
|
||||
// @ts-expect-error - valid path
|
||||
import('tldts/dist/index.cjs.min.js').then(tldts => {
|
||||
void import('tldts/dist/index.cjs.min.js').then(tldts => {
|
||||
tldtsRef.current = tldts
|
||||
})
|
||||
// This will get used in the avatar creator a few steps later, so lets preload it now
|
||||
// @ts-expect-error - valid path
|
||||
import('react-native-view-shot/src/index')
|
||||
void import('react-native-view-shot/src/index')
|
||||
}, [])
|
||||
|
||||
const onNextPress = () => {
|
||||
@@ -115,21 +113,21 @@ export function StepInfo({
|
||||
if (state.serviceDescription?.inviteCodeRequired && !inviteCode) {
|
||||
return dispatch({
|
||||
type: 'setError',
|
||||
value: _(msg`Please enter your invite code.`),
|
||||
value: l`Please enter your invite code.`,
|
||||
field: 'invite-code',
|
||||
})
|
||||
}
|
||||
if (!email) {
|
||||
return dispatch({
|
||||
type: 'setError',
|
||||
value: _(msg`Please enter your email.`),
|
||||
value: l`Please enter your email.`,
|
||||
field: 'email',
|
||||
})
|
||||
}
|
||||
if (!EmailValidator.validate(email)) {
|
||||
return dispatch({
|
||||
type: 'setError',
|
||||
value: _(msg`Your email appears to be invalid.`),
|
||||
value: l`Your email appears to be invalid.`,
|
||||
field: 'email',
|
||||
})
|
||||
}
|
||||
@@ -139,9 +137,7 @@ export function StepInfo({
|
||||
setHasWarnedEmail(true)
|
||||
return dispatch({
|
||||
type: 'setError',
|
||||
value: _(
|
||||
msg`Please double-check that you have entered your email address correctly.`,
|
||||
),
|
||||
value: l`Please double-check that you have entered your email address correctly.`,
|
||||
})
|
||||
}
|
||||
} else if (hasWarnedEmail) {
|
||||
@@ -151,14 +147,14 @@ export function StepInfo({
|
||||
if (!password) {
|
||||
return dispatch({
|
||||
type: 'setError',
|
||||
value: _(msg`Please choose your password.`),
|
||||
value: l`Please choose your password.`,
|
||||
field: 'password',
|
||||
})
|
||||
}
|
||||
if (password.length < 8) {
|
||||
return dispatch({
|
||||
type: 'setError',
|
||||
value: _(msg`Your password must be at least 8 characters long.`),
|
||||
value: l`Your password must be at least 8 characters long.`,
|
||||
field: 'password',
|
||||
})
|
||||
}
|
||||
@@ -205,7 +201,7 @@ export function StepInfo({
|
||||
dispatch({type: 'clearError'})
|
||||
}
|
||||
}}
|
||||
label={_(msg`Required for this provider`)}
|
||||
label={l`Required for this provider`}
|
||||
defaultValue={state.inviteCode}
|
||||
autoCapitalize="none"
|
||||
autoComplete="email"
|
||||
@@ -241,7 +237,7 @@ export function StepInfo({
|
||||
dispatch({type: 'clearError'})
|
||||
}
|
||||
}}
|
||||
label={_(msg`Enter your email address`)}
|
||||
label={l`Enter your email address`}
|
||||
defaultValue={state.email}
|
||||
autoCapitalize="none"
|
||||
autoComplete="email"
|
||||
@@ -269,7 +265,7 @@ export function StepInfo({
|
||||
dispatch({type: 'clearError'})
|
||||
}
|
||||
}}
|
||||
label={_(msg`Choose your password`)}
|
||||
label={l`Choose your password`}
|
||||
defaultValue={state.password}
|
||||
secureTextEntry
|
||||
autoComplete="new-password"
|
||||
@@ -297,8 +293,8 @@ export function StepInfo({
|
||||
value: sanitizeDate(new Date(date)),
|
||||
})
|
||||
}}
|
||||
label={_(msg`Date of birth`)}
|
||||
accessibilityHint={_(msg`Select your date of birth`)}
|
||||
label={l`Date of birth`}
|
||||
accessibilityHint={l`Select your date of birth`}
|
||||
maximumDate={new Date()}
|
||||
/>
|
||||
</View>
|
||||
@@ -331,9 +327,7 @@ export function StepInfo({
|
||||
<Trans>
|
||||
Have we got your location wrong?{' '}
|
||||
<SimpleInlineLinkText
|
||||
label={_(
|
||||
msg`Tap here to confirm your location with GPS.`,
|
||||
)}
|
||||
label={l`Tap here to confirm your location with GPS.`}
|
||||
{...createStaticClick(() => {
|
||||
locationControl.open()
|
||||
})}>
|
||||
@@ -363,7 +357,7 @@ export function StepInfo({
|
||||
props.closeDialog(() => {
|
||||
// set this after close!
|
||||
setDeviceGeolocation(props.geolocation)
|
||||
Toast.show(_(msg`Your location has been updated.`), {
|
||||
Toast.show(l`Your location has been updated.`, {
|
||||
type: 'success',
|
||||
})
|
||||
})
|
||||
@@ -380,7 +374,7 @@ export function StepInfo({
|
||||
onBackPress={onPressBack}
|
||||
onNextPress={onNextPress}
|
||||
onRetryPress={refetchServer}
|
||||
overrideNextText={hasWarnedEmail ? _(msg`It's correct`) : undefined}
|
||||
overrideNextText={hasWarnedEmail ? l`It's correct` : undefined}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -3,9 +3,7 @@ import {AppState, type AppStateStatus, View} from 'react-native'
|
||||
import ReactNativeDeviceAttest from 'react-native-device-attest'
|
||||
import Animated, {FadeIn, LayoutAnimationConfig} from 'react-native-reanimated'
|
||||
import {AppBskyGraphStarterpack} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {FEEDBACK_FORM_URL} from '#/lib/constants'
|
||||
import {logger} from '#/logger'
|
||||
@@ -36,7 +34,7 @@ import * as bsky from '#/types/bsky'
|
||||
|
||||
export function Signup({onPressBack}: {onPressBack: () => void}) {
|
||||
const ax = useAnalytics()
|
||||
const {_} = useLingui()
|
||||
const {t: l} = useLingui()
|
||||
const t = useTheme()
|
||||
const [state, dispatch] = useReducer(reducer, {
|
||||
...initialState,
|
||||
@@ -61,6 +59,7 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
|
||||
uri: activeStarterPack?.uri,
|
||||
})
|
||||
|
||||
// eslint-disable-next-line react/hook-use-state
|
||||
const [isFetchedAtMount] = useState(starterPack != null)
|
||||
const showStarterPackCard =
|
||||
activeStarterPack?.uri && !isFetchingStarterPack && starterPack
|
||||
@@ -85,21 +84,21 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
|
||||
dispatch({type: 'setServiceDescription', value: undefined})
|
||||
dispatch({
|
||||
type: 'setError',
|
||||
value: _(
|
||||
msg`Unable to contact your service. Please check your Internet connection.`,
|
||||
),
|
||||
value: l`Unable to contact your service. Please check your Internet connection.`,
|
||||
})
|
||||
} else if (serviceInfo) {
|
||||
dispatch({type: 'setServiceDescription', value: serviceInfo})
|
||||
dispatch({type: 'setError', value: ''})
|
||||
}
|
||||
}, [_, serviceInfo, isError])
|
||||
}, [l, serviceInfo, isError])
|
||||
|
||||
useEffect(() => {
|
||||
if (state.pendingSubmit) {
|
||||
if (!state.pendingSubmit.mutableProcessed) {
|
||||
// OK to mutate assuming it's never read in render.
|
||||
// eslint-disable-next-line react-hooks/immutability, react-compiler/react-compiler
|
||||
state.pendingSubmit.mutableProcessed = true
|
||||
submit(state, dispatch)
|
||||
void submit(state, dispatch)
|
||||
}
|
||||
}
|
||||
}, [state, dispatch, submit])
|
||||
@@ -133,8 +132,8 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
|
||||
<SignupContext.Provider value={{state, dispatch}}>
|
||||
<LoggedOutLayout
|
||||
leadin=""
|
||||
title={_(msg`Create Account`)}
|
||||
description={_(msg`We're so excited to have you join us!`)}
|
||||
title={l`Create account`}
|
||||
description={l`We’re so excited to have you join us!`}
|
||||
scrollable>
|
||||
<View testID="createAccount" style={a.flex_1}>
|
||||
{showStarterPackCard &&
|
||||
@@ -204,7 +203,7 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
|
||||
isFetchingStarterPack && !isErrorStarterPack
|
||||
}
|
||||
isServerError={isError}
|
||||
refetchServer={refetch}
|
||||
refetchServer={() => void refetch()}
|
||||
/>
|
||||
) : state.activeStep === SignupStep.HANDLE ? (
|
||||
<StepHandle />
|
||||
@@ -231,7 +230,7 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
|
||||
]}>
|
||||
<Trans>Having trouble?</Trans>{' '}
|
||||
<InlineLinkText
|
||||
label={_(msg`Contact support`)}
|
||||
label={l`Contact support`}
|
||||
to={FEEDBACK_FORM_URL({email: state.email})}
|
||||
style={[!gtMobile && a.text_md]}>
|
||||
<Trans>Contact support</Trans>
|
||||
|
||||
+14
-14
@@ -4,8 +4,7 @@ import {
|
||||
ComAtprotoServerCreateAccount,
|
||||
type ComAtprotoServerDescribeServer,
|
||||
} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
import * as EmailValidator from 'email-validator'
|
||||
|
||||
import {DEFAULT_SERVICE} from '#/lib/constants'
|
||||
@@ -18,7 +17,9 @@ import {type AnalyticsContextType, useAnalytics} from '#/analytics'
|
||||
|
||||
export type ServiceDescription = ComAtprotoServerDescribeServer.OutputSchema
|
||||
|
||||
const DEFAULT_DATE = new Date(Date.now() - 60e3 * 60 * 24 * 365 * 20) // default to 20 years ago
|
||||
const date = new Date()
|
||||
date.setFullYear(date.getFullYear() - 20) // default to 20 years ago
|
||||
const DEFAULT_DATE = date
|
||||
|
||||
export enum SignupStep {
|
||||
INFO,
|
||||
@@ -256,7 +257,7 @@ export const useSignupContext = () => useContext(SignupContext)
|
||||
|
||||
export function useSubmitSignup() {
|
||||
const ax = useAnalytics()
|
||||
const {_} = useLingui()
|
||||
const {t: l} = useLingui()
|
||||
const {createAccount} = useSessionApi()
|
||||
const onboardingDispatch = useOnboardingDispatch()
|
||||
|
||||
@@ -266,7 +267,7 @@ export function useSubmitSignup() {
|
||||
dispatch({type: 'setStep', value: SignupStep.INFO})
|
||||
return dispatch({
|
||||
type: 'setError',
|
||||
value: _(msg`Please enter your email.`),
|
||||
value: l`Please enter your email.`,
|
||||
field: 'email',
|
||||
})
|
||||
}
|
||||
@@ -274,7 +275,7 @@ export function useSubmitSignup() {
|
||||
dispatch({type: 'setStep', value: SignupStep.INFO})
|
||||
return dispatch({
|
||||
type: 'setError',
|
||||
value: _(msg`Your email appears to be invalid.`),
|
||||
value: l`Your email appears to be invalid.`,
|
||||
field: 'email',
|
||||
})
|
||||
}
|
||||
@@ -282,7 +283,7 @@ export function useSubmitSignup() {
|
||||
dispatch({type: 'setStep', value: SignupStep.INFO})
|
||||
return dispatch({
|
||||
type: 'setError',
|
||||
value: _(msg`Please choose your password.`),
|
||||
value: l`Please choose your password.`,
|
||||
field: 'password',
|
||||
})
|
||||
}
|
||||
@@ -290,7 +291,7 @@ export function useSubmitSignup() {
|
||||
dispatch({type: 'setStep', value: SignupStep.HANDLE})
|
||||
return dispatch({
|
||||
type: 'setError',
|
||||
value: _(msg`Please choose your handle.`),
|
||||
value: l`Please choose your handle.`,
|
||||
field: 'handle',
|
||||
})
|
||||
}
|
||||
@@ -305,7 +306,7 @@ export function useSubmitSignup() {
|
||||
})
|
||||
return dispatch({
|
||||
type: 'setError',
|
||||
value: _(msg`Please complete the verification captcha.`),
|
||||
value: l`Please complete the verification captcha.`,
|
||||
})
|
||||
}
|
||||
dispatch({type: 'setError', value: ''})
|
||||
@@ -337,14 +338,13 @@ export function useSubmitSignup() {
|
||||
* createAccount fails, one tab is not stuck in onboarding — Eric
|
||||
*/
|
||||
onboardingDispatch({type: 'start'})
|
||||
} catch (e: any) {
|
||||
} catch (err) {
|
||||
const e = err as Error
|
||||
let errMsg = e.toString()
|
||||
if (e instanceof ComAtprotoServerCreateAccount.InvalidInviteCodeError) {
|
||||
dispatch({
|
||||
type: 'setError',
|
||||
value: _(
|
||||
msg`Invite code not accepted. Check that you input it correctly and try again.`,
|
||||
),
|
||||
value: l`Invite code not accepted. Check that you input it correctly and try again.`,
|
||||
field: 'invite-code',
|
||||
})
|
||||
dispatch({type: 'setStep', value: SignupStep.INFO})
|
||||
@@ -370,6 +370,6 @@ export function useSubmitSignup() {
|
||||
dispatch({type: 'setIsLoading', value: false})
|
||||
}
|
||||
},
|
||||
[_, onboardingDispatch, createAccount],
|
||||
[l, ax.logger, createAccount, onboardingDispatch],
|
||||
)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ const BIRTHDATE_DELAY_HOURS = IS_DEV ? 0.001 : 48
|
||||
* Stores the timestamp of the birthday update locally. This is used to
|
||||
* debounce birthday updates globally.
|
||||
*
|
||||
* Use {@link useIsBirthDateUpdateAllowed} to check if an update is allowed.
|
||||
* Use {@link useIsBirthdateUpdateAllowed} to check if an update is allowed.
|
||||
*/
|
||||
export function snoozeBirthdateUpdateAllowedForDid(did: string) {
|
||||
account.set([did, 'birthdateLastUpdatedAt'], new Date().toISOString())
|
||||
|
||||
@@ -204,6 +204,7 @@ export async function compressImage(
|
||||
img: ComposerImage,
|
||||
options?: {
|
||||
highResolution?: boolean
|
||||
increasedBlobSizeLimit?: boolean
|
||||
},
|
||||
): Promise<PickerImage> {
|
||||
const source = img.transformed || img.source
|
||||
@@ -211,6 +212,7 @@ export async function compressImage(
|
||||
|
||||
let attempts = 0
|
||||
let maxDimension = highResolution ? 4000 : POST_IMG_MAX.width
|
||||
let maxBytes = options?.increasedBlobSizeLimit ? 2000000 : POST_IMG_MAX.size
|
||||
|
||||
let minQualityPercentage = 0
|
||||
let maxQualityPercentage = 101 // exclusive
|
||||
@@ -251,7 +253,7 @@ export async function compressImage(
|
||||
|
||||
const base64 = res.base64
|
||||
const size = base64 ? getDataUriSize(base64) : 0
|
||||
if (base64 && size <= POST_IMG_MAX.size) {
|
||||
if (base64 && size <= maxBytes) {
|
||||
minQualityPercentage = qualityPercentage
|
||||
newDataUri = {
|
||||
path: await moveIfNecessary(res.uri),
|
||||
@@ -275,7 +277,7 @@ export async function compressImage(
|
||||
async function moveIfNecessary(from: string) {
|
||||
const cacheDir = IS_NATIVE && getImageCacheDirectory()
|
||||
|
||||
if (cacheDir && from.startsWith(cacheDir)) {
|
||||
if (cacheDir && !from.startsWith(cacheDir)) {
|
||||
const to = joinPath(cacheDir, nanoid(36))
|
||||
|
||||
await makeDirectoryAsync(cacheDir, {intermediates: true})
|
||||
|
||||
@@ -52,7 +52,7 @@ export function useUpdateActorDeclaration({
|
||||
onError: error => {
|
||||
logger.error(error)
|
||||
if (currentAccount) {
|
||||
queryClient.invalidateQueries({
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: PROFILE_RKEY(currentAccount.did),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import {useCallback} from 'react'
|
||||
import {type AppBskyActorDefs, type AppBskyFeedDefs, AtUri} from '@atproto/api'
|
||||
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
|
||||
import {
|
||||
type QueryClient,
|
||||
useMutation,
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
} from '@tanstack/react-query'
|
||||
|
||||
import {useToggleMutationQueue} from '#/lib/hooks/useToggleMutationQueue'
|
||||
import {updatePostShadow} from '#/state/cache/post-shadow'
|
||||
@@ -43,6 +48,14 @@ export function usePostQuery(uri: string | undefined) {
|
||||
})
|
||||
}
|
||||
|
||||
export function precachePost(
|
||||
queryClient: QueryClient,
|
||||
uri: string,
|
||||
post: AppBskyFeedDefs.PostView,
|
||||
) {
|
||||
queryClient.setQueryData(RQKEY(uri), post)
|
||||
}
|
||||
|
||||
export function useGetPost() {
|
||||
const queryClient = useQueryClient()
|
||||
const agent = useAgent()
|
||||
|
||||
+80
-83
@@ -181,65 +181,64 @@ export async function createAgentAndCreateAccount(
|
||||
// Not awaited so that we can still get into onboarding.
|
||||
// This is OK because we won't let you toggle adult stuff until you set the date.
|
||||
if (IS_PROD_SERVICE(service)) {
|
||||
Promise.allSettled(
|
||||
[
|
||||
networkRetry(3, () => {
|
||||
return agent.setPersonalDetails({
|
||||
birthDate: birthdate,
|
||||
})
|
||||
}).catch(e => {
|
||||
logger.info(`createAgentAndCreateAccount: failed to set birthDate`)
|
||||
throw e
|
||||
}),
|
||||
networkRetry(3, () => {
|
||||
return agent.upsertProfile(prev => {
|
||||
const next: Un$Typed<AppBskyActorProfile.Record> = prev || {}
|
||||
next.displayName = handle
|
||||
next.createdAt = createdAt
|
||||
return next
|
||||
})
|
||||
}).catch(e => {
|
||||
logger.info(
|
||||
`createAgentAndCreateAccount: failed to set initial profile`,
|
||||
)
|
||||
throw e
|
||||
}),
|
||||
networkRetry(1, () => {
|
||||
return agent.overwriteSavedFeeds([
|
||||
{
|
||||
...DISCOVER_SAVED_FEED,
|
||||
id: TID.nextStr(),
|
||||
},
|
||||
{
|
||||
...TIMELINE_SAVED_FEED,
|
||||
id: TID.nextStr(),
|
||||
},
|
||||
])
|
||||
}).catch(e => {
|
||||
logger.info(
|
||||
`createAgentAndCreateAccount: failed to set initial feeds`,
|
||||
)
|
||||
throw e
|
||||
}),
|
||||
getAge(birthDate) < 18 &&
|
||||
networkRetry(3, () => {
|
||||
return agent.com.atproto.repo.putRecord({
|
||||
repo: account.did,
|
||||
collection: 'chat.bsky.actor.declaration',
|
||||
rkey: 'self',
|
||||
record: {
|
||||
$type: 'chat.bsky.actor.declaration',
|
||||
allowIncoming: 'none',
|
||||
},
|
||||
})
|
||||
}).catch(e => {
|
||||
logger.info(
|
||||
`createAgentAndCreateAccount: failed to set chat declaration`,
|
||||
)
|
||||
throw e
|
||||
}),
|
||||
].filter(Boolean),
|
||||
).then(promises => {
|
||||
void Promise.allSettled([
|
||||
networkRetry(3, () => {
|
||||
return agent.setPersonalDetails({
|
||||
birthDate: birthdate,
|
||||
})
|
||||
}).catch(e => {
|
||||
logger.info(`createAgentAndCreateAccount: failed to set birthDate`)
|
||||
throw e
|
||||
}),
|
||||
networkRetry(3, () => {
|
||||
return agent.upsertProfile(prev => {
|
||||
const next: Un$Typed<AppBskyActorProfile.Record> = prev || {}
|
||||
next.displayName = handle
|
||||
next.createdAt = createdAt
|
||||
return next
|
||||
})
|
||||
}).catch(e => {
|
||||
logger.info(
|
||||
`createAgentAndCreateAccount: failed to set initial profile`,
|
||||
)
|
||||
throw e
|
||||
}),
|
||||
networkRetry(1, () => {
|
||||
return agent.overwriteSavedFeeds([
|
||||
{
|
||||
...DISCOVER_SAVED_FEED,
|
||||
id: TID.nextStr(),
|
||||
},
|
||||
{
|
||||
...TIMELINE_SAVED_FEED,
|
||||
id: TID.nextStr(),
|
||||
},
|
||||
])
|
||||
}).catch(e => {
|
||||
logger.info(`createAgentAndCreateAccount: failed to set initial feeds`)
|
||||
throw e
|
||||
}),
|
||||
...(getAge(birthDate) < 18
|
||||
? [
|
||||
networkRetry(3, () => {
|
||||
return agent.com.atproto.repo.putRecord({
|
||||
repo: account.did,
|
||||
collection: 'chat.bsky.actor.declaration',
|
||||
rkey: 'self',
|
||||
record: {
|
||||
$type: 'chat.bsky.actor.declaration',
|
||||
allowIncoming: 'none',
|
||||
},
|
||||
})
|
||||
}).catch(e => {
|
||||
logger.info(
|
||||
`createAgentAndCreateAccount: failed to set chat declaration`,
|
||||
)
|
||||
throw e
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
]).then(promises => {
|
||||
const rejected = promises.filter(p => p.status === 'rejected')
|
||||
if (rejected.length > 0) {
|
||||
logger.error(
|
||||
@@ -248,30 +247,28 @@ export async function createAgentAndCreateAccount(
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Promise.allSettled(
|
||||
[
|
||||
networkRetry(3, () => {
|
||||
return agent.setPersonalDetails({
|
||||
birthDate: birthDate.toISOString(),
|
||||
})
|
||||
}).catch(e => {
|
||||
logger.info(`createAgentAndCreateAccount: failed to set birthDate`)
|
||||
throw e
|
||||
}),
|
||||
networkRetry(3, () => {
|
||||
return agent.upsertProfile(prev => {
|
||||
const next: Un$Typed<AppBskyActorProfile.Record> = prev || {}
|
||||
next.createdAt = prev?.createdAt || new Date().toISOString()
|
||||
return next
|
||||
})
|
||||
}).catch(e => {
|
||||
logger.info(
|
||||
`createAgentAndCreateAccount: failed to set initial profile`,
|
||||
)
|
||||
throw e
|
||||
}),
|
||||
].filter(Boolean),
|
||||
).then(promises => {
|
||||
void Promise.allSettled([
|
||||
networkRetry(3, () => {
|
||||
return agent.setPersonalDetails({
|
||||
birthDate: birthDate.toISOString(),
|
||||
})
|
||||
}).catch(e => {
|
||||
logger.info(`createAgentAndCreateAccount: failed to set birthDate`)
|
||||
throw e
|
||||
}),
|
||||
networkRetry(3, () => {
|
||||
return agent.upsertProfile(prev => {
|
||||
const next: Un$Typed<AppBskyActorProfile.Record> = prev || {}
|
||||
next.createdAt = prev?.createdAt || new Date().toISOString()
|
||||
return next
|
||||
})
|
||||
}).catch(e => {
|
||||
logger.info(
|
||||
`createAgentAndCreateAccount: failed to set initial profile`,
|
||||
)
|
||||
throw e
|
||||
}),
|
||||
]).then(promises => {
|
||||
const rejected = promises.filter(p => p.status === 'rejected')
|
||||
if (rejected.length > 0) {
|
||||
logger.error(
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
useState,
|
||||
useSyncExternalStore,
|
||||
} from 'react'
|
||||
import {type AtpSessionEvent, type BskyAgent} from '@atproto/api'
|
||||
import {type AtpAgent, type AtpSessionEvent} from '@atproto/api'
|
||||
|
||||
import * as persisted from '#/state/persisted'
|
||||
import {useCloseAllActiveElements} from '#/state/util'
|
||||
@@ -47,7 +47,7 @@ const StateContext = createContext<SessionStateContext>({
|
||||
})
|
||||
StateContext.displayName = 'SessionStateContext'
|
||||
|
||||
const AgentContext = createContext<BskyAgent | null>(null)
|
||||
const AgentContext = createContext<AtpAgent | null>(null)
|
||||
AgentContext.displayName = 'SessionAgentContext'
|
||||
|
||||
const ApiContext = createContext<SessionApiContext>({
|
||||
@@ -96,7 +96,7 @@ class SessionStore {
|
||||
),
|
||||
}
|
||||
addSessionDebugLog({type: 'persisted:broadcast', data: persistedData})
|
||||
persisted.write('session', persistedData)
|
||||
void persisted.write('session', persistedData)
|
||||
}
|
||||
this.listeners.forEach(listener => listener())
|
||||
}
|
||||
@@ -105,12 +105,13 @@ class SessionStore {
|
||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
const ax = useAnalyticsBase()
|
||||
const cancelPendingTask = useOneTaskAtATime()
|
||||
// eslint-disable-next-line react/hook-use-state
|
||||
const [store] = useState(() => new SessionStore())
|
||||
const state = useSyncExternalStore(store.subscribe, store.getState)
|
||||
const onboardingDispatch = useOnboardingDispatch()
|
||||
|
||||
const onAgentSessionChange = useCallback(
|
||||
(agent: BskyAgent, accountDid: string, sessionEvent: AtpSessionEvent) => {
|
||||
(agent: AtpAgent, accountDid: string, sessionEvent: AtpSessionEvent) => {
|
||||
const refreshedAccount = agentToSessionAccount(agent) // Mutable, so snapshot it right away.
|
||||
if (sessionEvent === 'expired' || sessionEvent === 'create-failed') {
|
||||
emitSessionDropped()
|
||||
@@ -327,10 +328,11 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
* follower tabs. Follower tabs will therefore receive the fresh
|
||||
* session. See APP-1960, or ask Eric.
|
||||
*/
|
||||
resumeSession(syncedAccount)
|
||||
void resumeSession(syncedAccount)
|
||||
} else {
|
||||
const agent = state.currentAgentState.agent as BskyAgent
|
||||
const agent = state.currentAgentState.agent as AtpAgent
|
||||
const prevSession = agent.session
|
||||
// eslint-disable-next-line react-compiler/react-compiler
|
||||
agent.sessionManager.session = sessionAccountToSession(syncedAccount)
|
||||
addSessionDebugLog({
|
||||
type: 'agent:patch',
|
||||
@@ -376,6 +378,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
)
|
||||
|
||||
// @ts-expect-error window type is not declared, debug only
|
||||
// eslint-disable-next-line react-hooks/immutability
|
||||
if (__DEV__ && IS_WEB) window.agent = state.currentAgentState.agent
|
||||
|
||||
const agent = state.currentAgentState.agent as BskyAppAgent
|
||||
@@ -448,7 +451,7 @@ export function useRequireAuth() {
|
||||
)
|
||||
}
|
||||
|
||||
export function useAgent(): BskyAgent {
|
||||
export function useAgent(): AtpAgent {
|
||||
const agent = useContext(AgentContext)
|
||||
if (!agent) {
|
||||
throw Error('useAgent() must be below <SessionProvider>.')
|
||||
|
||||
@@ -838,6 +838,9 @@ export const ComposePost = ({
|
||||
highResolutionImages: ax.features.enabled(
|
||||
ax.features.ImageUploadsHighResolution,
|
||||
),
|
||||
increasedBlobSizeLimit: ax.features.enabled(
|
||||
ax.features.ImageUploadsBlobSize2mbEnabled,
|
||||
),
|
||||
},
|
||||
)
|
||||
).uris[0]
|
||||
|
||||
Reference in New Issue
Block a user