Merge remote-tracking branch 'origin/main' into next/base
* origin/main: (39 commits) Revert "fix quote & feed padding not being pressable (#7694)" (#7733) Nightly source-language update Add dev mode for easy copying of at:// URIs and DIDs (#7723) Improved search language select (#7591) Improved search page (#7590) Animate drawer menu on mobile web (#7711) Special treatment for recommended starter packs (#7706) invert new postonboarding gate (#7695) Animate dropdown menus (#7704) Better animations for dialogs, animate web composer (#7703) fix quote & feed padding not being pressable (#7694) ungate trending (#7696) run prettier (#7727) Nightly source-language update Add deploy key to nightly commit (#7722) [APP-1031] Add new followerRule to threadgate settings (#7681) Fix bug in GH action nightly-update-source-languages.yaml More i18n process updates (#7720) Run intl:extract on english to see if crowdin picks up on the changes Refine accessibilityHint (#7554) ...
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
name: Nightly Update Source Languages
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 2 * * *' # run at 2 AM UTC
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
extract-languages:
|
||||
name: Nightly Update Source Languages
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
# Give the default GITHUB_TOKEN write permission to commit and push the
|
||||
# added or changed files to the repository.
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ssh-key: ${{secrets.GH_ACTION_DEPLOY_KEY}}
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- name: Yarn install
|
||||
uses: Wandalen/wretry.action@master
|
||||
with:
|
||||
command: yarn --frozen-lockfile
|
||||
attempt_limit: 3
|
||||
attempt_delay: 2000
|
||||
- name: Extract language strings
|
||||
run: yarn intl:extract
|
||||
- name: Create commit
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: Nightly source-language update
|
||||
file_pattern: ./src/locale/locales/en/messages.po
|
||||
@@ -0,0 +1,42 @@
|
||||
import {IsValidHandle, validateServiceHandle} from '#/lib/strings/handles'
|
||||
|
||||
describe('handle validation', () => {
|
||||
const valid = [
|
||||
['ali', 'bsky.social'],
|
||||
['alice', 'bsky.social'],
|
||||
['a-lice', 'bsky.social'],
|
||||
['a-----lice', 'bsky.social'],
|
||||
['123', 'bsky.social'],
|
||||
['123456789012345678', 'bsky.social'],
|
||||
['alice', 'custom-pds.com'],
|
||||
['alice', 'my-custom-pds-with-long-name.social'],
|
||||
['123456789012345678', 'my-custom-pds-with-long-name.social'],
|
||||
]
|
||||
it.each(valid)(`should be valid: %s.%s`, (handle, service) => {
|
||||
const result = validateServiceHandle(handle, service)
|
||||
expect(result.overall).toEqual(true)
|
||||
})
|
||||
|
||||
const invalid = [
|
||||
['al', 'bsky.social', 'frontLength'],
|
||||
['-alice', 'bsky.social', 'hyphenStartOrEnd'],
|
||||
['alice-', 'bsky.social', 'hyphenStartOrEnd'],
|
||||
['%%%', 'bsky.social', 'handleChars'],
|
||||
['1234567890123456789', 'bsky.social', 'frontLength'],
|
||||
[
|
||||
'1234567890123456789',
|
||||
'my-custom-pds-with-long-name.social',
|
||||
'frontLength',
|
||||
],
|
||||
['al', 'my-custom-pds-with-long-name.social', 'frontLength'],
|
||||
['a'.repeat(300), 'toolong.com', 'totalLength'],
|
||||
] satisfies [string, string, keyof IsValidHandle][]
|
||||
it.each(invalid)(
|
||||
`should be invalid: %s.%s due to %s`,
|
||||
(handle, service, expectedError) => {
|
||||
const result = validateServiceHandle(handle, service)
|
||||
expect(result.overall).toEqual(false)
|
||||
expect(result[expectedError]).toEqual(false)
|
||||
},
|
||||
)
|
||||
})
|
||||
@@ -86,6 +86,7 @@ module.exports = function (config) {
|
||||
'gl',
|
||||
'hi',
|
||||
'hu',
|
||||
'ia',
|
||||
'id',
|
||||
'it',
|
||||
'ja',
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M11.293 4.293a1 1 0 0 1 1.414 0l4 4a1 1 0 0 1-1.414 1.414L12 6.414 8.707 9.707a1 1 0 0 1-1.414-1.414l4-4Zm-4 10a1 1 0 0 1 1.414 0L12 17.586l3.293-3.293a1 1 0 0 1 1.414 1.414l-4 4a1 1 0 0 1-1.414 0l-4-4a1 1 0 0 1 0-1.414Z" clip-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 363 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M7 2a1 1 0 0 1 1 1v2a1 1 0 0 1-2 0V3a1 1 0 0 1 1-1Zm4 0a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V3a1 1 0 0 1 1-1Zm4 0a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V3a1 1 0 0 1 1-1ZM4 9a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2h.5a3.5 3.5 0 1 1 0 7H18v4a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V9Zm12 0H6v11h10V9Zm2 5h.5a1.5 1.5 0 0 0 0-3H18v3Z" clip-rule="evenodd"/></svg>
|
||||
|
Before Width: | Height: | Size: 437 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M6.5 4.999Zm0 0a.1.1 0 0 1-.016.024.2.2 0 0 1-.152.043q.086.014.168.032V7.5a1 1 0 0 1-.33.742c-.543.49-.917 1.176-1.23 2.036a1 1 0 0 1-.94.657H3V14h1a1 1 0 0 1 .866.5c.436.754.879 1.195 1.637 1.636A1 1 0 0 1 7 17v2h2a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2h2v-2.537a1 1 0 0 1 .332-.744A4.98 4.98 0 0 0 19 11.999 5 5 0 0 0 14 7H9.474a1 1 0 0 1-.893-.55v-.001l-.001-.002-.005-.008L9.474 6l-.9.438h.001v.002l.002.001.001.003.002.003v.003a2.6 2.6 0 0 0-.443-.538c-.319-.298-.839-.648-1.637-.814V5Zm2.082 1.452ZM9.5 4.447c.211.196.379.387.508.553H14a7 7 0 0 1 6.72 8.96 1.003 1.003 0 0 0 1.146-1.46 1 1 0 1 1 1.731-1 3 3 0 0 1-3.714 4.286A7 7 0 0 1 19 16.888V19a2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2h-2a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-1.45A5.8 5.8 0 0 1 3.448 16H3a2 2 0 0 1-2-2v-3.065a2 2 0 0 1 2-2h.324c.286-.649.657-1.291 1.176-1.852V5c0-1.047.89-2.12 2.161-1.907 1.33.223 2.248.805 2.839 1.354ZM8.25 12a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5Z" clip-rule="evenodd"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M12 4c0-1.012.894-2.187 2.237-1.846a5.002 5.002 0 0 1 3.218 7.119 4 4 0 0 1 2.345 4.976A3.501 3.501 0 0 1 18.5 21h-13a3.5 3.5 0 0 1-1.3-6.75 4 4 0 0 1 2.052-4.848A4 4 0 0 1 10 4h2.001Zm-4.187 7.009A2 2 0 0 0 6 13c0 .366.098.706.271 1H12a1 1 0 1 1 0 2H5.5a1.5 1.5 0 0 0 0 3h13a1.5 1.5 0 1 0 0-3H17a1 1 0 1 1 0-2h.729A1.96 1.96 0 0 0 18 13a2 2 0 0 0-2-2h-2a1 1 0 1 1 0-2h1.236a3.002 3.002 0 0 0-1.243-4.832A2 2 0 0 1 12 6h-2a2 2 0 0 0-1.728 3.009H9a1 1 0 0 1 0 2H7.813Z" clip-rule="evenodd"/></svg>
|
||||
|
Before Width: | Height: | Size: 610 B |
+20
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"project_id": "664276",
|
||||
"base_path": ".",
|
||||
"preserve_hierarchy": true,
|
||||
"files": [
|
||||
{
|
||||
"source": "/src/locale/locales/en/messages.po",
|
||||
"translation": "/src/locale/locales/%two_letters_code%/messages.po",
|
||||
"languages_mapping": {
|
||||
"two_letters_code": {
|
||||
"pt-BR": "pt-BR",
|
||||
"en-GB": "en-GB",
|
||||
"zh-CN": "zh-CN",
|
||||
"zh-TW": "zh-TW",
|
||||
"zh-HK": "zh-HK"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+57
-7
@@ -3,9 +3,64 @@
|
||||
We want the official Bluesky app to be supported in as many languages as possible. If you want to help us translate the app, please open a PR or issue on the [Bluesky app repo on GitHub](https://github.com/bluesky-social/social-app)
|
||||
|
||||
## Tools
|
||||
We are using Lingui to manage translations. You can find the documentation [here](https://lingui.dev/).
|
||||
|
||||
- We use Lingui to implement translations. You can find the documentation [here](https://lingui.dev/).
|
||||
- We use Crowdin to manage translations.
|
||||
- Bluesky Crowdin: https://crowdin.com/project/bluesky-social
|
||||
- Introduction to Crowdin: https://support.crowdin.com/for-translators/
|
||||
|
||||
## Translators
|
||||
|
||||
Much of the app is translated by community contributions. (We <3 our translators!) If you want to participate in the translation of the app, read this section.
|
||||
|
||||
### Using Crowdin
|
||||
|
||||
[Crowdin](https://crowdin.com/project/bluesky-social) is our primary tool for managing translations. There are two roles:
|
||||
|
||||
- **Proof-readers**. Can create new translations and approve submitted translations.
|
||||
- **Translators**. Can create new translations.
|
||||
|
||||
All translations must be approved by proof-readers before they are accepted into the app.
|
||||
|
||||
### Using other platforms
|
||||
|
||||
You may contribute PRs separately from Crowdin, however we strongly recommend using Crowdin to avoid conflicts.
|
||||
|
||||
### Code of conduct on Crowdin
|
||||
|
||||
Please treat everyone with respect. Proof-readers are given final say on translations. Translators who frequently come into conflict with other translators, or who contribute noticably incorrect translations, will have their membership to the Crowdin project revoked.
|
||||
|
||||
### Adding a new language
|
||||
|
||||
Create a new [Crowdin discussion](https://crowdin.com/project/bluesky-social/discussions) or [GitHub issue](https://github.com/bluesky-social/social-app/issues) requesting the new language be added to the project.
|
||||
|
||||
Please only request a new language when you are certain you will be able to contribute a substantive portion of translations for the language.
|
||||
|
||||
## Maintainers
|
||||
|
||||
Install the [Crowdin CLI](https://crowdin.github.io/crowdin-cli/). You will need to [configure your API token](https://crowdin.github.io/crowdin-cli/configuration) to access the project.
|
||||
|
||||
### English source-file sync with Crowdin
|
||||
|
||||
Every night, a GitHub action will run `yarn intl:extract` to update the english `messages.po` file. This will be automatically synced with Crowdin. Crowdin should notify all subscribed users of new translations.
|
||||
|
||||
### Release process
|
||||
|
||||
1. Pull main and create a branch.
|
||||
1. Run `yarn intl:pull` to fetch all translation updates from Crowdin.
|
||||
1. Create a PR, ensure the translations all look correct, and merge.
|
||||
1. If needed:
|
||||
1. Merge all approved translation PRs (contributions from outside crowdin).
|
||||
1. Run `yarn intl:push` to sync Crowdin with the state of the repo.
|
||||
|
||||
### Testing the translations in Crowdin
|
||||
|
||||
You can run `yarn intl:pull` to pull the currently-approved translations from Crowdin.
|
||||
|
||||
## Developers
|
||||
|
||||
### Adding new strings
|
||||
|
||||
When adding a new string, do it as follows:
|
||||
```jsx
|
||||
// Before
|
||||
@@ -61,6 +116,7 @@ So the workflow is as follows:
|
||||
6. Enjoy translated app!
|
||||
|
||||
### Common pitfalls
|
||||
|
||||
These pitfalls are memoization pitfalls that will cause the components to not re-render when the locale is changed -- causing stale translations to be shown.
|
||||
|
||||
```jsx
|
||||
@@ -111,9 +167,3 @@ export function Welcome() {
|
||||
return <div>{welcome}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Credits
|
||||
Please check each individual `messages.po` file for the credits of the translators. We are very grateful for their help!
|
||||
|
||||
If you would like to translate the Bluesky app into your language, please open a PR or issue on this repo.
|
||||
|
||||
@@ -17,6 +17,7 @@ module.exports = {
|
||||
'gl',
|
||||
'hi',
|
||||
'hu',
|
||||
'ia',
|
||||
'id',
|
||||
'it',
|
||||
'ja',
|
||||
|
||||
@@ -70,6 +70,7 @@ import {MessagesScreen} from '#/screens/Messages/ChatList'
|
||||
import {MessagesConversationScreen} from '#/screens/Messages/Conversation'
|
||||
import {MessagesSettingsScreen} from '#/screens/Messages/Settings'
|
||||
import {ModerationScreen} from '#/screens/Moderation'
|
||||
import {Screen as ModerationInteractionSettings} from '#/screens/ModerationInteractionSettings'
|
||||
import {PostLikedByScreen} from '#/screens/Post/PostLikedBy'
|
||||
import {PostQuotesScreen} from '#/screens/Post/PostQuotes'
|
||||
import {PostRepostedByScreen} from '#/screens/Post/PostRepostedBy'
|
||||
@@ -155,6 +156,14 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) {
|
||||
getComponent={() => ModerationBlockedAccounts}
|
||||
options={{title: title(msg`Blocked Accounts`), requireAuth: true}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="ModerationInteractionSettings"
|
||||
getComponent={() => ModerationInteractionSettings}
|
||||
options={{
|
||||
title: title(msg`Post Interaction Settings`),
|
||||
requireAuth: true,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Settings"
|
||||
getComponent={() => SettingsScreen}
|
||||
|
||||
+35
-2
@@ -50,6 +50,12 @@ export const atoms = {
|
||||
overflow_hidden: {
|
||||
overflow: 'hidden',
|
||||
},
|
||||
/**
|
||||
* @platform web
|
||||
*/
|
||||
overflow_auto: web({
|
||||
overflow: 'auto',
|
||||
}),
|
||||
|
||||
/*
|
||||
* Width
|
||||
@@ -68,7 +74,6 @@ export const atoms = {
|
||||
* Used for the outermost components on screens, to ensure that they can fill
|
||||
* the screen and extend beyond.
|
||||
*/
|
||||
// @ts-ignore - web only minHeight string
|
||||
util_screen_outer: [
|
||||
web({
|
||||
minHeight: '100vh',
|
||||
@@ -76,7 +81,7 @@ export const atoms = {
|
||||
native({
|
||||
height: '100%',
|
||||
}),
|
||||
] as ViewStyle,
|
||||
] as StyleProp<ViewStyle>,
|
||||
|
||||
/*
|
||||
* Theme-independent bg colors
|
||||
@@ -965,6 +970,34 @@ export const atoms = {
|
||||
transitionDelay: '50ms',
|
||||
}),
|
||||
|
||||
/*
|
||||
* Animaations
|
||||
*/
|
||||
fade_in: web({
|
||||
animation: 'fadeIn ease-out 0.15s',
|
||||
}),
|
||||
fade_out: web({
|
||||
animation: 'fadeOut ease-out 0.15s',
|
||||
}),
|
||||
zoom_in: web({
|
||||
animation: 'zoomIn ease-out 0.1s',
|
||||
}),
|
||||
zoom_out: web({
|
||||
animation: 'zoomOut ease-out 0.1s',
|
||||
}),
|
||||
slide_in_left: web({
|
||||
// exponential easing function
|
||||
animation: 'slideInLeft cubic-bezier(0.16, 1, 0.3, 1) 0.5s',
|
||||
}),
|
||||
slide_out_left: web({
|
||||
animation: 'slideOutLeft ease-in 0.15s',
|
||||
animationFillMode: 'forwards',
|
||||
}),
|
||||
// special composite animation for dialogs
|
||||
zoom_fade_in: web({
|
||||
animation: 'zoomIn ease-out 0.1s, fadeIn ease-out 0.1s',
|
||||
}),
|
||||
|
||||
/**
|
||||
* {@link Layout.SCROLLBAR_OFFSET}
|
||||
*/
|
||||
|
||||
@@ -15,6 +15,7 @@ import {FocusScope} from '@radix-ui/react-focus-scope'
|
||||
import {RemoveScrollBar} from 'react-remove-scroll-bar'
|
||||
|
||||
import {logger} from '#/logger'
|
||||
import {useA11y} from '#/state/a11y'
|
||||
import {useDialogStateControlContext} from '#/state/dialogs'
|
||||
import {atoms as a, flatten, useBreakpoints, useTheme, web} from '#/alf'
|
||||
import {Button, ButtonIcon} from '#/components/Button'
|
||||
@@ -152,6 +153,7 @@ export function Inner({
|
||||
const t = useTheme()
|
||||
const {close} = React.useContext(Context)
|
||||
const {gtMobile} = useBreakpoints()
|
||||
const {reduceMotionEnabled} = useA11y()
|
||||
useFocusGuards()
|
||||
return (
|
||||
<FocusScope loop asChild trapped>
|
||||
@@ -161,7 +163,7 @@ export function Inner({
|
||||
aria-label={label}
|
||||
aria-labelledby={accessibilityLabelledBy}
|
||||
aria-describedby={accessibilityDescribedBy}
|
||||
// @ts-ignore web only -prf
|
||||
// @ts-expect-error web only -prf
|
||||
onClick={stopPropagation}
|
||||
onStartShouldSetResponder={_ => true}
|
||||
onTouchEnd={stopPropagation}
|
||||
@@ -177,10 +179,9 @@ export function Inner({
|
||||
shadowColor: t.palette.black,
|
||||
shadowOpacity: t.name === 'light' ? 0.1 : 0.4,
|
||||
shadowRadius: 30,
|
||||
// @ts-ignore web only
|
||||
animation: 'fadeIn ease-out 0.1s',
|
||||
},
|
||||
flatten(style),
|
||||
!reduceMotionEnabled && a.zoom_fade_in,
|
||||
style,
|
||||
])}>
|
||||
<DismissableLayer
|
||||
onInteractOutside={preventDefault}
|
||||
@@ -216,7 +217,7 @@ export const InnerFlatList = React.forwardRef<
|
||||
style={[
|
||||
a.overflow_hidden,
|
||||
a.px_0,
|
||||
// @ts-ignore web only -sfn
|
||||
// @ts-expect-error web only -sfn
|
||||
{maxHeight: 'calc(-36px + 100vh)'},
|
||||
webInnerStyle,
|
||||
]}
|
||||
@@ -262,20 +263,15 @@ export function Handle() {
|
||||
|
||||
function Backdrop() {
|
||||
const t = useTheme()
|
||||
const {reduceMotionEnabled} = useA11y()
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
opacity: 0.8,
|
||||
}}>
|
||||
<View style={{opacity: 0.8}}>
|
||||
<View
|
||||
style={[
|
||||
a.fixed,
|
||||
a.inset_0,
|
||||
{
|
||||
backgroundColor: t.palette.black,
|
||||
// @ts-ignore web only
|
||||
animation: 'fadeIn ease-out 0.15s',
|
||||
},
|
||||
{backgroundColor: t.palette.black},
|
||||
!reduceMotionEnabled && a.fade_in,
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {createContext, useCallback, useContext} from 'react'
|
||||
import {GestureResponderEvent, View} from 'react-native'
|
||||
import {GestureResponderEvent, Keyboard, View} from 'react-native'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
@@ -140,6 +140,7 @@ export function MenuButton() {
|
||||
const {gtMobile} = useBreakpoints()
|
||||
|
||||
const onPress = useCallback(() => {
|
||||
Keyboard.dismiss()
|
||||
setDrawerOpen(true)
|
||||
}, [setDrawerOpen])
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
|
||||
|
||||
import {useA11y} from '#/state/a11y'
|
||||
import {atoms as a, flatten, useTheme, web} from '#/alf'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
||||
@@ -177,10 +178,16 @@ export function Outer({
|
||||
style?: StyleProp<ViewStyle>
|
||||
}>) {
|
||||
const t = useTheme()
|
||||
const {reduceMotionEnabled} = useA11y()
|
||||
|
||||
return (
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content sideOffset={5} loop aria-label="Test">
|
||||
<DropdownMenu.Content
|
||||
sideOffset={5}
|
||||
collisionPadding={{left: 5, right: 5, bottom: 5}}
|
||||
loop
|
||||
aria-label="Test"
|
||||
className="dropdown-menu-transform-origin">
|
||||
<View
|
||||
style={[
|
||||
a.rounded_sm,
|
||||
@@ -189,6 +196,8 @@ export function Outer({
|
||||
t.name === 'light' ? t.atoms.bg : t.atoms.bg_contrast_25,
|
||||
t.atoms.shadow_md,
|
||||
t.atoms.border_contrast_low,
|
||||
a.overflow_auto,
|
||||
!reduceMotionEnabled && a.zoom_fade_in,
|
||||
style,
|
||||
]}>
|
||||
{children}
|
||||
@@ -373,9 +382,8 @@ export function Divider() {
|
||||
style={flatten([
|
||||
a.my_xs,
|
||||
t.atoms.bg_contrast_100,
|
||||
{
|
||||
height: 1,
|
||||
},
|
||||
a.flex_shrink_0,
|
||||
{height: 1},
|
||||
])}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -762,7 +762,7 @@ function SearchInput({
|
||||
autoComplete="off"
|
||||
autoCapitalize="none"
|
||||
accessibilityLabel={_(msg`Search profiles`)}
|
||||
accessibilityHint={_(msg`Search profiles`)}
|
||||
accessibilityHint={_(msg`Searches for profiles`)}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
|
||||
@@ -12,7 +12,7 @@ import {precacheResolvedUri} from '#/state/queries/resolve-uri'
|
||||
import {precacheStarterPack} from '#/state/queries/starter-packs'
|
||||
import {useSession} from '#/state/session'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {StarterPack} from '#/components/icons/StarterPack'
|
||||
import {StarterPack as StarterPackIcon} from '#/components/icons/StarterPack'
|
||||
import {Link as BaseLink, LinkProps as BaseLinkProps} from '#/components/Link'
|
||||
import {Text} from '#/components/Typography'
|
||||
import * as bsky from '#/types/bsky'
|
||||
@@ -70,7 +70,7 @@ export function Card({
|
||||
return (
|
||||
<View style={[a.w_full, a.gap_md]}>
|
||||
<View style={[a.flex_row, a.gap_sm, a.w_full]}>
|
||||
{!noIcon ? <StarterPack width={40} gradient="sky" /> : null}
|
||||
{!noIcon ? <StarterPackIcon width={40} gradient="sky" /> : null}
|
||||
<View style={[a.flex_1]}>
|
||||
<Text
|
||||
emoji
|
||||
|
||||
@@ -12,6 +12,7 @@ import {PressableScale} from '#/lib/custom-animations/PressableScale'
|
||||
// import {UserAvatar} from '#/view/com/util/UserAvatar'
|
||||
import type {TrendingTopic} from '#/state/queries/trending/useTrendingTopics'
|
||||
import {atoms as a, native, useTheme, ViewStyleProp} from '#/alf'
|
||||
import {StarterPack as StarterPackIcon} from '#/components/icons/StarterPack'
|
||||
import {Link as InternalLink, LinkProps} from '#/components/Link'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
@@ -24,9 +25,8 @@ export function TrendingTopic({
|
||||
const topic = useTopic(raw)
|
||||
|
||||
const isSmall = size === 'small'
|
||||
// const hasAvi = topic.type === 'feed' || topic.type === 'profile'
|
||||
// const aviSize = isSmall ? 16 : 20
|
||||
// const iconSize = isSmall ? 16 : 20
|
||||
const hasIcon = topic.type === 'starter-pack' && !isSmall
|
||||
const iconSize = 20
|
||||
|
||||
return (
|
||||
<View
|
||||
@@ -45,45 +45,47 @@ export function TrendingTopic({
|
||||
},
|
||||
]
|
||||
: [a.py_sm, a.px_md],
|
||||
hasIcon && {gap: 6},
|
||||
style,
|
||||
/*
|
||||
{
|
||||
padding: 6,
|
||||
gap: hasAvi ? 4 : 2,
|
||||
},
|
||||
a.pr_md,
|
||||
*/
|
||||
]}>
|
||||
{hasIcon && topic.type === 'starter-pack' && (
|
||||
<StarterPackIcon
|
||||
gradient="sky"
|
||||
width={iconSize}
|
||||
style={{marginLeft: -3, marginVertical: -1}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/*
|
||||
<View
|
||||
style={[
|
||||
a.align_center,
|
||||
a.justify_center,
|
||||
a.rounded_full,
|
||||
a.overflow_hidden,
|
||||
{
|
||||
width: aviSize,
|
||||
height: aviSize,
|
||||
},
|
||||
]}>
|
||||
{topic.type === 'tag' ? (
|
||||
<Hashtag width={iconSize} />
|
||||
) : topic.type === 'topic' ? (
|
||||
<Quote width={iconSize - 2} />
|
||||
) : topic.type === 'feed' ? (
|
||||
<UserAvatar
|
||||
type="user"
|
||||
size={aviSize}
|
||||
avatar=""
|
||||
/>
|
||||
) : (
|
||||
<UserAvatar
|
||||
type="user"
|
||||
size={aviSize}
|
||||
avatar=""
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
a.align_center,
|
||||
a.justify_center,
|
||||
a.rounded_full,
|
||||
a.overflow_hidden,
|
||||
{
|
||||
width: iconSize,
|
||||
height: iconSize,
|
||||
},
|
||||
]}>
|
||||
{topic.type === 'tag' ? (
|
||||
<Hashtag width={iconSize} />
|
||||
) : topic.type === 'topic' ? (
|
||||
<Quote width={iconSize - 2} />
|
||||
) : topic.type === 'feed' ? (
|
||||
<UserAvatar
|
||||
type="user"
|
||||
size={aviSize}
|
||||
avatar=""
|
||||
/>
|
||||
) : (
|
||||
<UserAvatar
|
||||
type="user"
|
||||
size={aviSize}
|
||||
avatar=""
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
*/}
|
||||
|
||||
<Text
|
||||
@@ -151,7 +153,7 @@ export function TrendingTopicLink({
|
||||
|
||||
type ParsedTrendingTopic =
|
||||
| {
|
||||
type: 'topic' | 'tag' | 'unknown'
|
||||
type: 'topic' | 'tag' | 'starter-pack' | 'unknown'
|
||||
label: string
|
||||
displayName: string
|
||||
url: string
|
||||
@@ -187,6 +189,14 @@ export function useTopic(raw: TrendingTopic): ParsedTrendingTopic {
|
||||
uri: undefined,
|
||||
url: link,
|
||||
}
|
||||
} else if (link.startsWith('/starter-pack')) {
|
||||
return {
|
||||
type: 'starter-pack',
|
||||
label: _(msg`Browse starter pack ${displayName}`),
|
||||
displayName,
|
||||
uri: undefined,
|
||||
url: link,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -118,7 +118,7 @@ export function VideoPostCard({
|
||||
|
||||
return (
|
||||
<Link
|
||||
accessibilityHint={_(msg`Tap to view video in immersive mode.`)}
|
||||
accessibilityHint={_(msg`Views video in immersive mode`)}
|
||||
label={_(msg`Video from ${author.handle}: ${text}`)}
|
||||
to={{
|
||||
screen: 'VideoFeed',
|
||||
|
||||
@@ -213,7 +213,12 @@ function Rules({
|
||||
a.flex_wrap,
|
||||
t.atoms.text_contrast_medium,
|
||||
]}>
|
||||
{settings[0].type === 'everybody' ? (
|
||||
{settings.length === 0 ? (
|
||||
<Trans>
|
||||
This post has an unknown type of threadgate on it. Your app may be
|
||||
out of date.
|
||||
</Trans>
|
||||
) : settings[0].type === 'everybody' ? (
|
||||
<Trans>Everybody can reply to this post.</Trans>
|
||||
) : settings[0].type === 'nobody' ? (
|
||||
<Trans>Replies to this post are disabled.</Trans>
|
||||
@@ -257,6 +262,19 @@ function Rule({
|
||||
if (rule.type === 'mention') {
|
||||
return <Trans>mentioned users</Trans>
|
||||
}
|
||||
if (rule.type === 'followers') {
|
||||
return (
|
||||
<Trans>
|
||||
users following{' '}
|
||||
<InlineLinkText
|
||||
label={`@${post.author.handle}`}
|
||||
to={makeProfileLink(post.author)}
|
||||
style={[a.text_sm, a.leading_snug]}>
|
||||
@{post.author.handle}
|
||||
</InlineLinkText>
|
||||
</Trans>
|
||||
)
|
||||
}
|
||||
if (rule.type === 'following') {
|
||||
return (
|
||||
<Trans>
|
||||
|
||||
@@ -40,6 +40,7 @@ import {Loader} from '#/components/Loader'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
export type PostInteractionSettingsFormProps = {
|
||||
canSave?: boolean
|
||||
onSave: () => void
|
||||
isSaving?: boolean
|
||||
|
||||
@@ -58,20 +59,53 @@ export function PostInteractionSettingsControlledDialog({
|
||||
}: PostInteractionSettingsFormProps & {
|
||||
control: Dialog.DialogControlProps
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
|
||||
return (
|
||||
<Dialog.Outer control={control}>
|
||||
<Dialog.Handle />
|
||||
<Dialog.ScrollableInner
|
||||
label={_(msg`Edit post interaction settings`)}
|
||||
style={[{maxWidth: 500}, a.w_full]}>
|
||||
<PostInteractionSettingsForm {...rest} />
|
||||
<View style={[a.gap_md]}>
|
||||
<Header />
|
||||
<PostInteractionSettingsForm {...rest} />
|
||||
<Text
|
||||
style={[
|
||||
a.pt_sm,
|
||||
a.text_sm,
|
||||
a.leading_snug,
|
||||
t.atoms.text_contrast_medium,
|
||||
]}>
|
||||
<Trans>
|
||||
You can set default interaction settings in{' '}
|
||||
<Text style={[a.font_bold, t.atoms.text_contrast_medium]}>
|
||||
Settings → Moderation → Interaction settings.
|
||||
</Text>
|
||||
</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
<Dialog.Close />
|
||||
</Dialog.ScrollableInner>
|
||||
</Dialog.Outer>
|
||||
)
|
||||
}
|
||||
|
||||
export function Header() {
|
||||
return (
|
||||
<View style={[a.gap_md, a.pb_sm]}>
|
||||
<Text style={[a.text_2xl, a.font_bold]}>
|
||||
<Trans>Post interaction settings</Trans>
|
||||
</Text>
|
||||
<Text style={[a.text_md, a.pb_xs]}>
|
||||
<Trans>Customize who can interact with this post.</Trans>
|
||||
</Text>
|
||||
<Divider />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export type PostInteractionSettingsDialogProps = {
|
||||
control: Dialog.DialogControlProps
|
||||
/**
|
||||
@@ -203,26 +237,31 @@ export function PostInteractionSettingsDialogControlledInner(
|
||||
<Dialog.ScrollableInner
|
||||
label={_(msg`Edit post interaction settings`)}
|
||||
style={[{maxWidth: 500}, a.w_full]}>
|
||||
{isLoading ? (
|
||||
<View style={[a.flex_1, a.py_4xl, a.align_center, a.justify_center]}>
|
||||
<Loader size="xl" />
|
||||
</View>
|
||||
) : (
|
||||
<PostInteractionSettingsForm
|
||||
replySettingsDisabled={!isThreadgateOwnedByViewer}
|
||||
isSaving={isSaving}
|
||||
onSave={onSave}
|
||||
postgate={postgateValue}
|
||||
onChangePostgate={setEditedPostgate}
|
||||
threadgateAllowUISettings={allowUIValue}
|
||||
onChangeThreadgateAllowUISettings={setEditedAllowUISettings}
|
||||
/>
|
||||
)}
|
||||
<View style={[a.gap_md]}>
|
||||
<Header />
|
||||
|
||||
{isLoading ? (
|
||||
<View style={[a.flex_1, a.py_4xl, a.align_center, a.justify_center]}>
|
||||
<Loader size="xl" />
|
||||
</View>
|
||||
) : (
|
||||
<PostInteractionSettingsForm
|
||||
replySettingsDisabled={!isThreadgateOwnedByViewer}
|
||||
isSaving={isSaving}
|
||||
onSave={onSave}
|
||||
postgate={postgateValue}
|
||||
onChangePostgate={setEditedPostgate}
|
||||
threadgateAllowUISettings={allowUIValue}
|
||||
onChangeThreadgateAllowUISettings={setEditedAllowUISettings}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</Dialog.ScrollableInner>
|
||||
)
|
||||
}
|
||||
|
||||
export function PostInteractionSettingsForm({
|
||||
canSave = true,
|
||||
onSave,
|
||||
isSaving,
|
||||
postgate,
|
||||
@@ -283,17 +322,7 @@ export function PostInteractionSettingsForm({
|
||||
return (
|
||||
<View>
|
||||
<View style={[a.flex_1, a.gap_md]}>
|
||||
<Text style={[a.text_2xl, a.font_bold]}>
|
||||
<Trans>Post interaction settings</Trans>
|
||||
</Text>
|
||||
|
||||
<View style={[a.gap_lg]}>
|
||||
<Text style={[a.text_md]}>
|
||||
<Trans>Customize who can interact with this post.</Trans>
|
||||
</Text>
|
||||
|
||||
<Divider />
|
||||
|
||||
<View style={[a.gap_sm]}>
|
||||
<Text style={[a.font_bold, a.text_lg]}>
|
||||
<Trans>Quote settings</Trans>
|
||||
@@ -400,7 +429,7 @@ export function PostInteractionSettingsForm({
|
||||
disabled={replySettingsDisabled}
|
||||
/>
|
||||
<Selectable
|
||||
label={_(msg`Followed users`)}
|
||||
label={_(msg`Users you follow`)}
|
||||
isSelected={
|
||||
!!threadgateAllowUISettings.find(
|
||||
v => v.type === 'following',
|
||||
@@ -409,6 +438,16 @@ export function PostInteractionSettingsForm({
|
||||
onPress={() => onPressAudience({type: 'following'})}
|
||||
disabled={replySettingsDisabled}
|
||||
/>
|
||||
<Selectable
|
||||
label={_(msg`Your followers`)}
|
||||
isSelected={
|
||||
!!threadgateAllowUISettings.find(
|
||||
v => v.type === 'followers',
|
||||
)
|
||||
}
|
||||
onPress={() => onPressAudience({type: 'followers'})}
|
||||
disabled={replySettingsDisabled}
|
||||
/>
|
||||
{lists && lists.length > 0
|
||||
? lists.map(list => (
|
||||
<Selectable
|
||||
@@ -435,6 +474,7 @@ export function PostInteractionSettingsForm({
|
||||
</View>
|
||||
|
||||
<Button
|
||||
disabled={!canSave || isSaving}
|
||||
label={_(msg`Save`)}
|
||||
onPress={onSave}
|
||||
color="primary"
|
||||
|
||||
@@ -506,7 +506,7 @@ function SearchInput({
|
||||
autoCapitalize="none"
|
||||
autoFocus
|
||||
accessibilityLabel={_(msg`Search profiles`)}
|
||||
accessibilityHint={_(msg`Search profiles`)}
|
||||
accessibilityHint={_(msg`Searches for profiles`)}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
|
||||
@@ -47,7 +47,7 @@ export function HostingProvider({
|
||||
</Text>
|
||||
<Button
|
||||
label={toNiceDomain(serviceUrl)}
|
||||
accessibilityHint={_(msg`Press to change hosting provider`)}
|
||||
accessibilityHint={_(msg`Changes hosting provider`)}
|
||||
onPress={onPressSelectService}
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
@@ -63,7 +63,7 @@ export function HostingProvider({
|
||||
<Button
|
||||
testID="selectServiceButton"
|
||||
label={toNiceDomain(serviceUrl)}
|
||||
accessibilityHint={_(msg`Press to change hosting provider`)}
|
||||
accessibilityHint={_(msg`Changes hosting provider`)}
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
style={[
|
||||
|
||||
@@ -226,6 +226,7 @@ export function createInput(Component: typeof TextInput) {
|
||||
<>
|
||||
<Component
|
||||
accessibilityHint={undefined}
|
||||
hitSlop={HITSLOP_20}
|
||||
{...rest}
|
||||
accessibilityLabel={label}
|
||||
ref={refs}
|
||||
@@ -242,7 +243,6 @@ export function createInput(Component: typeof TextInput) {
|
||||
placeholder={placeholder || label}
|
||||
placeholderTextColor={t.palette.contrast_500}
|
||||
keyboardAppearance={t.name === 'light' ? 'light' : 'dark'}
|
||||
hitSlop={HITSLOP_20}
|
||||
style={flattened}
|
||||
/>
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ const locales: Record<AppLanguage, Locale | undefined> = {
|
||||
gl,
|
||||
hi,
|
||||
hu,
|
||||
ia: undefined,
|
||||
id,
|
||||
it,
|
||||
ja,
|
||||
|
||||
@@ -15,3 +15,7 @@ export const ChevronTop_Stroke2_Corner0_Rounded = createSinglePathSVG({
|
||||
export const ChevronBottom_Stroke2_Corner0_Rounded = createSinglePathSVG({
|
||||
path: 'M3.293 8.293a1 1 0 0 1 1.414 0L12 15.586l7.293-7.293a1 1 0 1 1 1.414 1.414l-8 8a1 1 0 0 1-1.414 0l-8-8a1 1 0 0 1 0-1.414Z',
|
||||
})
|
||||
|
||||
export const ChevronTopBottom_Stroke2_Corner0_Rounded = createSinglePathSVG({
|
||||
path: 'M11.293 4.293a1 1 0 0 1 1.414 0l4 4a1 1 0 0 1-1.414 1.414L12 6.414 8.707 9.707a1 1 0 0 1-1.414-1.414l4-4Zm-4 10a1 1 0 0 1 1.414 0L12 17.586l3.293-3.293a1 1 0 0 1 1.414 1.414l-4 4a1 1 0 0 1-1.414 0l-4-4a1 1 0 0 1 0-1.414Z',
|
||||
})
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import {createSinglePathSVG} from './TEMPLATE'
|
||||
|
||||
export const Coffee_Stroke2_Corner0_Rounded = createSinglePathSVG({
|
||||
path: 'M7 2a1 1 0 0 1 1 1v2a1 1 0 0 1-2 0V3a1 1 0 0 1 1-1Zm4 0a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V3a1 1 0 0 1 1-1Zm4 0a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V3a1 1 0 0 1 1-1ZM4 9a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2h.5a3.5 3.5 0 1 1 0 7H18v4a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V9Zm12 0H6v11h10V9Zm2 5h.5a1.5 1.5 0 0 0 0-3H18v3Z',
|
||||
})
|
||||
@@ -1,5 +0,0 @@
|
||||
import {createSinglePathSVG} from './TEMPLATE'
|
||||
|
||||
export const PiggyBank_Stroke2_Corner0_Rounded = createSinglePathSVG({
|
||||
path: 'M6.5 4.999Zm0 0a.054.054 0 0 1-.016.024.196.196 0 0 1-.152.043c.057.01.113.02.168.032V7.5a1 1 0 0 1-.33.742c-.543.49-.917 1.176-1.23 2.036a1 1 0 0 1-.94.657H3V14h1a1 1 0 0 1 .866.5c.436.754.879 1.195 1.637 1.636A1 1 0 0 1 7 17v2h2a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2h2v-2.537a1 1 0 0 1 .332-.744A4.978 4.978 0 0 0 19 11.999 5 5 0 0 0 14 7H9.474a1 1 0 0 1-.893-.55v-.001l-.001-.002-.005-.008L9.474 6l-.9.438h.001v.002l.002.001.001.003.002.003v.003a2.62 2.62 0 0 0-.443-.538c-.319-.298-.839-.648-1.637-.814V5Zm2.082 1.452ZM9.5 4.447c.211.196.379.387.508.553H14a7 7 0 0 1 6.72 8.96 1.003 1.003 0 0 0 1.146-1.46 1 1 0 1 1 1.731-1 3 3 0 0 1-3.714 4.286A7.074 7.074 0 0 1 19 16.888V19a2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2h-2a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-1.45A5.783 5.783 0 0 1 3.448 16H3a2 2 0 0 1-2-2v-3.065a2 2 0 0 1 2-2h.324c.286-.649.657-1.291 1.176-1.852V5c0-1.047.89-2.12 2.161-1.907 1.33.223 2.248.805 2.839 1.354ZM8.25 12a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5Z',
|
||||
})
|
||||
@@ -1,5 +0,0 @@
|
||||
import {createSinglePathSVG} from './TEMPLATE'
|
||||
|
||||
export const Poop_Stroke2_Corner0_Rounded = createSinglePathSVG({
|
||||
path: 'M12 4c0-1.012.894-2.187 2.237-1.846a5.002 5.002 0 0 1 3.218 7.119 4.001 4.001 0 0 1 2.345 4.976A3.501 3.501 0 0 1 18.5 21h-13a3.5 3.5 0 0 1-1.3-6.75 4 4 0 0 1 2.052-4.848A4 4 0 0 1 10 4h2.001Zm-4.187 7.009A2 2 0 0 0 6 13c0 .366.098.706.271 1H12a1 1 0 1 1 0 2H5.5a1.5 1.5 0 0 0 0 3h13a1.5 1.5 0 1 0 0-3H17a1 1 0 1 1 0-2h.729c.173-.294.271-.634.271-1a2 2 0 0 0-2-2h-2a1 1 0 1 1 0-2h1.236a3.002 3.002 0 0 0-1.243-4.832A2 2 0 0 1 12 6h-2a2 2 0 0 0-1.728 3.009H9a1 1 0 0 1 0 2H7.813Z',
|
||||
})
|
||||
@@ -146,10 +146,10 @@ function ContentHiderActive({
|
||||
label={desc.name}
|
||||
accessibilityHint={
|
||||
modui.noOverride
|
||||
? _(msg`Learn more about the moderation applied to this content.`)
|
||||
? _(msg`Learn more about the moderation applied to this content`)
|
||||
: override
|
||||
? _(msg`Hide the content`)
|
||||
: _(msg`Show the content`)
|
||||
? _(msg`Hides the content`)
|
||||
: _(msg`Shows the content`)
|
||||
}>
|
||||
{state => (
|
||||
<View
|
||||
|
||||
@@ -76,7 +76,7 @@ export function PostHider({
|
||||
}}
|
||||
accessibilityRole="button"
|
||||
accessibilityHint={
|
||||
override ? _(msg`Hide the content`) : _(msg`Show the content`)
|
||||
override ? _(msg`Hides the content`) : _(msg`Shows the content`)
|
||||
}
|
||||
accessibilityLabel=""
|
||||
style={[
|
||||
|
||||
@@ -12,6 +12,7 @@ export type CommonNavigatorParams = {
|
||||
ModerationModlists: undefined
|
||||
ModerationMutedAccounts: undefined
|
||||
ModerationBlockedAccounts: undefined
|
||||
ModerationInteractionSettings: undefined
|
||||
Settings: undefined
|
||||
Profile: {name: string; hideBackButton?: boolean}
|
||||
ProfileFollowers: {name: string}
|
||||
|
||||
@@ -25,3 +25,19 @@ export async function shareUrl(url: string) {
|
||||
Toast.show(t`Copied to clipboard`, 'clipboard-check')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function shares a text using the native Share API if available, or copies it to the clipboard
|
||||
* and displays a toast message if not (mostly on web)
|
||||
*
|
||||
* @param {string} text - A string representing the text that needs to be shared or copied to the
|
||||
* clipboard.
|
||||
*/
|
||||
export async function shareText(text: string) {
|
||||
if (isAndroid || isIOS) {
|
||||
await Share.share({message: text})
|
||||
} else {
|
||||
await setStringAsync(text)
|
||||
Toast.show(t`Copied to clipboard`, 'clipboard-check')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,8 @@ export type Gate =
|
||||
// Keep this alphabetic please.
|
||||
| 'debug_show_feedcontext'
|
||||
| 'debug_subscriptions'
|
||||
| 'new_postonboarding'
|
||||
| 'old_postonboarding'
|
||||
| 'onboarding_add_video_feed'
|
||||
| 'remove_show_latest_button'
|
||||
| 'test_gate_1'
|
||||
| 'test_gate_2'
|
||||
| 'trending_topics_beta'
|
||||
|
||||
@@ -42,10 +42,9 @@ export interface IsValidHandle {
|
||||
}
|
||||
|
||||
// More checks from https://github.com/bluesky-social/atproto/blob/main/packages/pds/src/handle/index.ts#L72
|
||||
export function validateHandle(
|
||||
export function validateServiceHandle(
|
||||
str: string,
|
||||
userDomain: string,
|
||||
isServiceHandle?: boolean,
|
||||
): IsValidHandle {
|
||||
const fullHandle = createFullHandle(str, userDomain)
|
||||
|
||||
@@ -53,8 +52,8 @@ export function validateHandle(
|
||||
handleChars:
|
||||
!str || (VALIDATE_REGEX.test(fullHandle) && !str.includes('.')),
|
||||
hyphenStartOrEnd: !str.startsWith('-') && !str.endsWith('-'),
|
||||
frontLength: str.length >= 3,
|
||||
totalLength: fullHandle.length <= (isServiceHandle ? 30 : 253),
|
||||
frontLength: str.length >= 3 && str.length <= 18,
|
||||
totalLength: fullHandle.length <= 253,
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -185,6 +185,8 @@ export function sanitizeAppLanguageSetting(appLanguage: string): AppLanguage {
|
||||
return AppLanguage.hi
|
||||
case 'hu':
|
||||
return AppLanguage.hu
|
||||
case 'ia':
|
||||
return AppLanguage.ia
|
||||
case 'id':
|
||||
return AppLanguage.id
|
||||
case 'it':
|
||||
|
||||
@@ -27,6 +27,7 @@ import {messages as messagesGa} from '#/locale/locales/ga/messages'
|
||||
import {messages as messagesGl} from '#/locale/locales/gl/messages'
|
||||
import {messages as messagesHi} from '#/locale/locales/hi/messages'
|
||||
import {messages as messagesHu} from '#/locale/locales/hu/messages'
|
||||
import {messages as messagesIa} from '#/locale/locales/ia/messages'
|
||||
import {messages as messagesId} from '#/locale/locales/id/messages'
|
||||
import {messages as messagesIt} from '#/locale/locales/it/messages'
|
||||
import {messages as messagesJa} from '#/locale/locales/ja/messages'
|
||||
@@ -173,6 +174,14 @@ export async function dynamicActivate(locale: AppLanguage) {
|
||||
])
|
||||
break
|
||||
}
|
||||
case AppLanguage.id: {
|
||||
i18n.loadAndActivate({locale, messages: messagesIa})
|
||||
await Promise.all([
|
||||
import('@formatjs/intl-pluralrules/locale-data/ia'),
|
||||
import('@formatjs/intl-numberformat/locale-data/ia'),
|
||||
])
|
||||
break
|
||||
}
|
||||
case AppLanguage.id: {
|
||||
i18n.loadAndActivate({locale, messages: messagesId})
|
||||
await Promise.all([
|
||||
|
||||
@@ -72,6 +72,10 @@ export async function dynamicActivate(locale: AppLanguage) {
|
||||
mod = await import(`./locales/hu/messages`)
|
||||
break
|
||||
}
|
||||
case AppLanguage.ia: {
|
||||
mod = await import(`./locales/ia/messages`)
|
||||
break
|
||||
}
|
||||
case AppLanguage.id: {
|
||||
mod = await import(`./locales/id/messages`)
|
||||
break
|
||||
|
||||
+13
-19
@@ -21,6 +21,7 @@ export enum AppLanguage {
|
||||
gl = 'gl',
|
||||
hi = 'hi',
|
||||
hu = 'hu',
|
||||
ia = 'ia',
|
||||
id = 'id',
|
||||
it = 'it',
|
||||
ja = 'ja',
|
||||
@@ -64,6 +65,7 @@ export const APP_LANGUAGES: AppLanguageConfig[] = [
|
||||
{code2: AppLanguage.gl, name: 'Galego – Galician'},
|
||||
{code2: AppLanguage.hi, name: 'हिंदी – Hindi'},
|
||||
{code2: AppLanguage.hu, name: 'magyar – Hungarian'},
|
||||
{code2: AppLanguage.ia, name: 'Interlingua'},
|
||||
{code2: AppLanguage.id, name: 'Bahasa Indonesia – Indonesian'},
|
||||
{code2: AppLanguage.it, name: 'Italiano – Italian'},
|
||||
{code2: AppLanguage.ja, name: '日本語 – Japanese'},
|
||||
@@ -177,11 +179,7 @@ export const LANGUAGES: Language[] = [
|
||||
{code3: 'cho', code2: '', name: 'Choctaw'},
|
||||
{code3: 'chp', code2: '', name: 'Chipewyan; Dene Suline'},
|
||||
{code3: 'chr', code2: '', name: 'Cherokee'},
|
||||
{
|
||||
code3: 'chu',
|
||||
code2: 'cu',
|
||||
name: 'Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic',
|
||||
},
|
||||
{code3: 'chu', code2: 'cu', name: 'Church Slavic'},
|
||||
{code3: 'chv', code2: 'cv', name: 'Chuvash'},
|
||||
{code3: 'chy', code2: '', name: 'Cheyenne'},
|
||||
{code3: 'cmc', code2: '', name: 'Chamic languages'},
|
||||
@@ -299,13 +297,9 @@ export const LANGUAGES: Language[] = [
|
||||
{code3: 'iii', code2: 'ii', name: 'Sichuan Yi; Nuosu'},
|
||||
{code3: 'ijo', code2: '', name: 'Ijo languages'},
|
||||
{code3: 'iku', code2: 'iu', name: 'Inuktitut'},
|
||||
{code3: 'ile', code2: 'ie', name: 'Interlingue; Occidental'},
|
||||
{code3: 'ile', code2: 'ie', name: 'Interlingue'},
|
||||
{code3: 'ilo', code2: '', name: 'Iloko'},
|
||||
{
|
||||
code3: 'ina',
|
||||
code2: 'ia',
|
||||
name: 'Interlingua (International Auxiliary Language Association)',
|
||||
},
|
||||
{code3: 'ina', code2: 'ia', name: 'Interlingua'},
|
||||
{code3: 'inc', code2: '', name: 'Indic languages'},
|
||||
{code3: 'ind', code2: 'id', name: 'Indonesian'},
|
||||
{code3: 'ine', code2: '', name: 'Indo-European languages'},
|
||||
@@ -323,7 +317,7 @@ export const LANGUAGES: Language[] = [
|
||||
{code3: 'kaa', code2: '', name: 'Kara-Kalpak'},
|
||||
{code3: 'kab', code2: '', name: 'Kabyle'},
|
||||
{code3: 'kac', code2: '', name: 'Kachin; Jingpho'},
|
||||
{code3: 'kal', code2: 'kl', name: 'Kalaallisut; Greenlandic'},
|
||||
{code3: 'kal', code2: 'kl', name: 'Kalaallisut'},
|
||||
{code3: 'kam', code2: '', name: 'Kamba'},
|
||||
{code3: 'kan', code2: 'kn', name: 'Kannada'},
|
||||
{code3: 'kar', code2: '', name: 'Karen languages'},
|
||||
@@ -362,7 +356,7 @@ export const LANGUAGES: Language[] = [
|
||||
{code3: 'lat', code2: 'la', name: 'Latin'},
|
||||
{code3: 'lav', code2: 'lv', name: 'Latvian'},
|
||||
{code3: 'lez', code2: '', name: 'Lezghian'},
|
||||
{code3: 'lim', code2: 'li', name: 'Limburgan; Limburger; Limburgish'},
|
||||
{code3: 'lim', code2: 'li', name: 'Limburgish'},
|
||||
{code3: 'lin', code2: 'ln', name: 'Lingala'},
|
||||
{code3: 'lit', code2: 'lt', name: 'Lithuanian'},
|
||||
{code3: 'lol', code2: '', name: 'Mongo'},
|
||||
@@ -423,9 +417,9 @@ export const LANGUAGES: Language[] = [
|
||||
{code3: 'nai', code2: '', name: 'North American Indian languages'},
|
||||
{code3: 'nap', code2: '', name: 'Neapolitan'},
|
||||
{code3: 'nau', code2: 'na', name: 'Nauru'},
|
||||
{code3: 'nav', code2: 'nv', name: 'Navajo; Navaho'},
|
||||
{code3: 'nbl', code2: 'nr', name: 'Ndebele, South; South Ndebele'},
|
||||
{code3: 'nde', code2: 'nd', name: 'Ndebele, North; North Ndebele'},
|
||||
{code3: 'nav', code2: 'nv', name: 'Navajo'},
|
||||
{code3: 'nbl', code2: 'nr', name: 'South Ndebele'},
|
||||
{code3: 'nde', code2: 'nd', name: 'North Ndebele'},
|
||||
{code3: 'ndo', code2: 'ng', name: 'Ndonga'},
|
||||
{
|
||||
code3: 'nds',
|
||||
@@ -438,8 +432,8 @@ export const LANGUAGES: Language[] = [
|
||||
{code3: 'nic', code2: '', name: 'Niger-Kordofanian languages'},
|
||||
{code3: 'niu', code2: '', name: 'Niuean'},
|
||||
{code3: 'nld', code2: 'nl', name: 'Dutch; Flemish'},
|
||||
{code3: 'nno', code2: 'nn', name: 'Norwegian Nynorsk; Nynorsk, Norwegian'},
|
||||
{code3: 'nob', code2: 'nb', name: 'Bokmål, Norwegian; Norwegian Bokmål'},
|
||||
{code3: 'nno', code2: 'nn', name: 'Norwegian Nynorsk'},
|
||||
{code3: 'nob', code2: 'nb', name: 'Norwegian Bokmål'},
|
||||
{code3: 'nog', code2: '', name: 'Nogai'},
|
||||
{code3: 'non', code2: '', name: 'Norse, Old'},
|
||||
{code3: 'nor', code2: 'no', name: 'Norwegian'},
|
||||
@@ -461,7 +455,7 @@ export const LANGUAGES: Language[] = [
|
||||
{code3: 'ori', code2: 'or', name: 'Oriya'},
|
||||
{code3: 'orm', code2: 'om', name: 'Oromo'},
|
||||
{code3: 'osa', code2: '', name: 'Osage'},
|
||||
{code3: 'oss', code2: 'os', name: 'Ossetian; Ossetic'},
|
||||
{code3: 'oss', code2: 'os', name: 'Ossetic'},
|
||||
{code3: 'ota', code2: '', name: 'Turkish, Ottoman (1500-1928)'},
|
||||
{code3: 'oto', code2: '', name: 'Otomian languages'},
|
||||
{code3: 'paa', code2: '', name: 'Papuan languages'},
|
||||
|
||||
+485
-289
File diff suppressed because it is too large
Load Diff
+1274
-1664
File diff suppressed because it is too large
Load Diff
+1288
-965
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@ msgstr ""
|
||||
|
||||
#: src/components/ProgressGuide/FollowDialog.tsx:567
|
||||
msgid "(active)"
|
||||
msgstr ""
|
||||
msgstr "(सक्रिय)"
|
||||
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:130
|
||||
msgid "(contains embedded content)"
|
||||
@@ -37,11 +37,11 @@ msgstr "{0, plural, other {# दिन}}"
|
||||
|
||||
#: src/screens/Profile/ProfileFollowers.tsx:40
|
||||
msgid "{0, plural, one {# follower} other {# followers}}"
|
||||
msgstr ""
|
||||
msgstr "{0, plural, one {# फ़ॉलोअर} other {# फ़ॉलोअर}}"
|
||||
|
||||
#: src/screens/Profile/ProfileFollows.tsx:40
|
||||
msgid "{0, plural, one {# following} other {# following}}"
|
||||
msgstr ""
|
||||
msgstr "{0, plural, one {# फ़ॉलोइंग} other {# फ़ॉलोइंग}}"
|
||||
|
||||
#: src/lib/hooks/useTimeAgo.ts:146
|
||||
msgid "{0, plural, one {# hour} other {# hours}}"
|
||||
@@ -65,15 +65,15 @@ msgstr "{0, plural, one {# घंटा} other {# घंटे}}"
|
||||
|
||||
#: src/components/moderation/LabelsOnMe.tsx:53
|
||||
msgid "{0, plural, one {# label has} other {# labels have}} been placed on this account"
|
||||
msgstr ""
|
||||
msgstr "इस खाते पर {0, plural, one {# लेबल लगाया गया है।} other {# लेबल लगाए गए हैं।}}"
|
||||
|
||||
#: src/components/moderation/LabelsOnMe.tsx:62
|
||||
msgid "{0, plural, one {# label has} other {# labels have}} been placed on this content"
|
||||
msgstr ""
|
||||
msgstr "इस सामाग्री पर {0, plural, one {# लेबल लगाया गया है।} other {# लेबल लगाए गए हैं।}}"
|
||||
|
||||
#: src/screens/Post/PostLikedBy.tsx:41
|
||||
msgid "{0, plural, one {# like} other {# likes}}"
|
||||
msgstr ""
|
||||
msgstr "{0, plural, one {# पसंद} other {# पसंद}}"
|
||||
|
||||
#: src/lib/hooks/useTimeAgo.ts:136
|
||||
msgid "{0, plural, one {# minute} other {# minutes}}"
|
||||
@@ -85,7 +85,7 @@ msgstr "{0, plural, one {# महीना} other {# महीने}}"
|
||||
|
||||
#: src/screens/Post/PostQuotes.tsx:41
|
||||
msgid "{0, plural, one {# quote} other {# quotes}}"
|
||||
msgstr ""
|
||||
msgstr "{0, plural, one {# क्वोट} other {# क्वोट}}"
|
||||
|
||||
#: src/screens/Post/PostRepostedBy.tsx:41
|
||||
msgid "{0, plural, one {# repost} other {# reposts}}"
|
||||
@@ -335,7 +335,7 @@ msgstr "{handle} को संदेश भेजा नहीं जा सक
|
||||
|
||||
#: src/view/shell/bottom-bar/BottomBarWeb.tsx:260
|
||||
msgid "{notificationCount} unread items"
|
||||
msgstr ""
|
||||
msgstr "{notificationCount} अपठित चीज़ें"
|
||||
|
||||
#: src/view/shell/Drawer.tsx:448
|
||||
msgid "{numUnreadNotifications} unread"
|
||||
@@ -621,7 +621,7 @@ msgstr "नया पोस्ट जोड़ें"
|
||||
#: src/view/screens/ProfileList.tsx:903
|
||||
#: src/view/screens/ProfileList.tsx:921
|
||||
msgid "Add people"
|
||||
msgstr ""
|
||||
msgstr "लोगों को जोड़ें"
|
||||
|
||||
#: src/screens/Home/NoFeedsPinned.tsx:99
|
||||
msgid "Add recommended feeds"
|
||||
@@ -703,7 +703,7 @@ msgstr "विकसित"
|
||||
|
||||
#: src/view/screens/Notifications.tsx:86
|
||||
msgid "All"
|
||||
msgstr ""
|
||||
msgstr "सभी"
|
||||
|
||||
#: src/screens/StarterPack/StarterPackScreen.tsx:381
|
||||
msgid "All accounts have been followed!"
|
||||
@@ -725,7 +725,7 @@ msgstr "इनसे नए संदेश आने की अनुमति
|
||||
|
||||
#: src/components/dialogs/PostInteractionSettingsDialog.tsx:314
|
||||
msgid "Allow quote posts"
|
||||
msgstr ""
|
||||
msgstr "क्वोट पोस्ट की अनुमति दें"
|
||||
|
||||
#: src/components/dialogs/PostInteractionSettingsDialog.tsx:359
|
||||
msgid "Allow replies from:"
|
||||
@@ -888,7 +888,7 @@ msgstr "कोई भी संपर्क कर सकता है"
|
||||
#: src/screens/Settings/AppIconSettings/SettingsListItem.tsx:18
|
||||
#: src/screens/Settings/AppIconSettings/SettingsListItem.tsx:23
|
||||
msgid "App Icon"
|
||||
msgstr ""
|
||||
msgstr "ऐप आइकॉन"
|
||||
|
||||
#: src/screens/Settings/LanguageSettings.tsx:80
|
||||
msgid "App Language"
|
||||
@@ -1113,7 +1113,7 @@ msgstr "अन्य उपयोगकर्ताओं को संदेश
|
||||
|
||||
#: src/screens/Search/components/ExploreTrendingTopics.tsx:74
|
||||
msgid "BETA"
|
||||
msgstr ""
|
||||
msgstr "बीटा"
|
||||
|
||||
#: src/components/dialogs/BirthDateSettings.tsx:106
|
||||
#: src/screens/Settings/AccountSettings.tsx:109
|
||||
@@ -1252,11 +1252,11 @@ msgstr "Bluesky लॉग आउट उपयोगकर्ताओं को
|
||||
|
||||
#: src/screens/Settings/AppIconSettings/index.tsx:99
|
||||
msgid "Bluesky+"
|
||||
msgstr ""
|
||||
msgstr "Bluesky+"
|
||||
|
||||
#: src/screens/Settings/AppIconSettings/index.tsx:102
|
||||
msgid "Bluesky+ icons"
|
||||
msgstr ""
|
||||
msgstr "Bluesky+ आइकॉन"
|
||||
|
||||
#: src/lib/moderation/useLabelBehaviorDescription.ts:53
|
||||
msgid "Blur images"
|
||||
@@ -1298,15 +1298,15 @@ msgstr "अन्य फ़ीड देखें"
|
||||
|
||||
#: src/components/TrendingTopics.tsx:176
|
||||
msgid "Browse posts about {displayName}"
|
||||
msgstr ""
|
||||
msgstr "{displayName} के बारे में पोस्ट देखें"
|
||||
|
||||
#: src/components/TrendingTopics.tsx:184
|
||||
msgid "Browse posts tagged with {displayName}"
|
||||
msgstr ""
|
||||
msgstr "{displayName} से टैग किए गए पोस्ट देखें"
|
||||
|
||||
#: src/components/TrendingTopics.tsx:222
|
||||
msgid "Browse topic {displayName}"
|
||||
msgstr ""
|
||||
msgstr "{displayName} विषय देखें"
|
||||
|
||||
#: src/view/screens/Settings/index.tsx:893
|
||||
#~ msgid "Build version {0} {1}"
|
||||
@@ -1330,7 +1330,7 @@ msgstr "{0} के द्वारा"
|
||||
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:447
|
||||
msgid "By <0>{0}</0>"
|
||||
msgstr ""
|
||||
msgstr "<0>{0}</0> के द्वार"
|
||||
|
||||
#: src/screens/Signup/StepInfo/Policies.tsx:80
|
||||
#~ msgid "By creating an account you agree to the {els}."
|
||||
@@ -1463,12 +1463,12 @@ msgstr "बदलें"
|
||||
|
||||
#: src/screens/Settings/AppIconSettings/index.tsx:39
|
||||
msgid "Change app icon"
|
||||
msgstr ""
|
||||
msgstr "ऐप आइकॉन बदलें"
|
||||
|
||||
#: src/screens/Settings/AppIconSettings/index.tsx:38
|
||||
#: src/screens/Settings/AppIconSettings/index.tsx:221
|
||||
msgid "Change app icon to \"{0}\""
|
||||
msgstr ""
|
||||
msgstr "ऐप आइकॉन को \"{0}\" में बदलें"
|
||||
|
||||
#: src/screens/Settings/AccountSettings.tsx:97
|
||||
#: src/screens/Settings/AccountSettings.tsx:101
|
||||
@@ -1600,7 +1600,7 @@ msgstr "इस रंग को अपना अवतार के रूप
|
||||
#: src/view/screens/Feeds.tsx:728
|
||||
#: src/view/screens/Search/Explore.tsx:411
|
||||
msgid "Choose your own timeline! Feeds built by the community help you find content you love."
|
||||
msgstr ""
|
||||
msgstr "आपका अपना टाइमलाइन चुनें! समुदाय द्वारा बनाए गए फ़ीड आपको पसंदीदा सामग्री ढूँढने में सहायता करते हैं।"
|
||||
|
||||
#: src/screens/Signup/StepInfo/index.tsx:201
|
||||
msgid "Choose your password"
|
||||
@@ -1696,7 +1696,7 @@ msgstr "डायलॉग बंद करें"
|
||||
#: src/view/com/composer/text-input/web/EmojiPicker.web.tsx:137
|
||||
#: src/view/com/composer/text-input/web/EmojiPicker.web.tsx:173
|
||||
msgid "Close emoji picker"
|
||||
msgstr ""
|
||||
msgstr "इमोजी चयन बंद करें"
|
||||
|
||||
#: src/components/dialogs/GifSelect.tsx:169
|
||||
msgid "Close GIF dialog"
|
||||
@@ -1859,7 +1859,7 @@ msgstr "सहायता से संपर्क करें"
|
||||
|
||||
#: src/screens/Settings/ContentAndMediaSettings.tsx:49
|
||||
msgid "Content & Media"
|
||||
msgstr ""
|
||||
msgstr "सामाग्री और मीडिया"
|
||||
|
||||
#: src/screens/Settings/AccessibilitySettings.tsx:109
|
||||
#: src/screens/Settings/Settings.tsx:175
|
||||
@@ -2149,7 +2149,7 @@ msgstr "{0} बनाया गया"
|
||||
|
||||
#: src/screens/List/ListHiddenScreen.tsx:127
|
||||
msgid "Creator has been blocked"
|
||||
msgstr ""
|
||||
msgstr "रचयिता को अवरुद्ध किया गया"
|
||||
|
||||
#: src/screens/Onboarding/index.tsx:26
|
||||
#: src/screens/Onboarding/state.ts:99
|
||||
@@ -2227,7 +2227,7 @@ msgstr "डिफ़ॉल्ट"
|
||||
|
||||
#: src/screens/Settings/AppIconSettings/index.tsx:75
|
||||
msgid "Default icons"
|
||||
msgstr ""
|
||||
msgstr "डिफ़ॉल्ट आइकॉन"
|
||||
|
||||
#: src/components/dms/MessageMenu.tsx:151
|
||||
#: src/screens/Settings/AppPasswords.tsx:212
|
||||
@@ -2713,7 +2713,7 @@ msgstr "शिक्षा"
|
||||
|
||||
#: src/screens/List/ListHiddenScreen.tsx:141
|
||||
msgid "Either the creator of this list has blocked you or you have blocked the creator."
|
||||
msgstr ""
|
||||
msgstr "इस सूची के रचयिता ने आपको अवरुद्ध किया है या आपने रचयिता को अवरुद्ध किया है।"
|
||||
|
||||
#: src/screens/Settings/AccountSettings.tsx:60
|
||||
#: src/screens/Signup/StepInfo/index.tsx:170
|
||||
@@ -2774,7 +2774,7 @@ msgstr "इस पोस्ट को अपने वेबसाइट मे
|
||||
|
||||
#: src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerWeb.tsx:57
|
||||
msgid "Embedded video player"
|
||||
msgstr ""
|
||||
msgstr "एम्बेडेड वीडियो प्लेयर"
|
||||
|
||||
#: src/screens/Settings/components/Email2FAToggle.tsx:56
|
||||
#: src/screens/Settings/components/Email2FAToggle.tsx:60
|
||||
@@ -2822,7 +2822,7 @@ msgstr "केवल इस सूत्र को सक्षम करें"
|
||||
#: src/screens/Settings/ContentAndMediaSettings.tsx:122
|
||||
#: src/screens/Settings/ContentAndMediaSettings.tsx:136
|
||||
msgid "Enable trending topics"
|
||||
msgstr ""
|
||||
msgstr "रुझान सक्षम करें"
|
||||
|
||||
#: src/screens/Messages/Settings.tsx:130
|
||||
#: src/screens/Messages/Settings.tsx:133
|
||||
@@ -2861,7 +2861,7 @@ msgstr "पुष्टिकरण कोड दर्ज करें"
|
||||
|
||||
#: src/view/com/util/post-embeds/VideoEmbedInner/web-controls/VideoControls.tsx:405
|
||||
msgid "Enter fullscreen"
|
||||
msgstr ""
|
||||
msgstr "फ़ुलस्क्रीन में जाएँ"
|
||||
|
||||
#: src/view/com/modals/ChangePassword.tsx:154
|
||||
msgid "Enter the code you received to change your password."
|
||||
@@ -3166,7 +3166,7 @@ msgstr "{0} द्वारा फ़ीड"
|
||||
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:353
|
||||
msgid "Feed menu"
|
||||
msgstr ""
|
||||
msgstr "फ़ीड मेनू"
|
||||
|
||||
#: src/view/screens/Feeds.tsx:709
|
||||
#~ msgid "Feed offline"
|
||||
@@ -3217,7 +3217,7 @@ msgstr "फ़ीड अपडेट की गई!"
|
||||
|
||||
#: src/screens/Search/components/ExploreRecommendations.tsx:54
|
||||
msgid "Feeds we think you might like."
|
||||
msgstr ""
|
||||
msgstr "फ़ीड जो हमें लगता है आपको पसंद आए।"
|
||||
|
||||
#: src/view/com/modals/ChangeHandle.tsx:468
|
||||
#~ msgid "File Contents"
|
||||
@@ -3245,7 +3245,7 @@ msgstr "फ़ॉलो करने के लिए खाते खोजे
|
||||
#: src/components/ProgressGuide/FollowDialog.tsx:88
|
||||
#: src/components/ProgressGuide/FollowDialog.tsx:417
|
||||
msgid "Find people to follow"
|
||||
msgstr ""
|
||||
msgstr "फ़ॉलो करने के लिए लोग खोजें"
|
||||
|
||||
#: src/view/screens/Search/Search.tsx:595
|
||||
msgid "Find posts and users on Bluesky"
|
||||
@@ -3305,7 +3305,7 @@ msgstr "{name} को फ़ॉलो करें"
|
||||
#: src/components/ProgressGuide/List.tsx:52
|
||||
#: src/state/shell/progress-guide.tsx:229
|
||||
msgid "Follow 10 accounts"
|
||||
msgstr ""
|
||||
msgstr "10 खाते फ़ॉलो करें"
|
||||
|
||||
#: src/components/ProgressGuide/List.tsx:69
|
||||
msgid "Follow 7 accounts"
|
||||
@@ -3732,13 +3732,13 @@ msgstr "यह पोस्ट छिपाएँ?"
|
||||
#: src/screens/Search/components/ExploreTrendingTopics.tsx:83
|
||||
#: src/view/shell/desktop/SidebarTrendingTopics.tsx:62
|
||||
msgid "Hide trending topics"
|
||||
msgstr ""
|
||||
msgstr "रुझान छिपाएँ"
|
||||
|
||||
#: src/components/interstitials/Trending.tsx:99
|
||||
#: src/screens/Search/components/ExploreTrendingTopics.tsx:135
|
||||
#: src/view/shell/desktop/SidebarTrendingTopics.tsx:108
|
||||
msgid "Hide trending topics?"
|
||||
msgstr ""
|
||||
msgstr "रुझान छिपाएँ?"
|
||||
|
||||
#: src/view/com/notifications/NotificationFeedItem.tsx:592
|
||||
msgid "Hide user list"
|
||||
@@ -3806,7 +3806,7 @@ msgstr "होस्टिंग प्रदाता"
|
||||
#: src/view/com/post-thread/PostThread.tsx:652
|
||||
#: src/view/com/post-thread/PostThread.tsx:657
|
||||
msgid "Hot replies first"
|
||||
msgstr ""
|
||||
msgstr "गर्मागर्म जवाब पहले"
|
||||
|
||||
#: src/view/com/modals/InAppBrowserConsent.tsx:41
|
||||
msgid "How should we open this link?"
|
||||
@@ -3859,7 +3859,7 @@ msgstr "यदि आप इस सूची को मिटते हैं,
|
||||
|
||||
#: src/screens/Settings/components/ChangeHandleDialog.tsx:250
|
||||
msgid "If you have your own domain, you can use that as your handle. This lets you self-verify your identity. <0>Learn more here.</0>"
|
||||
msgstr ""
|
||||
msgstr "यदि आपका अपना डोमेन है, आप उसे अपने हैंडल के तौर पर उपयोग कर सकते हैं। इसे आप ख़ुद अपने पहचान की पुष्टि कर सकते हैं। <0>यहाँ अधिक जानें।</0>"
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:658
|
||||
msgid "If you remove this post, you won't be able to recover it."
|
||||
@@ -4216,11 +4216,11 @@ msgstr "रोशन"
|
||||
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:514
|
||||
msgid "Like"
|
||||
msgstr ""
|
||||
msgstr "पसंद"
|
||||
|
||||
#: src/view/com/util/post-ctrls/PostCtrls.tsx:311
|
||||
msgid "Like ({0, plural, one {# like} other {# likes}})"
|
||||
msgstr ""
|
||||
msgstr "पसंद करें ({0, plural, one {# पसंद} other {# पसंद}})"
|
||||
|
||||
#: src/components/ProgressGuide/List.tsx:63
|
||||
msgid "Like 10 posts"
|
||||
@@ -4233,7 +4233,7 @@ msgstr "डिस्कवर फ़ीड को प्रशिक्षित
|
||||
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:501
|
||||
msgid "Like feed"
|
||||
msgstr ""
|
||||
msgstr "फ़ीड पसंद करें"
|
||||
|
||||
#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:275
|
||||
msgid "Like this feed"
|
||||
@@ -4254,14 +4254,14 @@ msgstr "इन्होनें पसंद किया"
|
||||
#: src/components/FeedCard.tsx:213
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:303
|
||||
msgid "Liked by {0, plural, one {# user} other {# users}}"
|
||||
msgstr ""
|
||||
msgstr "{0, plural, one {# उपयोगकर्ता} other {# उपयोगकर्ताओं}} ने पसंद किया"
|
||||
|
||||
#: src/components/LabelingServiceCard/index.tsx:96
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:489
|
||||
#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:295
|
||||
#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:309
|
||||
msgid "Liked by {likeCount, plural, one {# user} other {# users}}"
|
||||
msgstr ""
|
||||
msgstr "{likeCount, plural, one {# उपयोगकर्ता} other {# उपयोगकर्ताओं}} ने पसंद किया"
|
||||
|
||||
#: src/view/com/notifications/FeedItem.tsx:211
|
||||
#~ msgid "liked your custom feed"
|
||||
@@ -4282,7 +4282,7 @@ msgstr "इस पोस्ट पर पसंद"
|
||||
#: src/view/com/post-thread/PostThread.tsx:626
|
||||
#: src/view/com/post-thread/PostThread.tsx:631
|
||||
msgid "Linear"
|
||||
msgstr ""
|
||||
msgstr "रेखीय"
|
||||
|
||||
#: src/Navigation.tsx:198
|
||||
msgid "List"
|
||||
@@ -4303,11 +4303,11 @@ msgstr "{0} द्वारा सूची"
|
||||
|
||||
#: src/view/com/profile/ProfileSubpageHeader.tsx:156
|
||||
msgid "List by <0/>"
|
||||
msgstr ""
|
||||
msgstr "<0/> द्वारा सूची"
|
||||
|
||||
#: src/view/com/profile/ProfileSubpageHeader.tsx:154
|
||||
msgid "List by you"
|
||||
msgstr ""
|
||||
msgstr "आपके द्वारा सूची"
|
||||
|
||||
#: src/view/screens/ProfileList.tsx:455
|
||||
msgid "List deleted"
|
||||
@@ -4412,7 +4412,7 @@ msgstr "उस खाते में लॉग इन करें जो स
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:121
|
||||
msgid "Logo by @sawaratsuki.bsky.social"
|
||||
msgstr ""
|
||||
msgstr "@sawaratsuki.bsky.social द्वारा लोगो"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:103
|
||||
#~ msgid "Logo by <0/>"
|
||||
@@ -4483,7 +4483,7 @@ msgstr "उल्लेखित उपयोगकर्ता"
|
||||
|
||||
#: src/view/screens/Notifications.tsx:99
|
||||
msgid "Mentions"
|
||||
msgstr ""
|
||||
msgstr "उल्लेख"
|
||||
|
||||
#: src/components/Menu/index.tsx:96
|
||||
#: src/view/screens/Search/Search.tsx:863
|
||||
@@ -4837,7 +4837,7 @@ msgstr "नया हैंडल"
|
||||
#: src/view/screens/Lists.tsx:66
|
||||
#: src/view/screens/ModerationModlists.tsx:66
|
||||
msgid "New list"
|
||||
msgstr ""
|
||||
msgstr "नई सूची"
|
||||
|
||||
#: src/components/dms/NewMessagesPill.tsx:92
|
||||
msgid "New messages"
|
||||
@@ -5241,7 +5241,7 @@ msgstr "बातचीत विकल्प खोलें"
|
||||
|
||||
#: src/components/Layout/Header/index.tsx:145
|
||||
msgid "Open drawer menu"
|
||||
msgstr ""
|
||||
msgstr "ड्रॉअर मेनू खोलें"
|
||||
|
||||
#: src/screens/Messages/components/MessageInput.web.tsx:181
|
||||
#: src/view/com/composer/Composer.tsx:1228
|
||||
@@ -5251,7 +5251,7 @@ msgstr "इमोजी चयन खोलें"
|
||||
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:190
|
||||
msgid "Open feed info screen"
|
||||
msgstr ""
|
||||
msgstr "फ़ीड जानकारी स्क्रीन खोलें"
|
||||
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:290
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:295
|
||||
@@ -5582,7 +5582,7 @@ msgstr "वयस्कों के लिए छवि।"
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:519
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:526
|
||||
msgid "Pin feed"
|
||||
msgstr ""
|
||||
msgstr "फ़ीड पिन करें"
|
||||
|
||||
#: src/view/screens/ProfileList.tsx:685
|
||||
msgid "Pin to home"
|
||||
@@ -5604,7 +5604,7 @@ msgstr "पिन किया गया"
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:162
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:174
|
||||
msgid "Pinned {0} to Home"
|
||||
msgstr ""
|
||||
msgstr "होम में {0} पिन किया गया"
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:123
|
||||
msgid "Pinned Feeds"
|
||||
@@ -5912,11 +5912,11 @@ msgstr "सार्वजनिक"
|
||||
|
||||
#: src/view/com/lists/MyLists.tsx:77
|
||||
msgid "Public, sharable lists of users to mute or block in bulk."
|
||||
msgstr ""
|
||||
msgstr "उपयोगकर्ताओं की सार्वजनिक, साझा योग्य सूचियाँ जिन्हें एक साथ म्यूट या अवरुद्ध किया जा सकता है।"
|
||||
|
||||
#: src/view/com/lists/MyLists.tsx:72
|
||||
msgid "Public, sharable lists which can be used to drive feeds."
|
||||
msgstr ""
|
||||
msgstr "सार्वजनिक, साझा योग्य सूचियाँ जो फ़ीड चला सकती हैं।"
|
||||
|
||||
#: src/view/screens/ModerationModlists.tsx:75
|
||||
#~ msgid "Public, shareable lists of users to mute or block in bulk."
|
||||
@@ -6024,7 +6024,7 @@ msgstr "हाल के खोज"
|
||||
|
||||
#: src/screens/Search/components/ExploreRecommendations.tsx:50
|
||||
msgid "Recommended"
|
||||
msgstr ""
|
||||
msgstr "अनुशंसित"
|
||||
|
||||
#: src/view/com/auth/onboarding/RecommendedFeeds.tsx:117
|
||||
#~ msgid "Recommended Feeds"
|
||||
@@ -6216,7 +6216,7 @@ msgstr "जवाब दें"
|
||||
|
||||
#: src/view/com/util/post-ctrls/PostCtrls.tsx:263
|
||||
msgid "Reply ({0, plural, one {# reply} other {# replies}})"
|
||||
msgstr ""
|
||||
msgstr "जवाब दें ({0, plural, one {# जवाब} other {# जवाब}})"
|
||||
|
||||
#: src/view/screens/PreferencesFollowingFeed.tsx:142
|
||||
#~ msgid "Reply Filters"
|
||||
@@ -6242,7 +6242,7 @@ msgstr "जवाब सेटिंग थ्रेड का लेखक च
|
||||
|
||||
#: src/view/com/post-thread/PostThread.tsx:648
|
||||
msgid "Reply sorting"
|
||||
msgstr ""
|
||||
msgstr "जवाब क्रम"
|
||||
|
||||
#: src/view/com/post/Post.tsx:204
|
||||
#: src/view/com/posts/PostFeedItem.tsx:553
|
||||
@@ -6365,7 +6365,7 @@ msgstr "रीपोस्ट"
|
||||
|
||||
#: src/view/com/util/post-ctrls/RepostButton.tsx:74
|
||||
msgid "Repost ({0, plural, one {# repost} other {# reposts}})"
|
||||
msgstr ""
|
||||
msgstr "रीपोस्ट करें ({0, plural, one {# रीपोस्ट} other {# रीपोस्ट}})"
|
||||
|
||||
#: src/screens/StarterPack/StarterPackScreen.tsx:548
|
||||
#: src/view/com/util/post-ctrls/RepostButton.tsx:148
|
||||
@@ -6637,15 +6637,15 @@ msgstr "खोजें"
|
||||
|
||||
#: src/components/ProgressGuide/FollowDialog.tsx:745
|
||||
msgid "Search by name or interest"
|
||||
msgstr ""
|
||||
msgstr "नाम या दिलचस्पी से खोजें"
|
||||
|
||||
#: src/view/screens/Feeds.tsx:441
|
||||
msgid "Search feeds"
|
||||
msgstr ""
|
||||
msgstr "फ़ीड खोजें"
|
||||
|
||||
#: src/components/ProgressGuide/FollowDialog.tsx:575
|
||||
msgid "Search for \"{interestsDisplayName}\"{activeText}"
|
||||
msgstr ""
|
||||
msgstr "\"{interestsDisplayName}\"{activeText} खोजें"
|
||||
|
||||
#: src/view/shell/desktop/Search.tsx:201
|
||||
msgid "Search for \"{query}\""
|
||||
@@ -6716,7 +6716,7 @@ msgstr "यह गाइड देखें"
|
||||
|
||||
#: src/view/com/util/post-embeds/VideoEmbedInner/web-controls/Scrubber.tsx:194
|
||||
msgid "Seek slider. Use the arrow keys to seek forwards and backwards, and space to play/pause"
|
||||
msgstr ""
|
||||
msgstr "सीक स्लाइडर। एरो की से आगे और पीछे सीक करें, और स्पेस से चालू/बंद करें"
|
||||
|
||||
#: src/view/com/util/Selector.tsx:107
|
||||
#~ msgid "Select {item}"
|
||||
@@ -6909,7 +6909,7 @@ msgstr "सर्वर पता"
|
||||
|
||||
#: src/screens/Settings/AppIconSettings/index.tsx:178
|
||||
msgid "Set app icon to {0}"
|
||||
msgstr ""
|
||||
msgstr "ऐप आइकॉन को {0} करें"
|
||||
|
||||
#: src/screens/Moderation/index.tsx:290
|
||||
msgid "Set birthdate"
|
||||
@@ -7030,7 +7030,7 @@ msgstr "QR कोड साझा करें"
|
||||
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:470
|
||||
msgid "Share this feed"
|
||||
msgstr ""
|
||||
msgstr "इस फ़ीड को साझा करें"
|
||||
|
||||
#: src/screens/StarterPack/StarterPackScreen.tsx:416
|
||||
msgid "Share this starter pack"
|
||||
@@ -7129,11 +7129,11 @@ msgstr "जवाब दिखाएँ"
|
||||
|
||||
#: src/view/com/post-thread/PostThread.tsx:622
|
||||
msgid "Show replies as"
|
||||
msgstr ""
|
||||
msgstr "जवाबों को ऐसे दिखाएँ"
|
||||
|
||||
#: src/screens/Settings/ThreadPreferences.tsx:151
|
||||
msgid "Show replies as threaded"
|
||||
msgstr ""
|
||||
msgstr "जवाबों को थ्रेड में दिखाएँ"
|
||||
|
||||
#: src/screens/Settings/ThreadPreferences.tsx:126
|
||||
msgid "Show replies by people you follow before all other replies"
|
||||
@@ -7334,7 +7334,7 @@ msgstr "कोई गड़बड़ हुई!"
|
||||
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:538
|
||||
msgid "Something wrong? Let us know."
|
||||
msgstr ""
|
||||
msgstr "कुछ गड़बड़ है? हमें बताएँ।"
|
||||
|
||||
#: src/App.native.tsx:122
|
||||
#: src/App.web.tsx:97
|
||||
@@ -7390,12 +7390,12 @@ msgstr "नया बातचीत शुरू करें"
|
||||
#: src/view/screens/ProfileList.tsx:817
|
||||
#: src/view/screens/ProfileList.tsx:938
|
||||
msgid "Start adding people"
|
||||
msgstr ""
|
||||
msgstr "लोगों को जोड़ना शुरू करें"
|
||||
|
||||
#: src/view/screens/ProfileList.tsx:824
|
||||
#: src/view/screens/ProfileList.tsx:945
|
||||
msgid "Start adding people!"
|
||||
msgstr ""
|
||||
msgstr "लोगों को जोड़ना शुरू करें!"
|
||||
|
||||
#: src/components/dms/dialogs/SearchablePeopleList.tsx:353
|
||||
msgid "Start chat with {displayName}"
|
||||
@@ -7413,7 +7413,7 @@ msgstr "{0} द्वारा स्टार्टर पैक"
|
||||
|
||||
#: src/view/com/profile/ProfileSubpageHeader.tsx:182
|
||||
msgid "Starter pack by <0/>"
|
||||
msgstr ""
|
||||
msgstr "<0/> द्वारा स्टार्टर पैक"
|
||||
|
||||
#: src/components/StarterPack/StarterPackCard.tsx:80
|
||||
#: src/view/com/profile/ProfileSubpageHeader.tsx:180
|
||||
@@ -7560,12 +7560,12 @@ msgstr "केवल टैग"
|
||||
|
||||
#: src/screens/Settings/AppIconSettings/index.tsx:216
|
||||
msgid "Tap to change app icon"
|
||||
msgstr ""
|
||||
msgstr "ऐप आइकॉन बदलने के लिए टैप करें"
|
||||
|
||||
#: src/view/com/composer/text-input/web/EmojiPicker.web.tsx:138
|
||||
#: src/view/com/composer/text-input/web/EmojiPicker.web.tsx:174
|
||||
msgid "Tap to close the emoji picker"
|
||||
msgstr ""
|
||||
msgstr "इमोजी चयन बंद करने के लिए टैप करें"
|
||||
|
||||
#: src/components/ProgressGuide/Toast.tsx:156
|
||||
msgid "Tap to dismiss"
|
||||
@@ -7590,7 +7590,7 @@ msgstr "पूरी छवि देखने के लिए दबाएँ"
|
||||
|
||||
#: src/state/shell/progress-guide.tsx:233
|
||||
msgid "Task complete - 10 follows!"
|
||||
msgstr ""
|
||||
msgstr "कार्य समाप्त - 10 फ़ॉलो!"
|
||||
|
||||
#: src/state/shell/progress-guide.tsx:223
|
||||
msgid "Task complete - 10 likes!"
|
||||
@@ -7693,7 +7693,7 @@ msgstr "अनअवरुद्ध करने के बाद खाता
|
||||
#: src/screens/Settings/AppIconSettings/index.tsx:41
|
||||
#: src/screens/Settings/AppIconSettings/index.tsx:222
|
||||
msgid "The app will be restarted"
|
||||
msgstr ""
|
||||
msgstr "ऐप को फिर से शुरू किया जाएगा"
|
||||
|
||||
#: src/components/moderation/ModerationDetailsDialog.tsx:127
|
||||
#~ msgid "the author"
|
||||
@@ -8006,7 +8006,7 @@ msgstr "<0>{0}</0> द्वारा बनाई गई इस सूची
|
||||
|
||||
#: src/view/screens/ProfileList.tsx:933
|
||||
msgid "This list is empty."
|
||||
msgstr ""
|
||||
msgstr "यह सूची खाली है।"
|
||||
|
||||
#: src/screens/Profile/ErrorState.tsx:40
|
||||
msgid "This moderation service is unavailable. See below for more details. If this issue persists, contact us."
|
||||
@@ -8105,7 +8105,7 @@ msgstr "यह आपके पोस्ट को इस क्वोट पो
|
||||
#: src/view/com/post-thread/PostThread.tsx:606
|
||||
#: src/view/com/post-thread/PostThread.tsx:609
|
||||
msgid "Thread options"
|
||||
msgstr ""
|
||||
msgstr "थ्रेड विकल्प"
|
||||
|
||||
#: src/screens/Settings/ContentAndMediaSettings.tsx:66
|
||||
#: src/screens/Settings/ContentAndMediaSettings.tsx:69
|
||||
@@ -8119,7 +8119,7 @@ msgstr "थ्रेड प्राथमिकताएँ"
|
||||
#: src/view/com/post-thread/PostThread.tsx:636
|
||||
#: src/view/com/post-thread/PostThread.tsx:641
|
||||
msgid "Threaded"
|
||||
msgstr ""
|
||||
msgstr "थ्रेड"
|
||||
|
||||
#: src/screens/Settings/ThreadPreferences.tsx:142
|
||||
msgid "Threaded mode"
|
||||
@@ -8135,7 +8135,7 @@ msgstr "थ्रेड प्राथमिकताएँ"
|
||||
|
||||
#: src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx:33
|
||||
msgid "Time remaining: {time} seconds"
|
||||
msgstr ""
|
||||
msgstr "समय बाक़ी: {time} सेकंड"
|
||||
|
||||
#: src/screens/Settings/components/DisableEmail2FADialog.tsx:99
|
||||
msgid "To disable the email 2FA method, please verify your access to the email address."
|
||||
@@ -8173,7 +8173,7 @@ msgstr "बहतरीन"
|
||||
|
||||
#: src/Navigation.tsx:383
|
||||
msgid "Topic"
|
||||
msgstr ""
|
||||
msgstr "विषय"
|
||||
|
||||
#: src/view/com/modals/EditImage.tsx:272
|
||||
#~ msgid "Transformations"
|
||||
@@ -8191,7 +8191,7 @@ msgstr "अनुवाद करें"
|
||||
#: src/screens/Search/components/ExploreTrendingTopics.tsx:69
|
||||
#: src/view/shell/desktop/SidebarTrendingTopics.tsx:59
|
||||
msgid "Trending"
|
||||
msgstr ""
|
||||
msgstr "रुझान"
|
||||
|
||||
#: src/view/com/util/error/ErrorScreen.tsx:103
|
||||
msgctxt "action"
|
||||
@@ -8285,7 +8285,7 @@ msgstr "रीपोस्ट पूर्ववत करें"
|
||||
|
||||
#: src/view/com/util/post-ctrls/RepostButton.tsx:68
|
||||
msgid "Undo repost ({0, plural, one {# repost} other {# reposts}})"
|
||||
msgstr ""
|
||||
msgstr "रीपोस्ट पूर्ववत करें ({0, plural, one {# रीपोस्ट} other {# रीपोस्ट}})"
|
||||
|
||||
#: src/view/com/profile/FollowButton.tsx:60
|
||||
msgctxt "action"
|
||||
@@ -8307,11 +8307,11 @@ msgstr "खाता अनफ़ॉलो करें"
|
||||
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:514
|
||||
msgid "Unlike"
|
||||
msgstr ""
|
||||
msgstr "नापसंद करें"
|
||||
|
||||
#: src/view/com/util/post-ctrls/PostCtrls.tsx:305
|
||||
msgid "Unlike ({0, plural, one {# like} other {# likes}})"
|
||||
msgstr ""
|
||||
msgstr "नापसंद करें ({0, plural, one {# पसंद} other {# पसंद}})"
|
||||
|
||||
#: src/view/screens/ProfileFeed.tsx:576
|
||||
#~ msgid "Unlike this feed"
|
||||
@@ -8361,7 +8361,7 @@ msgstr "पिन से हटाएँ"
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:519
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:526
|
||||
msgid "Unpin feed"
|
||||
msgstr ""
|
||||
msgstr "फ़ीड को पिन से हटाएँ"
|
||||
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:309
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:311
|
||||
@@ -8379,7 +8379,7 @@ msgstr "मॉडरेशन सूची को पिन से हटाए
|
||||
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:164
|
||||
msgid "Unpinned {0} from Home"
|
||||
msgstr ""
|
||||
msgstr "{0} को होम पिन से हटाया गया"
|
||||
|
||||
#: src/view/screens/ProfileList.tsx:352
|
||||
msgid "Unpinned from your feeds"
|
||||
@@ -8404,11 +8404,11 @@ msgstr "सूची की सदस्यता छोड़ी गई"
|
||||
|
||||
#: src/view/com/composer/Composer.tsx:766
|
||||
msgid "Unsupported video type"
|
||||
msgstr ""
|
||||
msgstr "असमर्थित वीडियो प्रकार"
|
||||
|
||||
#: src/view/com/composer/videos/SelectVideoBtn.tsx:66
|
||||
msgid "Unsupported video type: {0}"
|
||||
msgstr ""
|
||||
msgstr "असमर्थित वीडियो प्रकार: {0}"
|
||||
|
||||
#: src/view/com/composer/videos/SelectVideoBtn.tsx:72
|
||||
#~ msgid "Unsupported video type: {mimeType}"
|
||||
@@ -8815,7 +8815,7 @@ msgstr "साइट पर जाएँ"
|
||||
|
||||
#: src/view/com/util/post-embeds/VideoEmbedInner/web-controls/VolumeControl.tsx:80
|
||||
msgid "Volume"
|
||||
msgstr ""
|
||||
msgstr "वॉल्यूम"
|
||||
|
||||
#: src/components/moderation/LabelPreference.tsx:136
|
||||
#: src/lib/moderation/useLabelBehaviorDescription.ts:17
|
||||
@@ -8837,7 +8837,7 @@ msgstr "हमें इस हैशटैग के लिए कोई पर
|
||||
|
||||
#: src/screens/Topic.tsx:178
|
||||
msgid "We couldn't find any results for that topic."
|
||||
msgstr ""
|
||||
msgstr "उस विषय से जुड़े हमें कोई परिणाम नहीं मिले।"
|
||||
|
||||
#: src/screens/Messages/Conversation.tsx:103
|
||||
msgid "We couldn't load this conversation"
|
||||
@@ -8951,7 +8951,7 @@ msgstr "आप अपने स्टार्टर पैक को क्य
|
||||
|
||||
#: src/screens/Search/components/ExploreTrendingTopics.tsx:79
|
||||
msgid "What people are posting about."
|
||||
msgstr ""
|
||||
msgstr "किसके बारे में लोग पोस्ट कर रहे हैं।"
|
||||
|
||||
#: src/view/com/auth/SplashScreen.tsx:38
|
||||
#: src/view/com/auth/SplashScreen.web.tsx:99
|
||||
@@ -9114,7 +9114,7 @@ msgstr "आप लॉग इन जारी रखने के लिए अ
|
||||
#: src/screens/Search/components/ExploreTrendingTopics.tsx:136
|
||||
#: src/view/shell/desktop/SidebarTrendingTopics.tsx:109
|
||||
msgid "You can update this later from your settings."
|
||||
msgstr ""
|
||||
msgstr "आप इसे बाद में अपने सेटिंग में बदल सकते हैं।"
|
||||
|
||||
#: src/view/com/profile/ProfileFollowers.tsx:94
|
||||
msgid "You do not have any followers."
|
||||
@@ -9348,7 +9348,7 @@ msgstr "आपने इस पोस्ट में से एक शब्द
|
||||
|
||||
#: src/state/shell/progress-guide.tsx:234
|
||||
msgid "You've found some people to follow"
|
||||
msgstr ""
|
||||
msgstr "आपने फ़ॉलो करने के लिए कुछ लोगों को ढूँढा है"
|
||||
|
||||
#: src/view/com/posts/FollowingEndOfFeed.tsx:44
|
||||
msgid "You've reached the end of your feed! Find some more accounts to follow."
|
||||
@@ -9396,7 +9396,7 @@ msgstr "आपका चयन सहेजा जाएगा, पर सेट
|
||||
|
||||
#: src/screens/Settings/components/ChangeHandleDialog.tsx:496
|
||||
msgid "Your current handle <0>{0}</0> will automatically remain reserved for you. You can switch back to it at any time from this account."
|
||||
msgstr ""
|
||||
msgstr "आपका वर्तमान हैंडल <0>{0}</0> आपके लिए आरक्षित रहेगा। आप इस खाते से कभी भी उस हैंडल पर वापस जा सकते हैं।"
|
||||
|
||||
#: src/screens/Login/ForgotPasswordForm.tsx:51
|
||||
#: src/screens/Signup/state.ts:203
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+152
-152
@@ -8,8 +8,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Italian localization\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-01-24 00:43+0000\n"
|
||||
"PO-Revision-Date: 2025-01-24 17:53+0000\n"
|
||||
"POT-Creation-Date: 2025-02-04 00:43+0000\n"
|
||||
"PO-Revision-Date: 2025-02-04 07:28+0000\n"
|
||||
"Last-Translator: Michele Locati <michele@locati.it>, 2025\n"
|
||||
"Language-Team: Italian (https://app.transifex.com/mlocati/teams/201833/it/)\n"
|
||||
"Language: it\n"
|
||||
@@ -23,7 +23,7 @@ msgstr ""
|
||||
msgid "(active)"
|
||||
msgstr "(attivo)"
|
||||
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:139
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:145
|
||||
msgid "(contains embedded content)"
|
||||
msgstr "(contiene allegati)"
|
||||
|
||||
@@ -374,9 +374,9 @@ msgstr "7 giorni"
|
||||
#: src/screens/Settings/Settings.tsx:215
|
||||
#: src/screens/Settings/Settings.tsx:218
|
||||
msgid "About"
|
||||
msgstr "Informazioni su"
|
||||
msgstr "Risorse aggiuntive"
|
||||
|
||||
#: src/view/screens/Search/Search.tsx:858
|
||||
#: src/view/screens/Search/Search.tsx:864
|
||||
msgid "Access navigation links and settings"
|
||||
msgstr "Accedi alle impostazioni di navigazione"
|
||||
|
||||
@@ -728,10 +728,10 @@ msgstr "Si è verificato un problema nell'aprire la chat"
|
||||
|
||||
#: src/components/hooks/useFollowMethods.ts:35
|
||||
#: src/components/hooks/useFollowMethods.ts:50
|
||||
#: src/components/ProfileCard.tsx:330
|
||||
#: src/components/ProfileCard.tsx:351
|
||||
#: src/view/com/profile/FollowButton.tsx:35
|
||||
#: src/view/com/profile/FollowButton.tsx:45
|
||||
#: src/components/ProfileCard.tsx:333
|
||||
#: src/components/ProfileCard.tsx:354
|
||||
#: src/view/com/profile/FollowButton.tsx:38
|
||||
#: src/view/com/profile/FollowButton.tsx:48
|
||||
msgid "An issue occurred, please try again."
|
||||
msgstr "Si è verificato un problema, riprova."
|
||||
|
||||
@@ -928,7 +928,7 @@ msgstr "Le opzioni di riproduzione automatica sono state spostate nelle <0>impos
|
||||
msgid "Autoplay videos and GIFs"
|
||||
msgstr "Riproduci automaticamente video e GIF"
|
||||
|
||||
#: src/components/dms/MessagesListHeader.tsx:74
|
||||
#: src/components/dms/MessagesListHeader.tsx:81
|
||||
#: src/components/moderation/LabelsOnMeDialog.tsx:290
|
||||
#: src/components/moderation/LabelsOnMeDialog.tsx:291
|
||||
#: src/screens/Login/ChooseAccountForm.tsx:90
|
||||
@@ -962,7 +962,7 @@ msgstr "Prima di creare uno starter pack, devi verificare la tua email."
|
||||
|
||||
#: src/components/dms/dialogs/NewChatDialog.tsx:79
|
||||
#: src/components/dms/MessageProfileButton.tsx:89
|
||||
#: src/screens/Messages/Conversation.tsx:209
|
||||
#: src/screens/Messages/Conversation.tsx:204
|
||||
msgid "Before you may message another user, you must first verify your email."
|
||||
msgstr "Prima di poter inviare un messaggio a un altro utente, devi verificare la tua email."
|
||||
|
||||
@@ -971,7 +971,7 @@ msgstr "Prima di poter inviare un messaggio a un altro utente, devi verificare l
|
||||
msgid "BETA"
|
||||
msgstr "BETA"
|
||||
|
||||
#: src/components/dialogs/BirthDateSettings.tsx:106
|
||||
#: src/components/dialogs/BirthDateSettings.tsx:102
|
||||
#: src/screens/Settings/AccountSettings.tsx:109
|
||||
msgid "Birthday"
|
||||
msgstr "Compleanno"
|
||||
@@ -982,8 +982,8 @@ msgstr "Compleanno"
|
||||
msgid "Block"
|
||||
msgstr "Blocca"
|
||||
|
||||
#: src/components/dms/ConvoMenu.tsx:191
|
||||
#: src/components/dms/ConvoMenu.tsx:195
|
||||
#: src/components/dms/ConvoMenu.tsx:250
|
||||
#: src/components/dms/ConvoMenu.tsx:253
|
||||
#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:603
|
||||
#: src/view/com/util/forms/PostDropdownBtnMenuItems.tsx:605
|
||||
msgid "Block account"
|
||||
@@ -1070,8 +1070,8 @@ msgstr "Il blocco non impedirà l'applicazione delle etichette al tuo account, m
|
||||
msgid "Blog"
|
||||
msgstr "Blog"
|
||||
|
||||
#: src/view/com/auth/server-input/index.tsx:92
|
||||
#: src/view/com/auth/server-input/index.tsx:94
|
||||
#: src/view/com/auth/server-input/index.tsx:132
|
||||
#: src/view/com/auth/server-input/index.tsx:133
|
||||
msgid "Bluesky"
|
||||
msgstr "Bluesky"
|
||||
|
||||
@@ -1079,13 +1079,13 @@ msgstr "Bluesky"
|
||||
msgid "Bluesky cannot confirm the authenticity of the claimed date."
|
||||
msgstr "Bluesky non può confermare l'autenticità della data dichiarata."
|
||||
|
||||
#: src/view/com/auth/server-input/index.tsx:172
|
||||
#: src/view/com/auth/server-input/index.tsx:208
|
||||
msgid "Bluesky is an open network where you can choose your hosting provider. If you're a developer, you can host your own server."
|
||||
msgstr "Bluesky è una rete aperta dove puoi scegliere il fornitore che ospita i tuoi dati. Se sei uno sviluppatore, puoi utilizzare il tuo server."
|
||||
|
||||
#: src/view/com/auth/server-input/index.tsx:109
|
||||
#: src/view/com/auth/server-input/index.tsx:145
|
||||
msgid "Bluesky is an open network where you can choose your own provider. If you're new here, we recommend sticking with the default Bluesky Social option."
|
||||
msgstr "Bluesky è una rete aperta che ti permette di scegliere il fornitore dei tuoi dati. Se sei un nuovo utente, ti consigliamo di iniziare con l'opzione predefinita Bluesky Social."
|
||||
msgstr "Bluesky è una rete aperta che ti permette di scegliere chi ospita i tuoi dati. Se sei un nuovo utente, ti consigliamo di iniziare con l'opzione predefinita Bluesky Social."
|
||||
|
||||
#: src/components/ProgressGuide/List.tsx:53
|
||||
#: src/components/ProgressGuide/List.tsx:70
|
||||
@@ -1094,7 +1094,7 @@ msgstr "Bluesky è meglio con gli amici!"
|
||||
|
||||
#: src/screens/Takendown.tsx:217
|
||||
msgid "Bluesky Social Terms of Service"
|
||||
msgstr "Termini di servizio di Bluesky Social"
|
||||
msgstr "Termini di servizio di Bluesky"
|
||||
|
||||
#: src/components/StarterPack/ProfileStarterPacks.tsx:299
|
||||
msgid "Bluesky will choose a set of recommended accounts from people in your network."
|
||||
@@ -1125,23 +1125,23 @@ msgstr "Sfoca le immagini e filtra dai feed"
|
||||
msgid "Books"
|
||||
msgstr "Libri"
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:349
|
||||
#: src/components/FeedInterstitials.tsx:370
|
||||
msgid "Browse more accounts on the Explore page"
|
||||
msgstr "Scopri altri account dalla Ricerca"
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:486
|
||||
#: src/components/FeedInterstitials.tsx:507
|
||||
msgid "Browse more feeds on the Explore page"
|
||||
msgstr "Scopri nuovi feed dalla Ricerca"
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:330
|
||||
#: src/components/FeedInterstitials.tsx:333
|
||||
#: src/components/FeedInterstitials.tsx:467
|
||||
#: src/components/FeedInterstitials.tsx:470
|
||||
#: src/components/FeedInterstitials.tsx:351
|
||||
#: src/components/FeedInterstitials.tsx:354
|
||||
#: src/components/FeedInterstitials.tsx:488
|
||||
#: src/components/FeedInterstitials.tsx:491
|
||||
msgid "Browse more suggestions"
|
||||
msgstr "Scopri nuovi suggerimenti"
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:357
|
||||
#: src/components/FeedInterstitials.tsx:495
|
||||
#: src/components/FeedInterstitials.tsx:378
|
||||
#: src/components/FeedInterstitials.tsx:516
|
||||
msgid "Browse more suggestions on the Explore page"
|
||||
msgstr "Scopri nuovi suggerimenti dalla Ricerca"
|
||||
|
||||
@@ -1218,7 +1218,7 @@ msgstr "Fotocamera"
|
||||
#: src/view/com/modals/VerifyEmail.tsx:255
|
||||
#: src/view/com/modals/VerifyEmail.tsx:261
|
||||
#: src/view/com/util/post-ctrls/RepostButton.tsx:213
|
||||
#: src/view/screens/Search/Search.tsx:887
|
||||
#: src/view/screens/Search/Search.tsx:893
|
||||
#: src/view/shell/desktop/LeftNav.tsx:209
|
||||
msgid "Cancel"
|
||||
msgstr "Annulla"
|
||||
@@ -1252,7 +1252,7 @@ msgid "Cancel reactivation and log out"
|
||||
msgstr "Annulla la riattivazione e disconnettiti"
|
||||
|
||||
#: src/view/com/modals/ListAddRemoveUsers.tsx:88
|
||||
#: src/view/screens/Search/Search.tsx:879
|
||||
#: src/view/screens/Search/Search.tsx:885
|
||||
msgid "Cancel search"
|
||||
msgstr "Annulla la ricerca"
|
||||
|
||||
@@ -1331,14 +1331,14 @@ msgstr "Modifica il tuo indirizzo email"
|
||||
msgid "Chat"
|
||||
msgstr "Messaggi"
|
||||
|
||||
#: src/components/dms/ConvoMenu.tsx:85
|
||||
#: src/components/dms/ConvoMenu.tsx:179
|
||||
msgid "Chat muted"
|
||||
msgstr "Conversazione silenziata"
|
||||
|
||||
#: src/components/dms/ConvoMenu.tsx:115
|
||||
#: src/components/dms/ConvoMenu.tsx:78
|
||||
#: src/components/dms/MessageMenu.tsx:81
|
||||
#: src/Navigation.tsx:394
|
||||
#: src/screens/Messages/ChatList.tsx:254
|
||||
#: src/screens/Messages/ChatList.tsx:253
|
||||
msgid "Chat settings"
|
||||
msgstr "Impostazioni chat"
|
||||
|
||||
@@ -1346,7 +1346,7 @@ msgstr "Impostazioni chat"
|
||||
msgid "Chat Settings"
|
||||
msgstr "Impostazioni chat"
|
||||
|
||||
#: src/components/dms/ConvoMenu.tsx:87
|
||||
#: src/components/dms/ConvoMenu.tsx:181
|
||||
msgid "Chat unmuted"
|
||||
msgstr "Conversazione riattivata"
|
||||
|
||||
@@ -1391,12 +1391,12 @@ msgstr "Scegli gli algoritmi che compilano i tuoi feed personalizzati."
|
||||
msgid "Choose this color as your avatar"
|
||||
msgstr "Scegli questo colore per il tuo avatar"
|
||||
|
||||
#: src/view/com/auth/server-input/index.tsx:86
|
||||
#: src/view/com/auth/server-input/index.tsx:126
|
||||
msgid "Choose your account provider"
|
||||
msgstr "Seleziona il fornitore del tuo account"
|
||||
msgstr "Scegli il fornitore del tuo account"
|
||||
|
||||
#: src/view/screens/Feeds.tsx:728
|
||||
#: src/view/screens/Search/Explore.tsx:424
|
||||
#: src/view/screens/Search/Explore.tsx:427
|
||||
msgid "Choose your own timeline! Feeds built by the community help you find content you love."
|
||||
msgstr "Personalizza la tua cronologia! I feed creati dalla collettività ti permettono di scoprire i contenuti di tuo interesse."
|
||||
|
||||
@@ -1547,7 +1547,7 @@ msgstr "Linee guida della community"
|
||||
|
||||
#: src/screens/Onboarding/StepFinished.tsx:300
|
||||
msgid "Complete onboarding and start using your account"
|
||||
msgstr "Completa l'incorporazione e inizia a usare il tuo account"
|
||||
msgstr "Completa la registrazione e inizia a usare il tuo account"
|
||||
|
||||
#: src/screens/Signup/index.tsx:144
|
||||
msgid "Complete the challenge"
|
||||
@@ -1700,7 +1700,7 @@ msgstr "Continua thread..."
|
||||
msgid "Continue to next step"
|
||||
msgstr "Vai al passaggio successivo"
|
||||
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:173
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:179
|
||||
msgid "Conversation deleted"
|
||||
msgstr "Conversazione eliminata"
|
||||
|
||||
@@ -1807,7 +1807,7 @@ msgstr "Non è stato possibile caricare il feed"
|
||||
msgid "Could not load list"
|
||||
msgstr "Non è stato possibile caricare la lista"
|
||||
|
||||
#: src/components/dms/ConvoMenu.tsx:91
|
||||
#: src/components/dms/ConvoMenu.tsx:185
|
||||
msgid "Could not mute chat"
|
||||
msgstr "Errore nel silenziare la conversazione"
|
||||
|
||||
@@ -1842,7 +1842,7 @@ msgstr "Crea uno starter pack per me"
|
||||
#: src/view/shell/NavSignupCard.tsx:47
|
||||
#: src/view/shell/NavSignupCard.tsx:52
|
||||
msgid "Create account"
|
||||
msgstr "Crea un account"
|
||||
msgstr "Crea account"
|
||||
|
||||
#: src/screens/Signup/index.tsx:93
|
||||
msgid "Create Account"
|
||||
@@ -1883,8 +1883,8 @@ msgstr "L'autore è stato bloccato"
|
||||
msgid "Culture"
|
||||
msgstr "Cultura"
|
||||
|
||||
#: src/view/com/auth/server-input/index.tsx:100
|
||||
#: src/view/com/auth/server-input/index.tsx:102
|
||||
#: src/view/com/auth/server-input/index.tsx:138
|
||||
#: src/view/com/auth/server-input/index.tsx:139
|
||||
msgid "Custom"
|
||||
msgstr "Personalizzato"
|
||||
|
||||
@@ -1897,7 +1897,7 @@ msgstr "Personalizza chi può interagire con questo post."
|
||||
msgid "Dark"
|
||||
msgstr "Scuro"
|
||||
|
||||
#: src/view/screens/Debug.tsx:70
|
||||
#: src/view/screens/Debug.tsx:69
|
||||
msgid "Dark mode"
|
||||
msgstr "Aspetto scuro"
|
||||
|
||||
@@ -1919,7 +1919,7 @@ msgstr "Disattiva account"
|
||||
msgid "Debug Moderation"
|
||||
msgstr "Eliminare errori nella Moderazione"
|
||||
|
||||
#: src/view/screens/Debug.tsx:90
|
||||
#: src/view/screens/Debug.tsx:89
|
||||
msgid "Debug panel"
|
||||
msgstr "Pannello per il debug"
|
||||
|
||||
@@ -2018,8 +2018,8 @@ msgstr "Eliminare questo post?"
|
||||
msgid "Deleted"
|
||||
msgstr "Eliminato"
|
||||
|
||||
#: src/components/dms/MessagesListHeader.tsx:148
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:107
|
||||
#: src/components/dms/MessagesListHeader.tsx:154
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:113
|
||||
msgid "Deleted Account"
|
||||
msgstr "Account eliminato"
|
||||
|
||||
@@ -2120,7 +2120,7 @@ msgstr "Scoraggia le app dal mostrare il mio account agli utenti disconnessi"
|
||||
msgid "Discover new custom feeds"
|
||||
msgstr "Scopri nuovi feed personalizzati"
|
||||
|
||||
#: src/view/screens/Search/Explore.tsx:422
|
||||
#: src/view/screens/Search/Explore.tsx:425
|
||||
msgid "Discover new feeds"
|
||||
msgstr "Scopri nuovi feed"
|
||||
|
||||
@@ -2189,17 +2189,17 @@ msgstr "Non inizia o termina con un trattino"
|
||||
msgid "Domain verified!"
|
||||
msgstr "Dominio verificato!"
|
||||
|
||||
#: src/components/dialogs/BirthDateSettings.tsx:118
|
||||
#: src/components/dialogs/BirthDateSettings.tsx:124
|
||||
#: src/components/dialogs/BirthDateSettings.tsx:114
|
||||
#: src/components/dialogs/BirthDateSettings.tsx:120
|
||||
#: src/components/dms/ReportDialog.tsx:323
|
||||
#: src/components/forms/DateField/index.tsx:77
|
||||
#: src/components/forms/DateField/index.tsx:83
|
||||
#: src/components/forms/DateField/index.tsx:87
|
||||
#: src/components/forms/DateField/index.tsx:93
|
||||
#: src/screens/Onboarding/StepProfile/index.tsx:330
|
||||
#: src/screens/Onboarding/StepProfile/index.tsx:333
|
||||
#: src/screens/Settings/components/AddAppPasswordDialog.tsx:215
|
||||
#: src/screens/Settings/components/AddAppPasswordDialog.tsx:222
|
||||
#: src/view/com/auth/server-input/index.tsx:192
|
||||
#: src/view/com/auth/server-input/index.tsx:193
|
||||
#: src/view/com/auth/server-input/index.tsx:228
|
||||
#: src/view/com/auth/server-input/index.tsx:229
|
||||
#: src/view/com/composer/labels/LabelsBtn.tsx:224
|
||||
#: src/view/com/composer/labels/LabelsBtn.tsx:231
|
||||
#: src/view/com/composer/videos/SubtitleDialog.tsx:169
|
||||
@@ -2226,7 +2226,7 @@ msgstr "Fatto{extraText}"
|
||||
msgid "Double tap to close the dialog"
|
||||
msgstr "Tocca due volte per chiudere la finestra"
|
||||
|
||||
#: src/screens/VideoFeed/index.tsx:1037
|
||||
#: src/screens/VideoFeed/index.tsx:1039
|
||||
msgid "Double tap to like"
|
||||
msgstr "Tocca due volte per mettere mi piace"
|
||||
|
||||
@@ -2550,7 +2550,7 @@ msgstr "Inserisci il dominio che vuoi usare"
|
||||
msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password."
|
||||
msgstr "Inserisci l'email che hai usato per creare il tuo account. Ti invieremo un codice in modo che tu possa scegliere una nuova password."
|
||||
|
||||
#: src/components/dialogs/BirthDateSettings.tsx:107
|
||||
#: src/components/dialogs/BirthDateSettings.tsx:103
|
||||
msgid "Enter your birth date"
|
||||
msgstr "Inserisci la tua data di nascita"
|
||||
|
||||
@@ -2738,8 +2738,8 @@ msgstr "Non è stato possibile eliminare il post, riprovare"
|
||||
msgid "Failed to delete starter pack"
|
||||
msgstr "Impossibile eliminare lo starter pack"
|
||||
|
||||
#: src/view/screens/Search/Explore.tsx:460
|
||||
#: src/view/screens/Search/Explore.tsx:488
|
||||
#: src/view/screens/Search/Explore.tsx:463
|
||||
#: src/view/screens/Search/Explore.tsx:491
|
||||
msgid "Failed to load feeds preferences"
|
||||
msgstr "Impossibile caricare preferenze feed"
|
||||
|
||||
@@ -2751,12 +2751,12 @@ msgstr "Impossibile caricare GIF"
|
||||
msgid "Failed to load past messages"
|
||||
msgstr "Impossibile caricare messaggi vecchi"
|
||||
|
||||
#: src/view/screens/Search/Explore.tsx:453
|
||||
#: src/view/screens/Search/Explore.tsx:481
|
||||
#: src/view/screens/Search/Explore.tsx:456
|
||||
#: src/view/screens/Search/Explore.tsx:484
|
||||
msgid "Failed to load suggested feeds"
|
||||
msgstr "Impossibile caricare feed consigliati"
|
||||
|
||||
#: src/view/screens/Search/Explore.tsx:411
|
||||
#: src/view/screens/Search/Explore.tsx:414
|
||||
msgid "Failed to load suggested follows"
|
||||
msgstr "Impossibile caricare follow consigliati"
|
||||
|
||||
@@ -2897,16 +2897,16 @@ msgid "Flexible"
|
||||
msgstr "Flessibile"
|
||||
|
||||
#. User is not following this account, click to follow
|
||||
#: src/components/ProfileCard.tsx:363
|
||||
#: src/components/ProfileCard.tsx:366
|
||||
#: src/components/ProfileHoverCard/index.web.tsx:449
|
||||
#: src/components/ProfileHoverCard/index.web.tsx:460
|
||||
#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:234
|
||||
#: src/screens/VideoFeed/index.tsx:819
|
||||
#: src/screens/VideoFeed/index.tsx:821
|
||||
#: src/view/com/post-thread/PostThreadFollowBtn.tsx:132
|
||||
msgid "Follow"
|
||||
msgstr "Segui"
|
||||
|
||||
#: src/view/com/profile/FollowButton.tsx:69
|
||||
#: src/view/com/profile/FollowButton.tsx:72
|
||||
msgctxt "action"
|
||||
msgid "Follow"
|
||||
msgstr "Segui"
|
||||
@@ -2916,7 +2916,7 @@ msgstr "Segui"
|
||||
msgid "Follow {0}"
|
||||
msgstr "Segui {0}"
|
||||
|
||||
#: src/screens/VideoFeed/index.tsx:798
|
||||
#: src/screens/VideoFeed/index.tsx:800
|
||||
msgid "Follow {handle}"
|
||||
msgstr "Segui {handle}"
|
||||
|
||||
@@ -2948,14 +2948,14 @@ msgstr "Segui tutti"
|
||||
msgid "Follow Back"
|
||||
msgstr "Ricambia follow"
|
||||
|
||||
#: src/view/com/profile/FollowButton.tsx:78
|
||||
#: src/view/com/profile/FollowButton.tsx:81
|
||||
msgctxt "action"
|
||||
msgid "Follow Back"
|
||||
msgstr "Segui anche tu"
|
||||
|
||||
#: src/view/screens/Search/Explore.tsx:368
|
||||
#: src/view/screens/Search/Explore.tsx:370
|
||||
msgid "Follow more accounts to get connected to your interests and build your network."
|
||||
msgstr "Segui altri account per connetterti ai tuoi interessi e crea il tuo network personale."
|
||||
msgstr "Segui altri account per connetterti ai tuoi interessi e creare la tua rete personale."
|
||||
|
||||
#: src/components/KnownFollowers.tsx:232
|
||||
msgid "Followed by <0>{0}</0>"
|
||||
@@ -2987,23 +2987,23 @@ msgid "Followers you know"
|
||||
msgstr "Follower che conosci"
|
||||
|
||||
#. User is following this account, click to unfollow
|
||||
#: src/components/ProfileCard.tsx:357
|
||||
#: src/components/ProfileCard.tsx:360
|
||||
#: src/components/ProfileHoverCard/index.web.tsx:448
|
||||
#: src/components/ProfileHoverCard/index.web.tsx:459
|
||||
#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:230
|
||||
#: src/screens/VideoFeed/index.tsx:817
|
||||
#: src/screens/VideoFeed/index.tsx:819
|
||||
#: src/view/com/post-thread/PostThreadFollowBtn.tsx:135
|
||||
#: src/view/screens/Feeds.tsx:599
|
||||
#: src/view/screens/SavedFeeds.tsx:422
|
||||
msgid "Following"
|
||||
msgstr "Following"
|
||||
|
||||
#: src/components/ProfileCard.tsx:321
|
||||
#: src/components/ProfileCard.tsx:323
|
||||
#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:98
|
||||
msgid "Following {0}"
|
||||
msgstr "Stai seguendo {0}"
|
||||
|
||||
#: src/screens/VideoFeed/index.tsx:797
|
||||
#: src/screens/VideoFeed/index.tsx:799
|
||||
msgid "Following {handle}"
|
||||
msgstr "Stai seguendo {handle}"
|
||||
|
||||
@@ -3122,8 +3122,8 @@ msgstr "Evidenti violazioni della legge o dei termini di servizio"
|
||||
#: src/components/moderation/ScreenHider.tsx:163
|
||||
#: src/screens/Profile/ProfileFeed/index.tsx:89
|
||||
#: src/screens/VideoFeed/components/Header.tsx:163
|
||||
#: src/screens/VideoFeed/index.tsx:1098
|
||||
#: src/screens/VideoFeed/index.tsx:1102
|
||||
#: src/screens/VideoFeed/index.tsx:1100
|
||||
#: src/screens/VideoFeed/index.tsx:1104
|
||||
#: src/view/com/auth/LoggedOut.tsx:72
|
||||
#: src/view/screens/NotFound.tsx:57
|
||||
#: src/view/screens/ProfileList.tsx:1009
|
||||
@@ -3166,7 +3166,7 @@ msgstr "Vai alla home"
|
||||
msgid "Go Home"
|
||||
msgstr "Vai alla home"
|
||||
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:274
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:282
|
||||
msgid "Go to conversation with {0}"
|
||||
msgstr "Vai alla conversazione con {0}"
|
||||
|
||||
@@ -3175,11 +3175,11 @@ msgstr "Vai alla conversazione con {0}"
|
||||
msgid "Go to next"
|
||||
msgstr "Prosegui"
|
||||
|
||||
#: src/components/dms/ConvoMenu.tsx:170
|
||||
#: src/components/dms/ConvoMenu.tsx:230
|
||||
msgid "Go to profile"
|
||||
msgstr "Vai al profilo"
|
||||
|
||||
#: src/components/dms/ConvoMenu.tsx:167
|
||||
#: src/components/dms/ConvoMenu.tsx:227
|
||||
msgid "Go to user's profile"
|
||||
msgstr "Vai al profilo dell'utente"
|
||||
|
||||
@@ -3439,7 +3439,7 @@ msgstr "Se rimuovi questo post, non potrai recuperarlo."
|
||||
msgid "If you want to change your password, we will send you a code to verify that this is your account."
|
||||
msgstr "Se vuoi modificare la password, ti invieremo un codice per verificare che questo è il tuo account."
|
||||
|
||||
#: src/view/com/auth/server-input/index.tsx:168
|
||||
#: src/view/com/auth/server-input/index.tsx:204
|
||||
msgid "If you're a developer, you can host your own server."
|
||||
msgstr "Se sei uno sviluppatore, puoi utilizzare il tuo server."
|
||||
|
||||
@@ -3560,7 +3560,7 @@ msgstr "Inviti, ma personali"
|
||||
msgid "It looks like you may have entered your email address incorrectly. Are you sure it's right?"
|
||||
msgstr "Sembra che tu abbia inserito il tuo indirizzo email in modo errato. Sei sicuro che sia corretto?"
|
||||
|
||||
#: src/screens/Signup/StepInfo/index.tsx:235
|
||||
#: src/screens/Signup/StepInfo/index.tsx:236
|
||||
msgid "It's correct"
|
||||
msgstr "È corretto"
|
||||
|
||||
@@ -3574,7 +3574,7 @@ msgstr "ID processo: {0}"
|
||||
|
||||
#: src/view/com/auth/SplashScreen.web.tsx:178
|
||||
msgid "Jobs"
|
||||
msgstr "Lavora con noi"
|
||||
msgstr "Opportunità di lavoro"
|
||||
|
||||
#: src/screens/StarterPack/StarterPackLandingScreen.tsx:201
|
||||
#: src/screens/StarterPack/StarterPackLandingScreen.tsx:207
|
||||
@@ -3658,7 +3658,7 @@ msgstr "Ulteriori Informazioni"
|
||||
msgid "Learn more about Bluesky"
|
||||
msgstr "Scopri di più su Bluesky"
|
||||
|
||||
#: src/view/com/auth/server-input/index.tsx:178
|
||||
#: src/view/com/auth/server-input/index.tsx:214
|
||||
msgid "Learn more about self hosting your PDS."
|
||||
msgstr "Maggiori informazioni su come ospitare il tuo server."
|
||||
|
||||
@@ -3678,7 +3678,7 @@ msgid "Learn more about what is public on Bluesky."
|
||||
msgstr "Scopri cosa è pubblico su Bluesky."
|
||||
|
||||
#: src/components/moderation/ContentHider.tsx:239
|
||||
#: src/view/com/auth/server-input/index.tsx:180
|
||||
#: src/view/com/auth/server-input/index.tsx:216
|
||||
msgid "Learn more."
|
||||
msgstr "Saperne di più."
|
||||
|
||||
@@ -3686,15 +3686,15 @@ msgstr "Saperne di più."
|
||||
msgid "Leave"
|
||||
msgstr "Abbandona"
|
||||
|
||||
#: src/components/dms/MessagesListBlockedFooter.tsx:66
|
||||
#: src/components/dms/MessagesListBlockedFooter.tsx:73
|
||||
#: src/components/dms/MessagesListBlockedFooter.tsx:80
|
||||
msgid "Leave chat"
|
||||
msgstr "Abbandona la chat"
|
||||
|
||||
#: src/components/dms/ConvoMenu.tsx:141
|
||||
#: src/components/dms/ConvoMenu.tsx:144
|
||||
#: src/components/dms/ConvoMenu.tsx:211
|
||||
#: src/components/dms/ConvoMenu.tsx:214
|
||||
#: src/components/dms/ConvoMenu.tsx:206
|
||||
#: src/components/dms/ConvoMenu.tsx:209
|
||||
#: src/components/dms/ConvoMenu.tsx:269
|
||||
#: src/components/dms/ConvoMenu.tsx:272
|
||||
#: src/components/dms/LeaveConvoPrompt.tsx:40
|
||||
msgid "Leave conversation"
|
||||
msgstr "Abbandona conversazione"
|
||||
@@ -3856,15 +3856,15 @@ msgstr "Liste"
|
||||
msgid "Lists blocking this user:"
|
||||
msgstr "Liste che bloccano questo utente:"
|
||||
|
||||
#: src/view/screens/Search/Explore.tsx:134
|
||||
#: src/view/screens/Search/Explore.tsx:135
|
||||
msgid "Load more"
|
||||
msgstr "Carica di più"
|
||||
|
||||
#: src/view/screens/Search/Explore.tsx:223
|
||||
#: src/view/screens/Search/Explore.tsx:224
|
||||
msgid "Load more suggested feeds"
|
||||
msgstr "Carica più feed consigliati"
|
||||
|
||||
#: src/view/screens/Search/Explore.tsx:221
|
||||
#: src/view/screens/Search/Explore.tsx:222
|
||||
msgid "Load more suggested follows"
|
||||
msgstr "Carica più utenti suggeriti"
|
||||
|
||||
@@ -3956,8 +3956,8 @@ msgstr "Gestisci i feed salvati"
|
||||
msgid "Manage your muted words and tags"
|
||||
msgstr "Gestisci parole e tag silenziati"
|
||||
|
||||
#: src/components/dms/ConvoMenu.tsx:154
|
||||
#: src/components/dms/ConvoMenu.tsx:161
|
||||
#: src/components/dms/ConvoMenu.tsx:218
|
||||
#: src/components/dms/ConvoMenu.tsx:221
|
||||
msgid "Mark as read"
|
||||
msgstr "Segna come letto"
|
||||
|
||||
@@ -3982,7 +3982,7 @@ msgid "Mentions"
|
||||
msgstr "Menzioni"
|
||||
|
||||
#: src/components/Menu/index.tsx:103
|
||||
#: src/view/screens/Search/Search.tsx:856
|
||||
#: src/view/screens/Search/Search.tsx:862
|
||||
msgid "Menu"
|
||||
msgstr "Menu"
|
||||
|
||||
@@ -3991,7 +3991,7 @@ msgid "Message {0}"
|
||||
msgstr "Messaggio {0}"
|
||||
|
||||
#: src/components/dms/MessageMenu.tsx:72
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:174
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:180
|
||||
msgid "Message deleted"
|
||||
msgstr "Messaggio eliminato"
|
||||
|
||||
@@ -4009,8 +4009,8 @@ msgid "Message is too long"
|
||||
msgstr "Il messaggio è troppo lungo"
|
||||
|
||||
#: src/Navigation.tsx:618
|
||||
#: src/screens/Messages/ChatList.tsx:270
|
||||
#: src/screens/Messages/ChatList.tsx:294
|
||||
#: src/screens/Messages/ChatList.tsx:269
|
||||
#: src/screens/Messages/ChatList.tsx:293
|
||||
msgid "Messages"
|
||||
msgstr "Messaggi"
|
||||
|
||||
@@ -4133,8 +4133,8 @@ msgstr "Silenzia account"
|
||||
msgid "Mute accounts"
|
||||
msgstr "Silenzia account"
|
||||
|
||||
#: src/components/dms/ConvoMenu.tsx:175
|
||||
#: src/components/dms/ConvoMenu.tsx:181
|
||||
#: src/components/dms/ConvoMenu.tsx:235
|
||||
#: src/components/dms/ConvoMenu.tsx:241
|
||||
msgid "Mute conversation"
|
||||
msgstr "Silenzia conversazione"
|
||||
|
||||
@@ -4275,8 +4275,8 @@ msgid "New"
|
||||
msgstr "Nuova"
|
||||
|
||||
#: src/components/dms/dialogs/NewChatDialog.tsx:65
|
||||
#: src/screens/Messages/ChatList.tsx:277
|
||||
#: src/screens/Messages/ChatList.tsx:284
|
||||
#: src/screens/Messages/ChatList.tsx:276
|
||||
#: src/screens/Messages/ChatList.tsx:283
|
||||
msgid "New chat"
|
||||
msgstr "Nuova chat"
|
||||
|
||||
@@ -4390,7 +4390,7 @@ msgstr "Nessun feed trovato. Prova a cercarne altri."
|
||||
msgid "No likes yet"
|
||||
msgstr "Ancora nessun mi piace"
|
||||
|
||||
#: src/components/ProfileCard.tsx:342
|
||||
#: src/components/ProfileCard.tsx:345
|
||||
#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:119
|
||||
msgid "No longer following {0}"
|
||||
msgstr "Non segui più {0}"
|
||||
@@ -4399,7 +4399,7 @@ msgstr "Non segui più {0}"
|
||||
msgid "No longer than {0} characters"
|
||||
msgstr "Non più di {0} caratteri"
|
||||
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:118
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:124
|
||||
msgid "No messages yet"
|
||||
msgstr "Ancora nessun messaggio"
|
||||
|
||||
@@ -4599,7 +4599,7 @@ msgstr "su<0><1/><2><3/></2></0>"
|
||||
|
||||
#: src/screens/Settings/Settings.tsx:304
|
||||
msgid "Onboarding reset"
|
||||
msgstr "Reimpostazione dell'onboarding"
|
||||
msgstr "Reimposta account"
|
||||
|
||||
#: src/view/com/composer/Composer.tsx:338
|
||||
msgid "One or more GIFs is missing alt text."
|
||||
@@ -4663,8 +4663,8 @@ msgstr "Apri il creatore di avatar"
|
||||
msgid "Open change handle dialog"
|
||||
msgstr "Apri la finestra di dialogo per la modifica del nome utente"
|
||||
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:282
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:283
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:290
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:291
|
||||
msgid "Open conversation options"
|
||||
msgstr "Apri opzioni conversazione"
|
||||
|
||||
@@ -4827,7 +4827,7 @@ msgstr "Altro..."
|
||||
|
||||
#: src/components/dms/ReportDialog.tsx:339
|
||||
msgid "Our moderation team has received your report."
|
||||
msgstr "Il nostro team di moderazione ha ricevuto la segnalazione."
|
||||
msgstr "Il team di moderazione ha ricevuto la tua segnalazione."
|
||||
|
||||
#: src/screens/Messages/components/ChatDisabled.tsx:28
|
||||
msgid "Our moderators have reviewed reports and decided to disable your access to chats on Bluesky."
|
||||
@@ -5326,11 +5326,11 @@ msgstr "Riattacca citazione"
|
||||
msgid "Reactivate your account"
|
||||
msgstr "Riattiva account"
|
||||
|
||||
#: src/screens/VideoFeed/index.tsx:932
|
||||
#: src/screens/VideoFeed/index.tsx:934
|
||||
msgid "Read less"
|
||||
msgstr "Leggi di meno"
|
||||
|
||||
#: src/screens/VideoFeed/index.tsx:932
|
||||
#: src/screens/VideoFeed/index.tsx:934
|
||||
msgid "Read more"
|
||||
msgstr "Leggi di più"
|
||||
|
||||
@@ -5351,13 +5351,13 @@ msgstr "Leggi i termini di servizio di Bluesky"
|
||||
#: src/screens/Takendown.tsx:162
|
||||
#: src/screens/Takendown.tsx:170
|
||||
msgid "Reason for appeal"
|
||||
msgstr "Motivo della richiesta di revisione della sospensione"
|
||||
msgstr "Motivo del ricorso"
|
||||
|
||||
#: src/components/dms/ReportDialog.tsx:210
|
||||
msgid "Reason:"
|
||||
msgstr "Motivo:"
|
||||
|
||||
#: src/view/screens/Search/Search.tsx:1034
|
||||
#: src/view/screens/Search/Search.tsx:1040
|
||||
msgid "Recent Searches"
|
||||
msgstr "Ricerche recenti"
|
||||
|
||||
@@ -5450,11 +5450,11 @@ msgstr "Rimuovi l'immagine"
|
||||
msgid "Remove mute word from your list"
|
||||
msgstr "Rimuovi parola silenziata dalla tua lista"
|
||||
|
||||
#: src/view/screens/Search/Search.tsx:1082
|
||||
#: src/view/screens/Search/Search.tsx:1088
|
||||
msgid "Remove profile"
|
||||
msgstr "Rimuovi profilo"
|
||||
|
||||
#: src/view/screens/Search/Search.tsx:1084
|
||||
#: src/view/screens/Search/Search.tsx:1090
|
||||
msgid "Remove profile from search history"
|
||||
msgstr "Rimuovi profilo dalla cronologia di ricerca"
|
||||
|
||||
@@ -5465,7 +5465,7 @@ msgstr "Rimuovi citazione"
|
||||
#: src/view/com/util/post-ctrls/RepostButton.tsx:155
|
||||
#: src/view/com/util/post-ctrls/RepostButton.tsx:165
|
||||
msgid "Remove repost"
|
||||
msgstr "Rimuovi ripubblicazione"
|
||||
msgstr "Rimuovi repost"
|
||||
|
||||
#: src/view/com/composer/videos/SubtitleDialog.tsx:260
|
||||
msgid "Remove subtitle file"
|
||||
@@ -5586,8 +5586,8 @@ msgid "Reply was successfully hidden"
|
||||
msgstr "Risposta nascosta con successo"
|
||||
|
||||
#: src/components/dms/MessageMenu.tsx:132
|
||||
#: src/components/dms/MessagesListBlockedFooter.tsx:77
|
||||
#: src/components/dms/MessagesListBlockedFooter.tsx:84
|
||||
#: src/components/dms/MessagesListBlockedFooter.tsx:91
|
||||
msgid "Report"
|
||||
msgstr "Segnala"
|
||||
|
||||
@@ -5596,8 +5596,8 @@ msgstr "Segnala"
|
||||
msgid "Report Account"
|
||||
msgstr "Segnala account"
|
||||
|
||||
#: src/components/dms/ConvoMenu.tsx:200
|
||||
#: src/components/dms/ConvoMenu.tsx:203
|
||||
#: src/components/dms/ConvoMenu.tsx:258
|
||||
#: src/components/dms/ConvoMenu.tsx:261
|
||||
#: src/components/dms/ReportConversationPrompt.tsx:17
|
||||
msgid "Report conversation"
|
||||
msgstr "Segnala conversazione"
|
||||
@@ -5759,7 +5759,7 @@ msgstr "Reimposta il codice"
|
||||
#: src/screens/Settings/Settings.tsx:344
|
||||
#: src/screens/Settings/Settings.tsx:346
|
||||
msgid "Reset onboarding state"
|
||||
msgstr "Reimposta lo stato dell'incorporazione"
|
||||
msgstr "Ripristina lo stato di registrazione"
|
||||
|
||||
#: src/screens/Login/ForgotPasswordForm.tsx:80
|
||||
msgid "Reset password"
|
||||
@@ -5804,12 +5804,12 @@ msgid "Returns to home page"
|
||||
msgstr "Ritorna alla home"
|
||||
|
||||
#: src/screens/Profile/ProfileFeed/index.tsx:90
|
||||
#: src/screens/VideoFeed/index.tsx:1099
|
||||
#: src/screens/VideoFeed/index.tsx:1101
|
||||
#: src/view/screens/NotFound.tsx:60
|
||||
msgid "Returns to previous page"
|
||||
msgstr "Ritorna alla pagina precedente"
|
||||
|
||||
#: src/components/dialogs/BirthDateSettings.tsx:124
|
||||
#: src/components/dialogs/BirthDateSettings.tsx:120
|
||||
#: src/components/dialogs/PostInteractionSettingsDialog.tsx:438
|
||||
#: src/components/dialogs/PostInteractionSettingsDialog.tsx:444
|
||||
#: src/components/StarterPack/QrCodeDialog.tsx:185
|
||||
@@ -5834,7 +5834,7 @@ msgctxt "action"
|
||||
msgid "Save"
|
||||
msgstr "Salva"
|
||||
|
||||
#: src/components/dialogs/BirthDateSettings.tsx:118
|
||||
#: src/components/dialogs/BirthDateSettings.tsx:114
|
||||
msgid "Save birthday"
|
||||
msgstr "Salva il compleanno"
|
||||
|
||||
@@ -5934,7 +5934,7 @@ msgstr "Cerca \"{interestsDisplayName}\"{activeText}"
|
||||
msgid "Search for \"{query}\""
|
||||
msgstr "Cerca \"{query}\""
|
||||
|
||||
#: src/view/screens/Search/Search.tsx:980
|
||||
#: src/view/screens/Search/Search.tsx:986
|
||||
msgid "Search for \"{searchText}\""
|
||||
msgstr "Cerca \"{searchText}\""
|
||||
|
||||
@@ -6145,7 +6145,7 @@ msgstr "Invia tramite messaggi"
|
||||
msgid "Sends email with confirmation code for account deletion"
|
||||
msgstr "Invia un'email con il codice di conferma per l'eliminazione dell'account"
|
||||
|
||||
#: src/view/com/auth/server-input/index.tsx:127
|
||||
#: src/view/com/auth/server-input/index.tsx:163
|
||||
msgid "Server address"
|
||||
msgstr "Indirizzo del server"
|
||||
|
||||
@@ -6420,7 +6420,7 @@ msgstr "Esci"
|
||||
#: src/screens/Settings/Settings.tsx:256
|
||||
#: src/view/shell/desktop/LeftNav.tsx:205
|
||||
msgid "Sign out?"
|
||||
msgstr "Uscire?"
|
||||
msgstr "Vuoi uscire?"
|
||||
|
||||
#: src/components/moderation/ScreenHider.tsx:91
|
||||
#: src/lib/moderation/useGlobalLabelStrings.ts:28
|
||||
@@ -6437,7 +6437,7 @@ msgstr "Accesso effettuato come @{0}"
|
||||
msgid "Signup without a starter pack"
|
||||
msgstr "Iscriviti senza uno starter pack"
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:314
|
||||
#: src/components/FeedInterstitials.tsx:335
|
||||
msgid "Similar accounts"
|
||||
msgstr "Account simili"
|
||||
|
||||
@@ -6459,7 +6459,7 @@ msgstr "Più piccolo"
|
||||
msgid "Software Dev"
|
||||
msgstr "Sviluppo software"
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:449
|
||||
#: src/components/FeedInterstitials.tsx:470
|
||||
msgid "Some other feeds you might like"
|
||||
msgstr "Altri feed che potrebbero piacerti"
|
||||
|
||||
@@ -6467,7 +6467,7 @@ msgstr "Altri feed che potrebbero piacerti"
|
||||
msgid "Some people can reply"
|
||||
msgstr "Risposte limitate"
|
||||
|
||||
#: src/screens/Messages/Conversation.tsx:102
|
||||
#: src/screens/Messages/Conversation.tsx:112
|
||||
msgid "Something went wrong"
|
||||
msgstr "Qualcosa è andato storto"
|
||||
|
||||
@@ -6632,11 +6632,11 @@ msgstr "Iscriviti alla lista"
|
||||
msgid "Success!"
|
||||
msgstr "Successo!"
|
||||
|
||||
#: src/view/screens/Search/Explore.tsx:366
|
||||
#: src/view/screens/Search/Explore.tsx:368
|
||||
msgid "Suggested accounts"
|
||||
msgstr "Account suggeriti"
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:316
|
||||
#: src/components/FeedInterstitials.tsx:337
|
||||
msgid "Suggested for you"
|
||||
msgstr "Suggerito per te"
|
||||
|
||||
@@ -6703,7 +6703,7 @@ msgstr "Tocca per ignorare"
|
||||
msgid "Tap to enter full screen"
|
||||
msgstr "Tocca per entrare in modalità a schermo intero"
|
||||
|
||||
#: src/screens/VideoFeed/index.tsx:931
|
||||
#: src/screens/VideoFeed/index.tsx:933
|
||||
msgid "Tap to expand or collapse post text."
|
||||
msgstr "Tocca per espandere o comprimere il testo del post."
|
||||
|
||||
@@ -6820,7 +6820,7 @@ msgstr "Impossibile trovare starter pack."
|
||||
msgid "That's all, folks!"
|
||||
msgstr "Questo è tutto, gente!"
|
||||
|
||||
#: src/screens/VideoFeed/index.tsx:1071
|
||||
#: src/screens/VideoFeed/index.tsx:1073
|
||||
msgid "That's everything!"
|
||||
msgstr "È tutto!"
|
||||
|
||||
@@ -7065,7 +7065,7 @@ msgstr "Questo contenuto non è disponibile perché uno degli utenti coinvolti h
|
||||
msgid "This content is not viewable without a Bluesky account."
|
||||
msgstr "Questo contenuto non è visualizzabile senza un account Bluesky."
|
||||
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:276
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:284
|
||||
msgid "This conversation is with a deleted or a deactivated account. Press for options."
|
||||
msgstr "L'utente di questa conversazione ha disattivato o eliminato l'account. Premi per le opzioni."
|
||||
|
||||
@@ -7180,7 +7180,7 @@ msgstr "Questo dovrebbe creare un record di dominio in:"
|
||||
msgid "This user doesn't have any followers."
|
||||
msgstr "Questo utente non ha follower."
|
||||
|
||||
#: src/components/dms/MessagesListBlockedFooter.tsx:60
|
||||
#: src/components/dms/MessagesListBlockedFooter.tsx:67
|
||||
msgid "This user has blocked you"
|
||||
msgstr "Questo utente ti ha bloccato"
|
||||
|
||||
@@ -7359,10 +7359,10 @@ msgstr "Impossibile contattare il servizio. Verifica la tua connessione a intern
|
||||
msgid "Unable to delete"
|
||||
msgstr "Impossibile eliminare"
|
||||
|
||||
#: src/components/dms/MessagesListBlockedFooter.tsx:89
|
||||
#: src/components/dms/MessagesListBlockedFooter.tsx:96
|
||||
#: src/components/dms/MessagesListBlockedFooter.tsx:104
|
||||
#: src/components/dms/MessagesListBlockedFooter.tsx:103
|
||||
#: src/components/dms/MessagesListBlockedFooter.tsx:111
|
||||
#: src/components/dms/MessagesListBlockedFooter.tsx:118
|
||||
#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:197
|
||||
#: src/screens/Profile/Header/ProfileHeaderStandard.tsx:283
|
||||
#: src/view/com/profile/ProfileMenu.tsx:341
|
||||
@@ -7375,8 +7375,8 @@ msgctxt "action"
|
||||
msgid "Unblock"
|
||||
msgstr "Sblocca"
|
||||
|
||||
#: src/components/dms/ConvoMenu.tsx:191
|
||||
#: src/components/dms/ConvoMenu.tsx:195
|
||||
#: src/components/dms/ConvoMenu.tsx:250
|
||||
#: src/components/dms/ConvoMenu.tsx:253
|
||||
msgid "Unblock account"
|
||||
msgstr "Sblocca account"
|
||||
|
||||
@@ -7399,7 +7399,7 @@ msgstr "Annulla ripubblicazione"
|
||||
msgid "Undo repost ({0, plural, one {# repost} other {# reposts}})"
|
||||
msgstr "Annulla ripubblicazione ({0, plural, one {# ripubblicazione} other {# ripubblicazioni}})"
|
||||
|
||||
#: src/view/com/profile/FollowButton.tsx:60
|
||||
#: src/view/com/profile/FollowButton.tsx:63
|
||||
msgctxt "action"
|
||||
msgid "Unfollow"
|
||||
msgstr "Smetti di seguire"
|
||||
@@ -7413,7 +7413,7 @@ msgstr "Smetti di seguire {0}"
|
||||
msgid "Unfollow Account"
|
||||
msgstr "Smetti di seguire questo account"
|
||||
|
||||
#: src/screens/VideoFeed/index.tsx:801
|
||||
#: src/screens/VideoFeed/index.tsx:803
|
||||
msgid "Unfollow user"
|
||||
msgstr "Smetti di seguire"
|
||||
|
||||
@@ -7445,7 +7445,7 @@ msgstr "Riattiva {tag}"
|
||||
msgid "Unmute Account"
|
||||
msgstr "Riattiva questo account"
|
||||
|
||||
#: src/components/dms/ConvoMenu.tsx:179
|
||||
#: src/components/dms/ConvoMenu.tsx:239
|
||||
msgid "Unmute conversation"
|
||||
msgstr "Riattiva conversazione"
|
||||
|
||||
@@ -7746,11 +7746,11 @@ msgstr "Video da {0}: {text}"
|
||||
msgid "Video Games"
|
||||
msgstr "Videogiochi"
|
||||
|
||||
#: src/screens/VideoFeed/index.tsx:1029
|
||||
#: src/screens/VideoFeed/index.tsx:1031
|
||||
msgid "Video is paused"
|
||||
msgstr "Video in pausa"
|
||||
|
||||
#: src/screens/VideoFeed/index.tsx:1029
|
||||
#: src/screens/VideoFeed/index.tsx:1031
|
||||
msgid "Video is playing"
|
||||
msgstr "Video in riproduzione"
|
||||
|
||||
@@ -7785,12 +7785,12 @@ msgstr "Vedi l'avatar di {0}"
|
||||
|
||||
#: src/components/ProfileCard.tsx:110
|
||||
#: src/screens/Profile/components/ProfileFeedHeader.tsx:450
|
||||
#: src/screens/VideoFeed/index.tsx:762
|
||||
#: src/screens/VideoFeed/index.tsx:763
|
||||
#: src/view/com/notifications/NotificationFeedItem.tsx:409
|
||||
msgid "View {0}'s profile"
|
||||
msgstr "Vedi il profilo di {0}"
|
||||
|
||||
#: src/components/dms/MessagesListHeader.tsx:167
|
||||
#: src/components/dms/MessagesListHeader.tsx:173
|
||||
msgid "View {displayName}'s profile"
|
||||
msgstr "Vedi il profilo di {displayName}"
|
||||
|
||||
@@ -7905,7 +7905,7 @@ msgstr "Non siamo riusciti a trovare alcun risultato per quell'hashtag."
|
||||
msgid "We couldn't find any results for that topic."
|
||||
msgstr "Nessun risultato per questo argomento."
|
||||
|
||||
#: src/screens/Messages/Conversation.tsx:103
|
||||
#: src/screens/Messages/Conversation.tsx:113
|
||||
msgid "We couldn't load this conversation"
|
||||
msgstr "Non riusciamo a caricare questa conversazione"
|
||||
|
||||
@@ -8037,7 +8037,7 @@ msgstr "Ops! Non è stato possibile caricare i video di tendenza."
|
||||
|
||||
#: src/screens/Takendown.tsx:173
|
||||
msgid "Why are you appealing?"
|
||||
msgstr "Perché richiedi la revisione?"
|
||||
msgstr "Per quale motivo fai ricorso?"
|
||||
|
||||
#: src/components/ReportDialog/SelectReportOptionView.tsx:42
|
||||
msgid "Why should this content be reviewed?"
|
||||
@@ -8192,7 +8192,7 @@ msgstr "Non hai salvato nessun feed."
|
||||
msgid "You have blocked the author or you have been blocked by the author."
|
||||
msgstr "Hai bloccato l'autore o sei stato bloccato dall'autore."
|
||||
|
||||
#: src/components/dms/MessagesListBlockedFooter.tsx:58
|
||||
#: src/components/dms/MessagesListBlockedFooter.tsx:65
|
||||
msgid "You have blocked this user"
|
||||
msgstr "Hai bloccato questo utente"
|
||||
|
||||
@@ -8329,15 +8329,15 @@ msgstr "Riceverai le notifiche per questo thread"
|
||||
msgid "You will receive an email with a \"reset code.\" Enter that code here, then enter your new password."
|
||||
msgstr "Riceverai un'email con un codice di recupero. Inserisci il codice qui, poi inserisci la tua nuova password."
|
||||
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:133
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:139
|
||||
msgid "You: {0}"
|
||||
msgstr "Tu: {0}"
|
||||
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:162
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:168
|
||||
msgid "You: {defaultEmbeddedContentMessage}"
|
||||
msgstr "Tu: {defaultEmbeddedContentMessage}"
|
||||
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:155
|
||||
#: src/screens/Messages/components/ChatListItem.tsx:161
|
||||
msgid "You: {short}"
|
||||
msgstr "Tu: {short}"
|
||||
|
||||
@@ -8399,7 +8399,7 @@ msgstr "Hai raggiunto il limite giornaliero di video caricati (troppi byte)"
|
||||
msgid "You've reached your daily limit for video uploads (too many videos)"
|
||||
msgstr "Hai raggiunto il limite giornaliero di video caricati (troppi video)"
|
||||
|
||||
#: src/screens/VideoFeed/index.tsx:1080
|
||||
#: src/screens/VideoFeed/index.tsx:1082
|
||||
msgid "You've run out of videos to watch. Maybe it's a good time to take a break?"
|
||||
msgstr "Non ci sono altri video da guardare. Forse è il momento giusto per fare una pausa?"
|
||||
|
||||
@@ -8425,11 +8425,11 @@ msgstr "L'archivio del tuo account, che contiene tutti i record di dati pubblici
|
||||
|
||||
#: src/screens/Takendown.tsx:214
|
||||
msgid "Your account was found to be in violation of the <0>Bluesky Social Terms of Service</0>. You have been sent an email outlining the specific violation and suspension period, if applicable. You can appeal this decision if you believe it was made in error."
|
||||
msgstr "Abbiamo riscontrato che il tuo account viola i <0>termini di servizio di Bluesky Social</0>. Ti abbiamo inviato un'email che descrive la violazione e il periodo di sospensione, se applicabile. Se ritieni che questa decisione sia stata presa per errore, puoi richiedere una revisione della sospensione."
|
||||
msgstr "Abbiamo riscontrato che il tuo account viola i <0>termini di servizio di Bluesky</0>. Ti abbiamo inviato un'email che descrive la violazione e il periodo di sospensione, se applicabile. Se ritieni che questa decisione sia stata presa per errore, puoi richiedere una revisione della sospensione."
|
||||
|
||||
#: src/screens/Takendown.tsx:154
|
||||
msgid "Your appeal has been submitted. If your appeal succeeds, you will receive an email."
|
||||
msgstr "La tua richiesta di revisione è stata inviata. Riceverai un'email se la revisione avrà esito positivo."
|
||||
msgstr "La richiesta di revisione è stata inviata. Se il ricorso ha esito positivo, riceverai un'email."
|
||||
|
||||
#: src/screens/Signup/StepInfo/index.tsx:205
|
||||
msgid "Your birth date"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+891
-611
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,7 @@ export const router = new Router({
|
||||
ModerationModlists: '/moderation/modlists',
|
||||
ModerationMutedAccounts: '/moderation/muted-accounts',
|
||||
ModerationBlockedAccounts: '/moderation/blocked-accounts',
|
||||
ModerationInteractionSettings: '/moderation/interaction-settings',
|
||||
// profiles, threads, lists
|
||||
Profile: ['/profile/:name', '/profile/:name/rss'],
|
||||
ProfileFollowers: '/profile/:name/followers',
|
||||
|
||||
@@ -232,7 +232,7 @@ export const LoginForm = ({
|
||||
onSubmitEditing={onPressNext}
|
||||
blurOnSubmit={false} // HACK: https://github.com/facebook/react-native/issues/21911#issuecomment-558343069 Keyboard blur behavior is now handled in onSubmitEditing
|
||||
editable={!isProcessing}
|
||||
accessibilityHint={_(msg`Input your password`)}
|
||||
accessibilityHint={_(msg`Enter your password`)}
|
||||
/>
|
||||
<Button
|
||||
testID="forgotPasswordButton"
|
||||
|
||||
@@ -281,7 +281,7 @@ function ChatListItemReady({
|
||||
!isDeletedAccount
|
||||
? _(msg`Go to conversation with ${profile.handle}`)
|
||||
: _(
|
||||
msg`This conversation is with a deleted or a deactivated account. Press for options.`,
|
||||
msg`This conversation is with a deleted or a deactivated account. Press for options`,
|
||||
)
|
||||
}
|
||||
accessibilityActions={
|
||||
|
||||
@@ -28,6 +28,7 @@ import * as Toggle from '#/components/forms/Toggle'
|
||||
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron'
|
||||
import {CircleBanSign_Stroke2_Corner0_Rounded as CircleBanSign} from '#/components/icons/CircleBanSign'
|
||||
import {Props as SVGIconProps} from '#/components/icons/common'
|
||||
import {EditBig_Stroke2_Corner0_Rounded as EditBig} from '#/components/icons/EditBig'
|
||||
import {Filter_Stroke2_Corner0_Rounded as Filter} from '#/components/icons/Filter'
|
||||
import {Group3_Stroke2_Corner0_Rounded as Group} from '#/components/icons/Group'
|
||||
import {Person_Stroke2_Corner0_Rounded as Person} from '#/components/icons/Person'
|
||||
@@ -199,6 +200,21 @@ export function ModerationScreenInner({
|
||||
a.overflow_hidden,
|
||||
t.atoms.bg_contrast_25,
|
||||
]}>
|
||||
<Link
|
||||
label={_(msg`View your default post interaction settings`)}
|
||||
testID="interactionSettingsBtn"
|
||||
to="/moderation/interaction-settings">
|
||||
{state => (
|
||||
<SubItem
|
||||
title={_(msg`Interaction settings`)}
|
||||
icon={EditBig}
|
||||
style={[
|
||||
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</Link>
|
||||
<Divider />
|
||||
<Button
|
||||
testID="mutedWordsBtn"
|
||||
label={_(msg`Open muted words and tags settings`)}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import deepEqual from 'lodash.isequal'
|
||||
|
||||
import {logger} from '#/logger'
|
||||
import {usePostInteractionSettingsMutation} from '#/state/queries/post-interaction-settings'
|
||||
import {createPostgateRecord} from '#/state/queries/postgate/util'
|
||||
import {
|
||||
usePreferencesQuery,
|
||||
UsePreferencesQueryResponse,
|
||||
} from '#/state/queries/preferences'
|
||||
import {
|
||||
threadgateAllowUISettingToAllowRecordValue,
|
||||
threadgateRecordToAllowUISetting,
|
||||
} from '#/state/queries/threadgate'
|
||||
import * as Toast from '#/view/com/util/Toast'
|
||||
import {atoms as a, useGutters} from '#/alf'
|
||||
import {Admonition} from '#/components/Admonition'
|
||||
import {PostInteractionSettingsForm} from '#/components/dialogs/PostInteractionSettingsDialog'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import {Loader} from '#/components/Loader'
|
||||
|
||||
export function Screen() {
|
||||
const gutters = useGutters(['base'])
|
||||
const {data: preferences} = usePreferencesQuery()
|
||||
return (
|
||||
<Layout.Screen testID="ModerationInteractionSettingsScreen">
|
||||
<Layout.Header.Outer>
|
||||
<Layout.Header.BackButton />
|
||||
<Layout.Header.Content>
|
||||
<Layout.Header.TitleText>
|
||||
<Trans>Post Interaction Settings</Trans>
|
||||
</Layout.Header.TitleText>
|
||||
</Layout.Header.Content>
|
||||
<Layout.Header.Slot />
|
||||
</Layout.Header.Outer>
|
||||
<Layout.Content>
|
||||
<View style={[gutters, a.gap_xl]}>
|
||||
<Admonition type="tip">
|
||||
<Trans>
|
||||
The following settings will be used as your defaults when creating
|
||||
new posts. You can edit these for a specific post from the
|
||||
composer.
|
||||
</Trans>
|
||||
</Admonition>
|
||||
{preferences ? (
|
||||
<Inner preferences={preferences} />
|
||||
) : (
|
||||
<View style={[gutters, a.justify_center, a.align_center]}>
|
||||
<Loader size="xl" />
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</Layout.Content>
|
||||
</Layout.Screen>
|
||||
)
|
||||
}
|
||||
|
||||
function Inner({preferences}: {preferences: UsePreferencesQueryResponse}) {
|
||||
const {_} = useLingui()
|
||||
const {mutateAsync: setPostInteractionSettings, isPending} =
|
||||
usePostInteractionSettingsMutation()
|
||||
const [error, setError] = React.useState<string | undefined>(undefined)
|
||||
|
||||
const allowUI = React.useMemo(() => {
|
||||
return threadgateRecordToAllowUISetting({
|
||||
$type: 'app.bsky.feed.threadgate',
|
||||
post: '',
|
||||
createdAt: new Date().toString(),
|
||||
allow: preferences.postInteractionSettings.threadgateAllowRules,
|
||||
})
|
||||
}, [preferences.postInteractionSettings.threadgateAllowRules])
|
||||
const postgate = React.useMemo(() => {
|
||||
return createPostgateRecord({
|
||||
post: '',
|
||||
embeddingRules:
|
||||
preferences.postInteractionSettings.postgateEmbeddingRules,
|
||||
})
|
||||
}, [preferences.postInteractionSettings.postgateEmbeddingRules])
|
||||
|
||||
const [maybeEditedAllowUI, setAllowUI] = React.useState(allowUI)
|
||||
const [maybeEditedPostgate, setEditedPostgate] = React.useState(postgate)
|
||||
|
||||
const wasEdited = React.useMemo(() => {
|
||||
return (
|
||||
!deepEqual(allowUI, maybeEditedAllowUI) ||
|
||||
!deepEqual(postgate.embeddingRules, maybeEditedPostgate.embeddingRules)
|
||||
)
|
||||
}, [postgate, allowUI, maybeEditedAllowUI, maybeEditedPostgate])
|
||||
|
||||
const onSave = React.useCallback(async () => {
|
||||
setError('')
|
||||
|
||||
try {
|
||||
await setPostInteractionSettings({
|
||||
threadgateAllowRules:
|
||||
threadgateAllowUISettingToAllowRecordValue(maybeEditedAllowUI),
|
||||
postgateEmbeddingRules: maybeEditedPostgate.embeddingRules ?? [],
|
||||
})
|
||||
Toast.show(_(msg`Settings saved`))
|
||||
} catch (e: any) {
|
||||
logger.error(`Failed to save post interaction settings`, {
|
||||
context: 'ModerationInteractionSettingsScreen',
|
||||
safeMessage: e.message,
|
||||
})
|
||||
setError(_(msg`Failed to save settings. Please try again.`))
|
||||
}
|
||||
}, [_, maybeEditedPostgate, maybeEditedAllowUI, setPostInteractionSettings])
|
||||
|
||||
return (
|
||||
<>
|
||||
<PostInteractionSettingsForm
|
||||
canSave={wasEdited}
|
||||
isSaving={isPending}
|
||||
onSave={onSave}
|
||||
postgate={maybeEditedPostgate}
|
||||
onChangePostgate={setEditedPostgate}
|
||||
threadgateAllowUISettings={maybeEditedAllowUI}
|
||||
onChangeThreadgateAllowUISettings={setAllowUI}
|
||||
/>
|
||||
|
||||
{error && <Admonition type="error">{error}</Admonition>}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -53,9 +53,7 @@ export function Layout({children}: React.PropsWithChildren<{}>) {
|
||||
aria-role="dialog"
|
||||
aria-label={dialogLabel}
|
||||
accessibilityLabel={dialogLabel}
|
||||
accessibilityHint={_(
|
||||
msg`The following steps will help customize your Bluesky experience.`,
|
||||
)}
|
||||
accessibilityHint={_(msg`Customizes your Bluesky experience`)}
|
||||
style={[
|
||||
// @ts-ignore web only -prf
|
||||
isWeb ? a.fixed : a.absolute,
|
||||
|
||||
@@ -205,7 +205,7 @@ export function StepFinished() {
|
||||
setActiveStarterPack(undefined)
|
||||
setHasCheckedForStarterPack(true)
|
||||
startProgressGuide(
|
||||
gate('new_postonboarding') ? 'follow-10' : 'like-10-and-follow-7',
|
||||
gate('old_postonboarding') ? 'like-10-and-follow-7' : 'follow-10',
|
||||
)
|
||||
dispatch({type: 'finish'})
|
||||
onboardDispatch({type: 'finish'})
|
||||
|
||||
@@ -3,7 +3,6 @@ import {Apple_Stroke2_Corner0_Rounded as Apple} from '#/components/icons/Apple'
|
||||
import {At_Stroke2_Corner0_Rounded as At} from '#/components/icons/At'
|
||||
import {Atom_Stroke2_Corner0_Rounded as Atom} from '#/components/icons/Atom'
|
||||
import {Celebrate_Stroke2_Corner0_Rounded as Celebrate} from '#/components/icons/Celebrate'
|
||||
import {Coffee_Stroke2_Corner0_Rounded as Coffee} from '#/components/icons/Coffee'
|
||||
import {
|
||||
EmojiArc_Stroke2_Corner0_Rounded as EmojiArc,
|
||||
EmojiHeartEyes_Stroke2_Corner0_Rounded as EmojiHeartEyes,
|
||||
@@ -13,8 +12,6 @@ import {GameController_Stroke2_Corner0_Rounded as GameController} from '#/compon
|
||||
import {Lab_Stroke2_Corner0_Rounded as Lab} from '#/components/icons/Lab'
|
||||
import {Leaf_Stroke2_Corner0_Rounded as Leaf} from '#/components/icons/Leaf'
|
||||
import {MusicNote_Stroke2_Corner0_Rounded as MusicNote} from '#/components/icons/MusicNote'
|
||||
import {PiggyBank_Stroke2_Corner0_Rounded as PiggyBank} from '#/components/icons/PiggyBank'
|
||||
import {Poop_Stroke2_Corner0_Rounded as Poop} from '#/components/icons/Poop'
|
||||
import {Rose_Stroke2_Corner0_Rounded as Rose} from '#/components/icons/Rose'
|
||||
import {Shaka_Stroke2_Corner0_Rounded as Shaka} from '#/components/icons/Shaka'
|
||||
import {UFO_Stroke2_Corner0_Rounded as UFO} from '#/components/icons/UFO'
|
||||
@@ -33,7 +30,6 @@ export const emojiNames = [
|
||||
'apple',
|
||||
'atom',
|
||||
'celebrate',
|
||||
'coffee',
|
||||
'gameController',
|
||||
'leaf',
|
||||
'musicNote',
|
||||
@@ -43,8 +39,6 @@ export const emojiNames = [
|
||||
'zap',
|
||||
'explosion',
|
||||
'lab',
|
||||
'piggyBank',
|
||||
'poop',
|
||||
] as const
|
||||
export type EmojiName = (typeof emojiNames)[number]
|
||||
|
||||
@@ -81,10 +75,6 @@ export const emojiItems: Record<EmojiName, Emoji> = {
|
||||
name: 'celebrate',
|
||||
component: Celebrate,
|
||||
},
|
||||
coffee: {
|
||||
name: 'coffee',
|
||||
component: Coffee,
|
||||
},
|
||||
gameController: {
|
||||
name: 'gameController',
|
||||
component: GameController,
|
||||
@@ -121,14 +111,6 @@ export const emojiItems: Record<EmojiName, Emoji> = {
|
||||
name: 'lab',
|
||||
component: Lab,
|
||||
},
|
||||
piggyBank: {
|
||||
name: 'piggyBank',
|
||||
component: PiggyBank,
|
||||
},
|
||||
poop: {
|
||||
name: 'poop',
|
||||
component: Poop,
|
||||
},
|
||||
}
|
||||
|
||||
export const avatarColors = [
|
||||
|
||||
@@ -60,7 +60,7 @@ export function ErrorState({error}: {error: string}) {
|
||||
color="secondary"
|
||||
variant="solid"
|
||||
label={_(msg`Go Back`)}
|
||||
accessibilityHint="Return to previous page"
|
||||
accessibilityHint="Returns to previous page"
|
||||
onPress={onPressBack}>
|
||||
<ButtonText>
|
||||
<Trans>Go Back</Trans>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {View} from 'react-native'
|
||||
import {AppBskyUnspeccedDefs} from '@atproto/api'
|
||||
import {Trans} from '@lingui/macro'
|
||||
|
||||
import {logEvent} from '#/lib/statsig/statsig'
|
||||
@@ -27,6 +28,7 @@ function Inner() {
|
||||
const gutters = useGutters([0, 'compact'])
|
||||
const {data: trending, error, isLoading} = useTrendingTopics()
|
||||
const noRecs = !isLoading && !error && !trending?.suggested?.length
|
||||
const allFeeds = trending?.suggested && isAllFeeds(trending.suggested)
|
||||
|
||||
return error || noRecs ? null : (
|
||||
<>
|
||||
@@ -50,9 +52,17 @@ function Inner() {
|
||||
<Trans>Recommended</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
<Text style={[t.atoms.text_contrast_high, a.leading_snug]}>
|
||||
<Trans>Feeds we think you might like.</Trans>
|
||||
</Text>
|
||||
{!allFeeds ? (
|
||||
<Text style={[t.atoms.text_contrast_high, a.leading_snug]}>
|
||||
<Trans>
|
||||
Content from across the network we think you might like.
|
||||
</Trans>
|
||||
</Text>
|
||||
) : (
|
||||
<Text style={[t.atoms.text_contrast_high, a.leading_snug]}>
|
||||
<Trans>Feeds we think you might like.</Trans>
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -98,3 +108,10 @@ function Inner() {
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function isAllFeeds(topics: AppBskyUnspeccedDefs.TrendingTopic[]) {
|
||||
return topics.every(topic => {
|
||||
const segments = topic.link.split('/').slice(1)
|
||||
return segments[0] === 'profile' && segments[2] === 'feed'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||
import {appVersion, BUNDLE_DATE, bundleInfo} from '#/lib/app-info'
|
||||
import {STATUS_PAGE_URL} from '#/lib/constants'
|
||||
import {CommonNavigatorParams} from '#/lib/routes/types'
|
||||
import {useDevModeEnabled} from '#/state/preferences/dev-mode'
|
||||
import * as Toast from '#/view/com/util/Toast'
|
||||
import * as SettingsList from '#/screens/Settings/components/SettingsList'
|
||||
import {CodeLines_Stroke2_Corner2_Rounded as CodeLinesIcon} from '#/components/icons/CodeLines'
|
||||
@@ -18,6 +19,7 @@ import * as Layout from '#/components/Layout'
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'AboutSettings'>
|
||||
export function AboutSettingsScreen({}: Props) {
|
||||
const {_} = useLingui()
|
||||
const [devModeEnabled, setDevModeEnabled] = useDevModeEnabled()
|
||||
|
||||
return (
|
||||
<Layout.Screen>
|
||||
@@ -65,7 +67,16 @@ export function AboutSettingsScreen({}: Props) {
|
||||
</SettingsList.LinkItem>
|
||||
<SettingsList.PressableItem
|
||||
label={_(msg`Version ${appVersion}`)}
|
||||
accessibilityHint={_(msg`Copy build version to clipboard`)}
|
||||
accessibilityHint={_(msg`Copies build version to clipboard`)}
|
||||
onLongPress={() => {
|
||||
const newDevModeEnabled = !devModeEnabled
|
||||
setDevModeEnabled(newDevModeEnabled)
|
||||
Toast.show(
|
||||
newDevModeEnabled
|
||||
? _(msg`Developer mode enabled`)
|
||||
: _(msg`Developer mode disabled`),
|
||||
)
|
||||
}}
|
||||
onPress={() => {
|
||||
setStringAsync(
|
||||
`Build version: ${appVersion}; Bundle info: ${bundleInfo}; Bundle date: ${BUNDLE_DATE}; Platform: ${Platform.OS}; Platform version: ${Platform.Version}`,
|
||||
|
||||
@@ -124,7 +124,7 @@ export function AccountSettingsScreen({}: Props) {
|
||||
</SettingsList.PressableItem>
|
||||
<SettingsList.PressableItem
|
||||
label={_(msg`Handle`)}
|
||||
accessibilityHint={_(msg`Open change handle dialog`)}
|
||||
accessibilityHint={_(msg`Opens change handle dialog`)}
|
||||
onPress={() => changeHandleControl.open()}>
|
||||
<SettingsList.ItemIcon icon={AtIcon} />
|
||||
<SettingsList.ItemText>
|
||||
|
||||
@@ -213,7 +213,7 @@ function AppIcon({icon, size = 50}: {icon: AppIconSet; size: number}) {
|
||||
return (
|
||||
<PressableScale
|
||||
accessibilityLabel={icon.name}
|
||||
accessibilityHint={_(msg`Tap to change app icon`)}
|
||||
accessibilityHint={_(msg`Changes app icon`)}
|
||||
targetScale={0.95}
|
||||
onPress={() => {
|
||||
if (isAndroid) {
|
||||
|
||||
@@ -101,7 +101,7 @@ export function SettingsScreen({}: Props) {
|
||||
<SettingsList.PressableItem
|
||||
label={_(msg`Switch account`)}
|
||||
accessibilityHint={_(
|
||||
msg`Show other accounts you can switch to`,
|
||||
msg`Shows other accounts you can switch to`,
|
||||
)}
|
||||
onPress={() => {
|
||||
if (!reducedMotion) {
|
||||
@@ -205,7 +205,7 @@ export function SettingsScreen({}: Props) {
|
||||
<SettingsList.PressableItem
|
||||
onPress={() => Linking.openURL(HELP_DESK_URL)}
|
||||
label={_(msg`Help`)}
|
||||
accessibilityHint={_(msg`Open helpdesk in browser`)}>
|
||||
accessibilityHint={_(msg`Opens helpdesk in browser`)}>
|
||||
<SettingsList.ItemIcon icon={CircleQuestionIcon} />
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Help</Trans>
|
||||
@@ -421,7 +421,7 @@ function AccountRow({
|
||||
<View style={[{width: 28}]} />
|
||||
)}
|
||||
<SettingsList.ItemText>
|
||||
<Trans>{sanitizeHandle(account.handle, '@')}</Trans>
|
||||
{sanitizeHandle(account.handle, '@')}
|
||||
</SettingsList.ItemText>
|
||||
{pendingDid === account.did && <SettingsList.ItemIcon icon={Loader} />}
|
||||
</SettingsList.PressableItem>
|
||||
|
||||
@@ -17,8 +17,8 @@ import {useMutation, useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {HITSLOP_10} from '#/lib/constants'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {createFullHandle, validateServiceHandle} from '#/lib/strings/handles'
|
||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||
import {createFullHandle, validateHandle} from '#/lib/strings/handles'
|
||||
import {useFetchDid, useUpdateHandleMutation} from '#/state/queries/handle'
|
||||
import {RQKEY as RQKEY_PROFILE} from '#/state/queries/profile'
|
||||
import {useServiceQuery} from '#/state/queries/service'
|
||||
@@ -172,7 +172,7 @@ function ProvidedHandlePage({
|
||||
const host = serviceInfo.availableUserDomains[0]
|
||||
|
||||
const validation = useMemo(
|
||||
() => validateHandle(subdomain, host, true),
|
||||
() => validateServiceHandle(subdomain, host),
|
||||
[subdomain, host],
|
||||
)
|
||||
|
||||
@@ -537,7 +537,7 @@ function OwnHandlePage({goToServiceHandle}: {goToServiceHandle: () => void}) {
|
||||
|
||||
<Button
|
||||
label={_(msg`Use default provider`)}
|
||||
accessibilityHint={_(msg`Go back to previous page`)}
|
||||
accessibilityHint={_(msg`Returns to previous page`)}
|
||||
onPress={goToServiceHandle}
|
||||
variant="outline"
|
||||
color="secondary"
|
||||
|
||||
@@ -7,7 +7,7 @@ import {logEvent} from '#/lib/statsig/statsig'
|
||||
import {
|
||||
createFullHandle,
|
||||
maxServiceHandleLength,
|
||||
validateHandle,
|
||||
validateServiceHandle,
|
||||
} from '#/lib/strings/handles'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {ScreenTransition} from '#/screens/Login/ScreenTransition'
|
||||
@@ -37,7 +37,7 @@ export function StepHandle() {
|
||||
value: handle,
|
||||
})
|
||||
|
||||
const newValidCheck = validateHandle(handle, state.userDomain)
|
||||
const newValidCheck = validateServiceHandle(handle, state.userDomain)
|
||||
if (!newValidCheck.overall) {
|
||||
return
|
||||
}
|
||||
@@ -97,7 +97,7 @@ export function StepHandle() {
|
||||
})
|
||||
}, [dispatch, state.activeStep])
|
||||
|
||||
const validCheck = validateHandle(draftValue, state.userDomain, true)
|
||||
const validCheck = validateServiceHandle(draftValue, state.userDomain)
|
||||
return (
|
||||
<ScreenTransition>
|
||||
<View style={[a.gap_lg]}>
|
||||
|
||||
@@ -285,7 +285,7 @@ function WizardInner({
|
||||
<Layout.Header.Outer>
|
||||
<Layout.Header.BackButton
|
||||
label={_(msg`Back`)}
|
||||
accessibilityHint={_(msg`Go back to the previous step`)}
|
||||
accessibilityHint={_(msg`Returns to the previous step`)}
|
||||
onPress={evt => {
|
||||
if (state.currentStep !== 'Details') {
|
||||
evt.preventDefault()
|
||||
|
||||
@@ -806,7 +806,9 @@ function Overlay({
|
||||
: _(msg`Follow ${handle}`)
|
||||
}
|
||||
accessibilityHint={
|
||||
profile.viewer?.following ? _(msg`Unfollow user`) : ''
|
||||
profile.viewer?.following
|
||||
? _(msg`Unfollows the user`)
|
||||
: ''
|
||||
}
|
||||
size="small"
|
||||
variant="solid"
|
||||
@@ -936,7 +938,7 @@ function ExpandableRichTextView({
|
||||
/>
|
||||
{constrained && !screenReaderEnabled && (
|
||||
<Pressable
|
||||
accessibilityHint={_(msg`Tap to expand or collapse post text.`)}
|
||||
accessibilityHint={_(msg`Expands or collapses post text`)}
|
||||
accessibilityLabel={expanded ? _(msg`Read less`) : _(msg`Read more`)}
|
||||
hitSlop={HITSLOP_20}
|
||||
onPress={() => setExpanded(prev => !prev)}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import {device, useStorage} from '#/storage'
|
||||
|
||||
export function useDevModeEnabled() {
|
||||
const [devModeEnabled = false, setDevModeEnabled] = useStorage(device, [
|
||||
'devMode',
|
||||
])
|
||||
|
||||
return [devModeEnabled, setDevModeEnabled] as const
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import {AppBskyActorDefs} from '@atproto/api'
|
||||
import {useMutation, useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {preferencesQueryKey} from '#/state/queries/preferences'
|
||||
import {useAgent} from '#/state/session'
|
||||
|
||||
export function usePostInteractionSettingsMutation() {
|
||||
const qc = useQueryClient()
|
||||
const agent = useAgent()
|
||||
return useMutation({
|
||||
async mutationFn(props: AppBskyActorDefs.PostInteractionSettingsPref) {
|
||||
await agent.setPostInteractionSettings(props)
|
||||
},
|
||||
async onSuccess() {
|
||||
await qc.invalidateQueries({
|
||||
queryKey: preferencesQueryKey,
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -39,4 +39,8 @@ export const DEFAULT_LOGGED_OUT_PREFERENCES: UsePreferencesQueryResponse = {
|
||||
activeProgressGuide: undefined,
|
||||
nuxs: [],
|
||||
},
|
||||
postInteractionSettings: {
|
||||
threadgateAllowRules: undefined,
|
||||
postgateEmbeddingRules: [],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Un$Typed,
|
||||
} from '@atproto/api'
|
||||
import {
|
||||
keepPreviousData,
|
||||
QueryClient,
|
||||
useMutation,
|
||||
useQuery,
|
||||
@@ -84,7 +85,13 @@ export function useProfileQuery({
|
||||
})
|
||||
}
|
||||
|
||||
export function useProfilesQuery({handles}: {handles: string[]}) {
|
||||
export function useProfilesQuery({
|
||||
handles,
|
||||
maintainData,
|
||||
}: {
|
||||
handles: string[]
|
||||
maintainData?: boolean
|
||||
}) {
|
||||
const agent = useAgent()
|
||||
return useQuery({
|
||||
staleTime: STALE.MINUTES.FIVE,
|
||||
@@ -93,6 +100,7 @@ export function useProfilesQuery({handles}: {handles: string[]}) {
|
||||
const res = await agent.getProfiles({actors: handles})
|
||||
return res.data
|
||||
},
|
||||
placeholderData: maintainData ? keepPreviousData : undefined,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -4,3 +4,4 @@ export type ThreadgateAllowUISetting =
|
||||
| {type: 'mention'}
|
||||
| {type: 'following'}
|
||||
| {type: 'list'; list: string}
|
||||
| {type: 'followers'}
|
||||
|
||||
@@ -46,6 +46,8 @@ export function threadgateRecordToAllowUISetting(
|
||||
setting = {type: 'following'}
|
||||
} else if (AppBskyFeedThreadgate.isListRule(allow)) {
|
||||
setting = {type: 'list', list: allow.list}
|
||||
} else if (AppBskyFeedThreadgate.isFollowerRule(allow)) {
|
||||
setting = {type: 'followers'}
|
||||
}
|
||||
return setting
|
||||
})
|
||||
@@ -76,6 +78,8 @@ export function threadgateAllowUISettingToAllowRecordValue(
|
||||
allow.push({$type: 'app.bsky.feed.threadgate#mentionRule'})
|
||||
} else if (rule.type === 'following') {
|
||||
allow.push({$type: 'app.bsky.feed.threadgate#followingRule'})
|
||||
} else if (rule.type === 'followers') {
|
||||
allow.push({$type: 'app.bsky.feed.threadgate#followerRule'})
|
||||
} else if (rule.type === 'list') {
|
||||
allow.push({
|
||||
$type: 'app.bsky.feed.threadgate#listRule',
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react'
|
||||
|
||||
import {useGate} from '#/lib/statsig/statsig'
|
||||
import {useLanguagePrefs} from '#/state/preferences/languages'
|
||||
import {useServiceConfigQuery} from '#/state/queries/service-config'
|
||||
import {device} from '#/storage'
|
||||
@@ -14,7 +13,6 @@ const Context = React.createContext<Context>({
|
||||
})
|
||||
|
||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
const gate = useGate()
|
||||
const langPrefs = useLanguagePrefs()
|
||||
const {data: config, isLoading: isInitialLoad} = useServiceConfigQuery()
|
||||
const ctx = React.useMemo<Context>(() => {
|
||||
@@ -45,23 +43,12 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
* the server, we can exit early.
|
||||
*/
|
||||
const enabled = Boolean(config?.topicsEnabled)
|
||||
if (!enabled) {
|
||||
// cache for next reload
|
||||
device.set(['trendingBetaEnabled'], enabled)
|
||||
return {enabled: false}
|
||||
}
|
||||
|
||||
/*
|
||||
* Service is enabled, but also check statsig in case we're rolling back.
|
||||
*/
|
||||
const gateEnabled = gate('trending_topics_beta')
|
||||
const _enabled = enabled && gateEnabled
|
||||
|
||||
// update cache
|
||||
device.set(['trendingBetaEnabled'], _enabled)
|
||||
device.set(['trendingBetaEnabled'], enabled)
|
||||
|
||||
return {enabled: _enabled}
|
||||
}, [isInitialLoad, config, gate, langPrefs.contentLanguages])
|
||||
return {enabled}
|
||||
}, [isInitialLoad, config, langPrefs.contentLanguages])
|
||||
return <Context.Provider value={ctx}>{children}</Context.Provider>
|
||||
}
|
||||
|
||||
|
||||
+69
-3
@@ -1,5 +1,5 @@
|
||||
import {useCallback, useEffect, useState} from 'react'
|
||||
import {MMKV} from 'react-native-mmkv'
|
||||
import {Did} from '@atproto/api'
|
||||
|
||||
import {Account, Device} from '#/storage/schema'
|
||||
|
||||
@@ -65,6 +65,72 @@ export class Storage<Scopes extends unknown[], Schema> {
|
||||
removeMany<Key extends keyof Schema>(scopes: [...Scopes], keys: Key[]) {
|
||||
keys.forEach(key => this.remove([...scopes, key]))
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires a callback when the storage associated with a given key changes
|
||||
*
|
||||
* @returns Listener - call `remove()` to stop listening
|
||||
*/
|
||||
addOnValueChangedListener<Key extends keyof Schema>(
|
||||
scopes: [...Scopes, Key],
|
||||
callback: () => void,
|
||||
) {
|
||||
return this.store.addOnValueChangedListener(key => {
|
||||
if (key === scopes.join(this.sep)) {
|
||||
callback()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type StorageSchema<T extends Storage<any, any>> = T extends Storage<
|
||||
any,
|
||||
infer U
|
||||
>
|
||||
? U
|
||||
: never
|
||||
type StorageScopes<T extends Storage<any, any>> = T extends Storage<
|
||||
infer S,
|
||||
any
|
||||
>
|
||||
? S
|
||||
: never
|
||||
|
||||
/**
|
||||
* Hook to use a storage instance. Acts like a useState hook, but persists the
|
||||
* value in storage.
|
||||
*/
|
||||
export function useStorage<
|
||||
Store extends Storage<any, any>,
|
||||
Key extends keyof StorageSchema<Store>,
|
||||
>(
|
||||
storage: Store,
|
||||
scopes: [...StorageScopes<Store>, Key],
|
||||
): [
|
||||
StorageSchema<Store>[Key] | undefined,
|
||||
(data: StorageSchema<Store>[Key]) => void,
|
||||
] {
|
||||
type Schema = StorageSchema<Store>
|
||||
const [value, setValue] = useState<Schema[Key] | undefined>(() =>
|
||||
storage.get(scopes),
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const sub = storage.addOnValueChangedListener(scopes, () => {
|
||||
setValue(storage.get(scopes))
|
||||
})
|
||||
return () => sub.remove()
|
||||
}, [storage, scopes])
|
||||
|
||||
const setter = useCallback(
|
||||
(data: Schema[Key]) => {
|
||||
setValue(data)
|
||||
storage.set(scopes, data)
|
||||
},
|
||||
[storage, scopes],
|
||||
)
|
||||
|
||||
return [value, setter] as const
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,10 +143,10 @@ export const device = new Storage<[], Device>({id: 'bsky_device'})
|
||||
/**
|
||||
* Account data that's specific to the account on this device
|
||||
*/
|
||||
export const account = new Storage<[Did], Account>({id: 'bsky_account'})
|
||||
export const account = new Storage<[string], Account>({id: 'bsky_account'})
|
||||
|
||||
if (__DEV__ && typeof window !== 'undefined') {
|
||||
// @ts-ignore
|
||||
// @ts-expect-error - dev global
|
||||
window.bsky_storage = {
|
||||
device,
|
||||
account,
|
||||
|
||||
@@ -9,8 +9,10 @@ export type Device = {
|
||||
countryCode: string | undefined
|
||||
}
|
||||
trendingBetaEnabled: boolean
|
||||
devMode: boolean
|
||||
}
|
||||
|
||||
export type Account = {
|
||||
searchTermHistory?: string[]
|
||||
searchAccountHistory?: string[]
|
||||
}
|
||||
|
||||
@@ -187,6 +187,7 @@ input:focus {
|
||||
animation: rotate 500ms linear infinite;
|
||||
}
|
||||
|
||||
/* animations for atoms */
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
@@ -205,6 +206,38 @@ input:focus {
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zoomIn {
|
||||
from {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
to {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideInLeft {
|
||||
from {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideOutLeft {
|
||||
from {
|
||||
transform: translateX(0);
|
||||
}
|
||||
to {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
/* animating radix dropdowns requires knowing the data attributes */
|
||||
.dropdown-menu-transform-origin > * {
|
||||
transform-origin: var(--radix-dropdown-menu-content-transform-origin);
|
||||
}
|
||||
|
||||
.force-no-clicks > *,
|
||||
.force-no-clicks * {
|
||||
pointer-events: none !important;
|
||||
|
||||
@@ -83,6 +83,7 @@ import {
|
||||
useLanguagePrefs,
|
||||
useLanguagePrefsApi,
|
||||
} from '#/state/preferences/languages'
|
||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||
import {useProfileQuery} from '#/state/queries/profile'
|
||||
import {Gif} from '#/state/queries/tenor'
|
||||
import {useAgent, useSession} from '#/state/session'
|
||||
@@ -169,6 +170,7 @@ export const ComposePost = ({
|
||||
const discardPromptControl = Prompt.usePromptControl()
|
||||
const {closeAllDialogs} = useDialogStateControlContext()
|
||||
const {closeAllModals} = useModalControls()
|
||||
const {data: preferences} = usePreferencesQuery()
|
||||
|
||||
const [isKeyboardVisible] = useIsKeyboardVisible({iosUseWillEvents: true})
|
||||
const [isPublishing, setIsPublishing] = useState(false)
|
||||
@@ -177,7 +179,13 @@ export const ComposePost = ({
|
||||
|
||||
const [composerState, composerDispatch] = useReducer(
|
||||
composerReducer,
|
||||
{initImageUris, initQuoteUri: initQuote?.uri, initText, initMention},
|
||||
{
|
||||
initImageUris,
|
||||
initQuoteUri: initQuote?.uri,
|
||||
initText,
|
||||
initMention,
|
||||
initInteractionSettings: preferences?.postInteractionSettings,
|
||||
},
|
||||
createComposerState,
|
||||
)
|
||||
|
||||
@@ -1233,7 +1241,7 @@ function ComposerFooter({
|
||||
onPress={onEmojiButtonPress}
|
||||
style={a.p_sm}
|
||||
label={_(msg`Open emoji picker`)}
|
||||
accessibilityHint={_(msg`Open emoji picker`)}
|
||||
accessibilityHint={_(msg`Opens emoji picker`)}
|
||||
variant="ghost"
|
||||
shape="round"
|
||||
color="primary">
|
||||
|
||||
@@ -70,7 +70,7 @@ export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) {
|
||||
msg`Expand or collapse the full post you are replying to`,
|
||||
)}
|
||||
accessibilityHint={_(
|
||||
msg`Expand or collapse the full post you are replying to`,
|
||||
msg`Expands or collapses the full post you are replying to`,
|
||||
)}>
|
||||
<PreviewableUserAvatar
|
||||
size={50}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import {ImagePickerAsset} from 'expo-image-picker'
|
||||
import {AppBskyFeedPostgate, AppBskyRichtextFacet, RichText} from '@atproto/api'
|
||||
import {
|
||||
AppBskyFeedPostgate,
|
||||
AppBskyRichtextFacet,
|
||||
BskyPreferences,
|
||||
RichText,
|
||||
} from '@atproto/api'
|
||||
import {nanoid} from 'nanoid/non-secure'
|
||||
|
||||
import {SelfLabel} from '#/lib/moderation'
|
||||
@@ -13,7 +18,7 @@ import {
|
||||
import {ComposerImage, createInitialImages} from '#/state/gallery'
|
||||
import {createPostgateRecord} from '#/state/queries/postgate/util'
|
||||
import {Gif} from '#/state/queries/tenor'
|
||||
import {threadgateViewToAllowUISetting} from '#/state/queries/threadgate'
|
||||
import {threadgateRecordToAllowUISetting} from '#/state/queries/threadgate'
|
||||
import {ThreadgateAllowUISetting} from '#/state/queries/threadgate'
|
||||
import {ComposerOpts} from '#/state/shell/composer'
|
||||
import {
|
||||
@@ -477,11 +482,15 @@ export function createComposerState({
|
||||
initMention,
|
||||
initImageUris,
|
||||
initQuoteUri,
|
||||
initInteractionSettings,
|
||||
}: {
|
||||
initText: string | undefined
|
||||
initMention: string | undefined
|
||||
initImageUris: ComposerOpts['imageUris']
|
||||
initQuoteUri: string | undefined
|
||||
initInteractionSettings:
|
||||
| BskyPreferences['postInteractionSettings']
|
||||
| undefined
|
||||
}): ComposerState {
|
||||
let media: ImagesMedia | undefined
|
||||
if (initImageUris?.length) {
|
||||
@@ -591,8 +600,16 @@ export function createComposerState({
|
||||
},
|
||||
},
|
||||
],
|
||||
postgate: createPostgateRecord({post: ''}),
|
||||
threadgate: threadgateViewToAllowUISetting(undefined),
|
||||
postgate: createPostgateRecord({
|
||||
post: '',
|
||||
embeddingRules: initInteractionSettings?.postgateEmbeddingRules || [],
|
||||
}),
|
||||
threadgate: threadgateRecordToAllowUISetting({
|
||||
$type: 'app.bsky.feed.threadgate',
|
||||
post: '',
|
||||
createdAt: new Date().toString(),
|
||||
allow: initInteractionSettings?.threadgateAllowRules,
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ export function EmojiPicker({state, close, pinToTop}: IProps) {
|
||||
<Pressable
|
||||
accessible
|
||||
accessibilityLabel={_(msg`Close emoji picker`)}
|
||||
accessibilityHint={_(msg`Tap to close the emoji picker`)}
|
||||
accessibilityHint={_(msg`Closes the emoji picker`)}
|
||||
onPress={close}
|
||||
style={[a.fixed, a.inset_0]}
|
||||
/>
|
||||
@@ -171,7 +171,7 @@ export function EmojiPicker({state, close, pinToTop}: IProps) {
|
||||
<Pressable
|
||||
accessible
|
||||
accessibilityLabel={_(msg`Close emoji picker`)}
|
||||
accessibilityHint={_(msg`Tap to close the emoji picker`)}
|
||||
accessibilityHint={_(msg`Closes the emoji picker`)}
|
||||
onPress={close}
|
||||
style={[a.fixed, a.inset_0]}
|
||||
/>
|
||||
|
||||
@@ -120,6 +120,7 @@ export function ModalsContainer() {
|
||||
}
|
||||
handleIndicatorStyle={{backgroundColor: pal.text.color}}
|
||||
handleStyle={[styles.handle, pal.view]}
|
||||
backgroundStyle={pal.view}
|
||||
onChange={onBottomSheetChange}>
|
||||
{element}
|
||||
</BottomSheet>
|
||||
|
||||
@@ -242,6 +242,11 @@ let PostThreadItemLoaded = ({
|
||||
return makeProfileLink(post.author, 'post', urip.rkey, 'quotes')
|
||||
}, [post.uri, post.author])
|
||||
const quotesTitle = _(msg`Quotes of this post`)
|
||||
const onlyFollowersCanReply = !!threadgateRecord?.allow?.find(
|
||||
rule => rule.$type === 'app.bsky.feed.threadgate#followerRule',
|
||||
)
|
||||
const showFollowButton =
|
||||
currentAccount?.did !== post.author.did && !onlyFollowersCanReply
|
||||
|
||||
const translatorUrl = getTranslatorLink(
|
||||
record?.text || '',
|
||||
@@ -344,7 +349,7 @@ let PostThreadItemLoaded = ({
|
||||
</Text>
|
||||
</Link>
|
||||
</View>
|
||||
{currentAccount?.did !== post.author.did && (
|
||||
{showFollowButton && (
|
||||
<View>
|
||||
<PostThreadFollowBtn did={post.author.did} />
|
||||
</View>
|
||||
@@ -806,7 +811,7 @@ function BackdatedPostIndicator({post}: {post: AppBskyFeedDefs.PostView}) {
|
||||
<Button
|
||||
label={_(msg`Archived post`)}
|
||||
accessibilityHint={_(
|
||||
msg`Show information about when this post was created`,
|
||||
msg`Shows information about when this post was created`,
|
||||
)}
|
||||
onPress={e => {
|
||||
e.preventDefault()
|
||||
|
||||
@@ -6,11 +6,12 @@ import {useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {HITSLOP_20} from '#/lib/constants'
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {shareUrl} from '#/lib/sharing'
|
||||
import {shareText, shareUrl} from '#/lib/sharing'
|
||||
import {toShareUrl} from '#/lib/strings/url-helpers'
|
||||
import {logger} from '#/logger'
|
||||
import {Shadow} from '#/state/cache/types'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {useDevModeEnabled} from '#/state/preferences/dev-mode'
|
||||
import {
|
||||
RQKEY as profileQueryKey,
|
||||
useProfileBlockMutationQueue,
|
||||
@@ -52,6 +53,7 @@ let ProfileMenu = ({
|
||||
const isBlocked = profile.viewer?.blocking || profile.viewer?.blockedBy
|
||||
const isFollowingBlockedAccount = isFollowing && isBlocked
|
||||
const isLabelerAndNotBlocked = !!profile.associated?.labeler && !isBlocked
|
||||
const [devModeEnabled] = useDevModeEnabled()
|
||||
|
||||
const [queueMute, queueUnmute] = useProfileMuteMutationQueue(profile)
|
||||
const [queueBlock, queueUnblock] = useProfileBlockMutationQueue(profile)
|
||||
@@ -167,6 +169,14 @@ let ProfileMenu = ({
|
||||
reportDialogControl.open()
|
||||
}, [reportDialogControl])
|
||||
|
||||
const onPressShareATUri = React.useCallback(() => {
|
||||
shareText(`at://${profile.did}`)
|
||||
}, [profile.did])
|
||||
|
||||
const onPressShareDID = React.useCallback(() => {
|
||||
shareText(profile.did)
|
||||
}, [profile.did])
|
||||
|
||||
return (
|
||||
<EventStopper onKeyDown={false}>
|
||||
<Menu.Root>
|
||||
@@ -308,6 +318,31 @@ let ProfileMenu = ({
|
||||
</Menu.Group>
|
||||
</>
|
||||
)}
|
||||
{devModeEnabled ? (
|
||||
<>
|
||||
<Menu.Divider />
|
||||
<Menu.Group>
|
||||
<Menu.Item
|
||||
testID="profileHeaderDropdownShareATURIBtn"
|
||||
label={_(msg`Copy at:// URI`)}
|
||||
onPress={onPressShareATUri}>
|
||||
<Menu.ItemText>
|
||||
<Trans>Copy at:// URI</Trans>
|
||||
</Menu.ItemText>
|
||||
<Menu.ItemIcon icon={Share} />
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
testID="profileHeaderDropdownShareDIDBtn"
|
||||
label={_(msg`Copy DID`)}
|
||||
onPress={onPressShareDID}>
|
||||
<Menu.ItemText>
|
||||
<Trans>Copy DID</Trans>
|
||||
</Menu.ItemText>
|
||||
<Menu.ItemIcon icon={Share} />
|
||||
</Menu.Item>
|
||||
</Menu.Group>
|
||||
</>
|
||||
) : null}
|
||||
</Menu.Outer>
|
||||
</Menu.Root>
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import {useLingui} from '@lingui/react'
|
||||
|
||||
import {usePalette} from '#/lib/hooks/usePalette'
|
||||
import {useTheme} from '#/lib/ThemeContext'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import {Text} from '../text/Text'
|
||||
|
||||
export function ErrorMessage({
|
||||
@@ -31,39 +32,44 @@ export function ErrorMessage({
|
||||
const pal = usePalette('error')
|
||||
const {_} = useLingui()
|
||||
return (
|
||||
<View testID="errorMessageView" style={[styles.outer, pal.view, style]}>
|
||||
<View
|
||||
style={[styles.errorIcon, {backgroundColor: theme.palette.error.icon}]}>
|
||||
<FontAwesomeIcon
|
||||
icon="exclamation"
|
||||
style={pal.text as FontAwesomeIconStyle}
|
||||
size={16}
|
||||
/>
|
||||
</View>
|
||||
<Text
|
||||
type="sm-medium"
|
||||
style={[styles.message, pal.text]}
|
||||
numberOfLines={numberOfLines}>
|
||||
{message}
|
||||
</Text>
|
||||
{onPressTryAgain && (
|
||||
<TouchableOpacity
|
||||
testID="errorMessageTryAgainButton"
|
||||
style={styles.btn}
|
||||
onPress={onPressTryAgain}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Retry`)}
|
||||
accessibilityHint={_(
|
||||
msg`Retries the last action, which errored out`,
|
||||
)}>
|
||||
<Layout.Center>
|
||||
<View testID="errorMessageView" style={[styles.outer, pal.view, style]}>
|
||||
<View
|
||||
style={[
|
||||
styles.errorIcon,
|
||||
{backgroundColor: theme.palette.error.icon},
|
||||
]}>
|
||||
<FontAwesomeIcon
|
||||
icon="arrows-rotate"
|
||||
style={{color: theme.palette.error.icon}}
|
||||
size={18}
|
||||
icon="exclamation"
|
||||
style={pal.text as FontAwesomeIconStyle}
|
||||
size={16}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
<Text
|
||||
type="sm-medium"
|
||||
style={[styles.message, pal.text]}
|
||||
numberOfLines={numberOfLines}>
|
||||
{message}
|
||||
</Text>
|
||||
{onPressTryAgain && (
|
||||
<TouchableOpacity
|
||||
testID="errorMessageTryAgainButton"
|
||||
style={styles.btn}
|
||||
onPress={onPressTryAgain}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Retry`)}
|
||||
accessibilityHint={_(
|
||||
msg`Retries the last action, which errored out`,
|
||||
)}>
|
||||
<FontAwesomeIcon
|
||||
icon="arrows-rotate"
|
||||
style={{color: theme.palette.error.icon}}
|
||||
size={18}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
</Layout.Center>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -293,7 +293,9 @@ const DropdownItems = ({
|
||||
onPress={() => onPressItem(index)}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={item.label}
|
||||
accessibilityHint={_(msg`Option ${index + 1} of ${numItems}`)}>
|
||||
accessibilityHint={_(
|
||||
msg`Selects option ${index + 1} of ${numItems}`,
|
||||
)}>
|
||||
{item.icon && (
|
||||
<FontAwesomeIcon
|
||||
style={styles.icon}
|
||||
|
||||
@@ -21,7 +21,7 @@ import {useOpenLink} from '#/lib/hooks/useOpenLink'
|
||||
import {getCurrentRoute} from '#/lib/routes/helpers'
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {CommonNavigatorParams, NavigationProp} from '#/lib/routes/types'
|
||||
import {shareUrl} from '#/lib/sharing'
|
||||
import {shareText, shareUrl} from '#/lib/sharing'
|
||||
import {logEvent} from '#/lib/statsig/statsig'
|
||||
import {richTextToString} from '#/lib/strings/rich-text-helpers'
|
||||
import {toShareUrl} from '#/lib/strings/url-helpers'
|
||||
@@ -33,6 +33,7 @@ import {useProfileShadow} from '#/state/cache/profile-shadow'
|
||||
import {useFeedFeedbackContext} from '#/state/feed-feedback'
|
||||
import {useLanguagePrefs} from '#/state/preferences'
|
||||
import {useHiddenPosts, useHiddenPostsApi} from '#/state/preferences'
|
||||
import {useDevModeEnabled} from '#/state/preferences/dev-mode'
|
||||
import {usePinnedPostMutation} from '#/state/queries/pinned-post'
|
||||
import {
|
||||
usePostDeleteMutation,
|
||||
@@ -122,6 +123,7 @@ let PostDropdownMenuItems = ({
|
||||
const hideReplyConfirmControl = useDialogControl()
|
||||
const {mutateAsync: toggleReplyVisibility} =
|
||||
useToggleReplyVisibilityMutation()
|
||||
const [devModeEnabled] = useDevModeEnabled()
|
||||
|
||||
const postUri = post.uri
|
||||
const postCid = post.cid
|
||||
@@ -366,6 +368,14 @@ let PostDropdownMenuItems = ({
|
||||
}
|
||||
}, [_, queueBlock])
|
||||
|
||||
const onShareATURI = useCallback(() => {
|
||||
shareText(postUri)
|
||||
}, [postUri])
|
||||
|
||||
const onShareAuthorDID = useCallback(() => {
|
||||
shareText(postAuthor.did)
|
||||
}, [postAuthor.did])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Menu.Outer>
|
||||
@@ -647,6 +657,28 @@ let PostDropdownMenuItems = ({
|
||||
</>
|
||||
)}
|
||||
</Menu.Group>
|
||||
|
||||
{devModeEnabled ? (
|
||||
<>
|
||||
<Menu.Divider />
|
||||
<Menu.Group>
|
||||
<Menu.Item
|
||||
testID="postAtUriShareBtn"
|
||||
label={_(msg`Copy post at:// URI`)}
|
||||
onPress={onShareATURI}>
|
||||
<Menu.ItemText>{_(msg`Copy post at:// URI`)}</Menu.ItemText>
|
||||
<Menu.ItemIcon icon={Share} position="right" />
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
testID="postAuthorDIDShareBtn"
|
||||
label={_(msg`Copy author DID`)}
|
||||
onPress={onShareAuthorDID}>
|
||||
<Menu.ItemText>{_(msg`Copy author DID`)}</Menu.ItemText>
|
||||
<Menu.ItemIcon icon={Share} position="right" />
|
||||
</Menu.Item>
|
||||
</Menu.Group>
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</Menu.Outer>
|
||||
|
||||
@@ -196,7 +196,7 @@ export function AutoSizedImage({
|
||||
onPressIn={onPressIn}
|
||||
// alt here is what screen readers actually use
|
||||
accessibilityLabel={image.alt}
|
||||
accessibilityHint={_(msg`Tap to view full image`)}
|
||||
accessibilityHint={_(msg`Views full image`)}
|
||||
style={[
|
||||
a.w_full,
|
||||
a.rounded_md,
|
||||
@@ -218,7 +218,7 @@ export function AutoSizedImage({
|
||||
onPressIn={onPressIn}
|
||||
// alt here is what screen readers actually use
|
||||
accessibilityLabel={image.alt}
|
||||
accessibilityHint={_(msg`Tap to view full image`)}
|
||||
accessibilityHint={_(msg`Views full image`)}
|
||||
style={[a.h_full]}>
|
||||
{contents}
|
||||
</Pressable>
|
||||
|
||||
@@ -26,6 +26,7 @@ import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {shareUrl} from '#/lib/sharing'
|
||||
import {useGate} from '#/lib/statsig/statsig'
|
||||
import {toShareUrl} from '#/lib/strings/url-helpers'
|
||||
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
||||
import {Shadow} from '#/state/cache/types'
|
||||
import {useFeedFeedbackContext} from '#/state/feed-feedback'
|
||||
import {
|
||||
@@ -94,6 +95,15 @@ let PostCtrls = ({
|
||||
post.author.viewer?.blockingByList,
|
||||
)
|
||||
|
||||
const shadowedAuthor = useProfileShadow(post.author)
|
||||
const followersCanReply = !!threadgateRecord?.allow?.find(
|
||||
rule => rule.$type === 'app.bsky.feed.threadgate#followerRule',
|
||||
)
|
||||
const canOverrideReplyDisabled =
|
||||
followersCanReply &&
|
||||
shadowedAuthor.viewer?.following?.startsWith('at://did')
|
||||
const replyDisabled = post.viewer?.replyDisabled && !canOverrideReplyDisabled
|
||||
|
||||
const shouldShowLoggedOutWarning = React.useMemo(() => {
|
||||
return (
|
||||
post.author.did !== currentAccount?.did &&
|
||||
@@ -247,13 +257,13 @@ let PostCtrls = ({
|
||||
<View
|
||||
style={[
|
||||
big ? a.align_center : [a.flex_1, a.align_start, {marginLeft: -6}],
|
||||
post.viewer?.replyDisabled ? {opacity: 0.5} : undefined,
|
||||
replyDisabled ? {opacity: 0.5} : undefined,
|
||||
]}>
|
||||
<Pressable
|
||||
testID="replyBtn"
|
||||
style={btnStyle}
|
||||
onPress={() => {
|
||||
if (!post.viewer?.replyDisabled) {
|
||||
if (!replyDisabled) {
|
||||
playHaptic('Light')
|
||||
requireAuth(() => onPressReply())
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ function PlaceholderOverlay({
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Play Video`)}
|
||||
accessibilityHint={_(msg`Play Video`)}
|
||||
accessibilityHint={_(msg`Plays the video`)}
|
||||
onPress={onPress}
|
||||
style={[styles.overlayContainer]}>
|
||||
{!isPlayerActive ? (
|
||||
|
||||
@@ -39,7 +39,7 @@ function PlaybackControls({
|
||||
return (
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityHint={_(msg`Play or pause the GIF`)}
|
||||
accessibilityHint={_(msg`Plays or pauses the GIF`)}
|
||||
accessibilityLabel={isPlaying ? _(msg`Pause`) : _(msg`Play`)}
|
||||
style={[
|
||||
a.absolute,
|
||||
|
||||
@@ -132,13 +132,13 @@ function VideoControls({
|
||||
onPress={enterFullscreen}
|
||||
style={a.flex_1}
|
||||
accessibilityLabel={_(msg`Video`)}
|
||||
accessibilityHint={_(msg`Tap to enter full screen`)}
|
||||
accessibilityHint={_(msg`Enters full screen`)}
|
||||
accessibilityRole="button"
|
||||
/>
|
||||
<ControlButton
|
||||
onPress={togglePlayback}
|
||||
label={isPlaying ? _(msg`Pause`) : _(msg`Play`)}
|
||||
accessibilityHint={_(msg`Tap to play or pause`)}
|
||||
accessibilityHint={_(msg`Plays or pauses the video`)}
|
||||
style={{left: 6}}>
|
||||
{isPlaying ? (
|
||||
<PauseIcon width={13} fill={t.palette.white} />
|
||||
@@ -155,7 +155,7 @@ function VideoControls({
|
||||
? _(msg({message: `Unmute`, context: 'video'}))
|
||||
: _(msg({message: `Mute`, context: 'video'}))
|
||||
}
|
||||
accessibilityHint={_(msg`Tap to toggle sound`)}
|
||||
accessibilityHint={_(msg`Toggles the sound`)}
|
||||
style={{right: 6}}>
|
||||
{muted ? (
|
||||
<MuteIcon width={13} fill={t.palette.white} />
|
||||
|
||||
@@ -1007,7 +1007,7 @@ function ErrorScreen({error}: {error: string}) {
|
||||
<Button
|
||||
type="default"
|
||||
accessibilityLabel={_(msg`Go back`)}
|
||||
accessibilityHint={_(msg`Return to previous page`)}
|
||||
accessibilityHint={_(msg`Returns to previous page`)}
|
||||
onPress={onPressBack}
|
||||
style={{flexShrink: 1}}>
|
||||
<Text type="button" style={pal.text}>
|
||||
|
||||
+237
-285
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React, {useCallback, useLayoutEffect, useMemo} from 'react'
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Image,
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
View,
|
||||
} from 'react-native'
|
||||
import {ScrollView as RNGHScrollView} from 'react-native-gesture-handler'
|
||||
import RNPickerSelect from 'react-native-picker-select'
|
||||
import {AppBskyActorDefs, AppBskyFeedDefs, moderateProfile} from '@atproto/api'
|
||||
import {
|
||||
FontAwesomeIcon,
|
||||
@@ -18,11 +17,10 @@ import {
|
||||
} from '@fortawesome/react-native-fontawesome'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage'
|
||||
import {useFocusEffect, useNavigation} from '@react-navigation/native'
|
||||
|
||||
import {APP_LANGUAGES, LANGUAGES} from '#/lib/../locale/languages'
|
||||
import {createHitslop} from '#/lib/constants'
|
||||
import {createHitslop, HITSLOP_20} from '#/lib/constants'
|
||||
import {HITSLOP_10} from '#/lib/constants'
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
import {usePalette} from '#/lib/hooks/usePalette'
|
||||
@@ -37,7 +35,6 @@ import {
|
||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||
import {augmentSearchQuery} from '#/lib/strings/helpers'
|
||||
import {languageName} from '#/locale/helpers'
|
||||
import {logger} from '#/logger'
|
||||
import {isNative, isWeb} from '#/platform/detection'
|
||||
import {listenSoftReset} from '#/state/events'
|
||||
import {useLanguagePrefs} from '#/state/preferences/languages'
|
||||
@@ -45,9 +42,9 @@ import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete'
|
||||
import {useActorSearch} from '#/state/queries/actor-search'
|
||||
import {usePopularFeedsSearch} from '#/state/queries/feed'
|
||||
import {useProfilesQuery} from '#/state/queries/profile'
|
||||
import {useSearchPostsQuery} from '#/state/queries/search-posts'
|
||||
import {useSession} from '#/state/session'
|
||||
import {useSetDrawerOpen} from '#/state/shell'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {Pager} from '#/view/com/pager/Pager'
|
||||
import {TabBar} from '#/view/com/pager/TabBar'
|
||||
@@ -61,17 +58,24 @@ import {SearchLinkCard, SearchProfileCard} from '#/view/shell/desktop/Search'
|
||||
import {makeSearchQuery, parseSearchQuery} from '#/screens/Search/utils'
|
||||
import {
|
||||
atoms as a,
|
||||
native,
|
||||
platform,
|
||||
tokens,
|
||||
useBreakpoints,
|
||||
useTheme as useThemeNew,
|
||||
useTheme,
|
||||
web,
|
||||
} from '#/alf'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import * as FeedCard from '#/components/FeedCard'
|
||||
import {SearchInput} from '#/components/forms/SearchInput'
|
||||
import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDown} from '#/components/icons/Chevron'
|
||||
import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
|
||||
import {
|
||||
ChevronBottom_Stroke2_Corner0_Rounded as ChevronDownIcon,
|
||||
ChevronTopBottom_Stroke2_Corner0_Rounded as ChevronUpDownIcon,
|
||||
} from '#/components/icons/Chevron'
|
||||
import {Earth_Stroke2_Corner0_Rounded as EarthIcon} from '#/components/icons/Globe'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import * as Menu from '#/components/Menu'
|
||||
import {account, useStorage} from '#/storage'
|
||||
|
||||
function Loader() {
|
||||
return (
|
||||
@@ -278,7 +282,7 @@ let SearchScreenFeedsResults = ({
|
||||
query: string
|
||||
active: boolean
|
||||
}): React.ReactNode => {
|
||||
const t = useThemeNew()
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
|
||||
const {data: results, isFetched} = usePopularFeedsSearch({
|
||||
@@ -323,103 +327,95 @@ function SearchLanguageDropdown({
|
||||
value: string
|
||||
onChange(value: string): void
|
||||
}) {
|
||||
const t = useThemeNew()
|
||||
const {_} = useLingui()
|
||||
const {appLanguage, contentLanguages} = useLanguagePrefs()
|
||||
|
||||
const items = React.useMemo(() => {
|
||||
return [
|
||||
{
|
||||
label: _(msg`Any language`),
|
||||
inputLabel: _(msg`Any language`),
|
||||
value: '',
|
||||
key: '*',
|
||||
},
|
||||
].concat(
|
||||
LANGUAGES.filter(
|
||||
(lang, index, self) =>
|
||||
Boolean(lang.code2) && // reduce to the code2 varieties
|
||||
index === self.findIndex(t => t.code2 === lang.code2), // remove dupes (which will happen)
|
||||
)
|
||||
.map(l => ({
|
||||
label: languageName(l, appLanguage),
|
||||
inputLabel: languageName(l, appLanguage),
|
||||
value: l.code2,
|
||||
key: l.code2 + l.code3,
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
// prioritize user's languages
|
||||
const aIsUser = contentLanguages.includes(a.value)
|
||||
const bIsUser = contentLanguages.includes(b.value)
|
||||
if (aIsUser && !bIsUser) return -1
|
||||
if (bIsUser && !aIsUser) return 1
|
||||
// prioritize "common" langs in the network
|
||||
const aIsCommon = !!APP_LANGUAGES.find(al => al.code2 === a.value)
|
||||
const bIsCommon = !!APP_LANGUAGES.find(al => al.code2 === b.value)
|
||||
if (aIsCommon && !bIsCommon) return -1
|
||||
if (bIsCommon && !aIsCommon) return 1
|
||||
// fall back to alphabetical
|
||||
return a.label.localeCompare(b.label)
|
||||
}),
|
||||
const languages = useMemo(() => {
|
||||
return LANGUAGES.filter(
|
||||
(lang, index, self) =>
|
||||
Boolean(lang.code2) && // reduce to the code2 varieties
|
||||
index === self.findIndex(t => t.code2 === lang.code2), // remove dupes (which will happen)
|
||||
)
|
||||
}, [_, appLanguage, contentLanguages])
|
||||
.map(l => ({
|
||||
label: languageName(l, appLanguage),
|
||||
value: l.code2,
|
||||
key: l.code2 + l.code3,
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
// prioritize user's languages
|
||||
const aIsUser = contentLanguages.includes(a.value)
|
||||
const bIsUser = contentLanguages.includes(b.value)
|
||||
if (aIsUser && !bIsUser) return -1
|
||||
if (bIsUser && !aIsUser) return 1
|
||||
// prioritize "common" langs in the network
|
||||
const aIsCommon = !!APP_LANGUAGES.find(al => al.code2 === a.value)
|
||||
const bIsCommon = !!APP_LANGUAGES.find(al => al.code2 === b.value)
|
||||
if (aIsCommon && !bIsCommon) return -1
|
||||
if (bIsCommon && !aIsCommon) return 1
|
||||
// fall back to alphabetical
|
||||
return a.label.localeCompare(b.label)
|
||||
})
|
||||
}, [appLanguage, contentLanguages])
|
||||
|
||||
const style = {
|
||||
backgroundColor: t.atoms.bg_contrast_25.backgroundColor,
|
||||
color: t.atoms.text.color,
|
||||
fontSize: a.text_xs.fontSize,
|
||||
fontFamily: 'inherit',
|
||||
fontWeight: a.font_bold.fontWeight,
|
||||
paddingHorizontal: 14,
|
||||
paddingRight: 32,
|
||||
paddingVertical: 8,
|
||||
borderRadius: a.rounded_full.borderRadius,
|
||||
borderWidth: a.border.borderWidth,
|
||||
borderColor: t.atoms.border_contrast_low.borderColor,
|
||||
}
|
||||
const currentLanguageLabel =
|
||||
languages.find(lang => lang.value === value)?.label ?? _(msg`All languages`)
|
||||
|
||||
return (
|
||||
<RNPickerSelect
|
||||
darkTheme={t.scheme === 'dark'}
|
||||
placeholder={{}}
|
||||
value={value}
|
||||
onValueChange={onChange}
|
||||
items={items}
|
||||
Icon={() => (
|
||||
<ChevronDown fill={t.atoms.text_contrast_low.color} size="sm" />
|
||||
)}
|
||||
useNativeAndroidPickerStyle={false}
|
||||
style={{
|
||||
iconContainer: {
|
||||
pointerEvents: 'none',
|
||||
right: a.px_sm.paddingRight,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
inputAndroid: {
|
||||
...style,
|
||||
paddingVertical: 2,
|
||||
},
|
||||
inputIOS: {
|
||||
...style,
|
||||
},
|
||||
inputWeb: web({
|
||||
...style,
|
||||
cursor: 'pointer',
|
||||
// @ts-ignore web only
|
||||
'-moz-appearance': 'none',
|
||||
'-webkit-appearance': 'none',
|
||||
appearance: 'none',
|
||||
outline: 0,
|
||||
borderWidth: 0,
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
<Menu.Root>
|
||||
<Menu.Trigger
|
||||
label={_(
|
||||
msg`Filter search by language (currently: ${currentLanguageLabel})`,
|
||||
)}>
|
||||
{({props}) => (
|
||||
<Button
|
||||
{...props}
|
||||
label={props.accessibilityLabel}
|
||||
size="small"
|
||||
color={platform({native: 'primary', default: 'secondary'})}
|
||||
variant={platform({native: 'ghost', default: 'solid'})}
|
||||
style={native([
|
||||
a.py_sm,
|
||||
a.px_sm,
|
||||
{marginRight: tokens.space.sm * -1},
|
||||
])}>
|
||||
<ButtonIcon icon={EarthIcon} />
|
||||
<ButtonText>{currentLanguageLabel}</ButtonText>
|
||||
<ButtonIcon
|
||||
icon={platform({
|
||||
native: ChevronUpDownIcon,
|
||||
default: ChevronDownIcon,
|
||||
})}
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
</Menu.Trigger>
|
||||
<Menu.Outer
|
||||
// HACKFIX: Currently there is no height limit for Radix dropdowns,
|
||||
// so if it's too tall it just goes off screen. TODO: fix internally -sfn
|
||||
style={web({maxHeight: '70vh'})}>
|
||||
<Menu.LabelText>
|
||||
<Trans>Filter search by language</Trans>
|
||||
</Menu.LabelText>
|
||||
<Menu.Item label={_(msg`All languages`)} onPress={() => onChange('')}>
|
||||
<Menu.ItemText>
|
||||
<Trans>All languages</Trans>
|
||||
</Menu.ItemText>
|
||||
<Menu.ItemRadio selected={value === ''} />
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
<Menu.Group>
|
||||
{languages.map(lang => (
|
||||
<Menu.Item
|
||||
key={lang.key}
|
||||
label={lang.label}
|
||||
onPress={() => onChange(lang.value)}>
|
||||
<Menu.ItemText>{lang.label}</Menu.ItemText>
|
||||
<Menu.ItemRadio selected={value === lang.value} />
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu.Group>
|
||||
</Menu.Outer>
|
||||
</Menu.Root>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -534,12 +530,7 @@ let SearchScreenInner = ({
|
||||
<Pager
|
||||
onPageSelected={onPageSelected}
|
||||
renderTabBar={props => (
|
||||
<Layout.Center
|
||||
style={[
|
||||
a.z_10,
|
||||
web([a.sticky]),
|
||||
{top: isWeb ? headerHeight : undefined},
|
||||
]}>
|
||||
<Layout.Center style={[a.z_10, web([a.sticky, {top: headerHeight}])]}>
|
||||
<TabBar items={sections.map(section => section.title)} {...props} />
|
||||
</Layout.Center>
|
||||
)}
|
||||
@@ -597,13 +588,13 @@ SearchScreenInner = React.memo(SearchScreenInner)
|
||||
export function SearchScreen(
|
||||
props: NativeStackScreenProps<SearchTabNavigatorParams, 'Search'>,
|
||||
) {
|
||||
const t = useThemeNew()
|
||||
const t = useTheme()
|
||||
const {gtMobile} = useBreakpoints()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const textInput = React.useRef<TextInput>(null)
|
||||
const {_} = useLingui()
|
||||
const setDrawerOpen = useSetDrawerOpen()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {currentAccount} = useSession()
|
||||
|
||||
// Query terms
|
||||
const queryParam = props.route?.params?.q ?? ''
|
||||
@@ -612,20 +603,72 @@ export function SearchScreen(
|
||||
useActorAutocompleteQuery(searchText, true)
|
||||
|
||||
const [showAutocomplete, setShowAutocomplete] = React.useState(false)
|
||||
const [searchHistory, setSearchHistory] = React.useState<string[]>([])
|
||||
const [selectedProfiles, setSelectedProfiles] = React.useState<
|
||||
AppBskyActorDefs.ProfileViewBasic[]
|
||||
>([])
|
||||
|
||||
const [termHistory = [], setTermHistory] = useStorage(account, [
|
||||
currentAccount?.did ?? 'pwi',
|
||||
'searchTermHistory',
|
||||
] as const)
|
||||
const [accountHistory = [], setAccountHistory] = useStorage(account, [
|
||||
currentAccount?.did ?? 'pwi',
|
||||
'searchAccountHistory',
|
||||
])
|
||||
|
||||
const {data: accountHistoryProfiles} = useProfilesQuery({
|
||||
handles: accountHistory,
|
||||
maintainData: true,
|
||||
})
|
||||
|
||||
const updateSearchHistory = useCallback(
|
||||
async (item: string) => {
|
||||
if (!item) return
|
||||
const newSearchHistory = [
|
||||
item,
|
||||
...termHistory.filter(search => search !== item),
|
||||
].slice(0, 6)
|
||||
setTermHistory(newSearchHistory)
|
||||
},
|
||||
[termHistory, setTermHistory],
|
||||
)
|
||||
|
||||
const updateProfileHistory = useCallback(
|
||||
async (item: AppBskyActorDefs.ProfileViewBasic) => {
|
||||
const newAccountHistory = [
|
||||
item.did,
|
||||
...accountHistory.filter(p => p !== item.did),
|
||||
].slice(0, 5)
|
||||
setAccountHistory(newAccountHistory)
|
||||
},
|
||||
[accountHistory, setAccountHistory],
|
||||
)
|
||||
|
||||
const deleteSearchHistoryItem = useCallback(
|
||||
async (item: string) => {
|
||||
setTermHistory(termHistory.filter(search => search !== item))
|
||||
},
|
||||
[termHistory, setTermHistory],
|
||||
)
|
||||
const deleteProfileHistoryItem = useCallback(
|
||||
async (item: AppBskyActorDefs.ProfileViewBasic) => {
|
||||
setAccountHistory(accountHistory.filter(p => p !== item.did))
|
||||
},
|
||||
[accountHistory, setAccountHistory],
|
||||
)
|
||||
|
||||
const {params, query, queryWithParams} = useQueryManager({
|
||||
initialQuery: queryParam,
|
||||
})
|
||||
const showFilters = Boolean(queryWithParams && !showAutocomplete)
|
||||
/*
|
||||
* Arbitrary sizing, so guess and check, used for sticky header alignment and
|
||||
* sizing.
|
||||
*/
|
||||
const headerHeight = 60 + (showFilters ? 40 : 0)
|
||||
|
||||
// web only - measure header height for sticky positioning
|
||||
const [headerHeight, setHeaderHeight] = React.useState(0)
|
||||
const headerRef = React.useRef(null)
|
||||
useLayoutEffect(() => {
|
||||
if (isWeb) {
|
||||
if (!headerRef.current) return
|
||||
const measurement = (headerRef.current as Element).getBoundingClientRect()
|
||||
setHeaderHeight(measurement.height)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useFocusEffect(
|
||||
useNonReactiveCallback(() => {
|
||||
@@ -635,30 +678,6 @@ export function SearchScreen(
|
||||
}),
|
||||
)
|
||||
|
||||
React.useEffect(() => {
|
||||
const loadSearchHistory = async () => {
|
||||
try {
|
||||
const history = await AsyncStorage.getItem('searchHistory')
|
||||
if (history !== null) {
|
||||
setSearchHistory(JSON.parse(history))
|
||||
}
|
||||
const profiles = await AsyncStorage.getItem('selectedProfiles')
|
||||
if (profiles !== null) {
|
||||
setSelectedProfiles(JSON.parse(profiles))
|
||||
}
|
||||
} catch (e: any) {
|
||||
logger.error('Failed to load search history', {message: e})
|
||||
}
|
||||
}
|
||||
|
||||
loadSearchHistory()
|
||||
}, [])
|
||||
|
||||
const onPressMenu = React.useCallback(() => {
|
||||
textInput.current?.blur()
|
||||
setDrawerOpen(true)
|
||||
}, [setDrawerOpen])
|
||||
|
||||
const onPressClearQuery = React.useCallback(() => {
|
||||
scrollToTopWeb()
|
||||
setSearchText('')
|
||||
@@ -670,57 +689,6 @@ export function SearchScreen(
|
||||
setSearchText(text)
|
||||
}, [])
|
||||
|
||||
const updateSearchHistory = React.useCallback(
|
||||
async (newQuery: string) => {
|
||||
newQuery = newQuery.trim()
|
||||
if (newQuery) {
|
||||
let newHistory = [
|
||||
newQuery,
|
||||
...searchHistory.filter(q => q !== newQuery),
|
||||
]
|
||||
|
||||
if (newHistory.length > 5) {
|
||||
newHistory = newHistory.slice(0, 5)
|
||||
}
|
||||
|
||||
setSearchHistory(newHistory)
|
||||
try {
|
||||
await AsyncStorage.setItem(
|
||||
'searchHistory',
|
||||
JSON.stringify(newHistory),
|
||||
)
|
||||
} catch (e: any) {
|
||||
logger.error('Failed to save search history', {message: e})
|
||||
}
|
||||
}
|
||||
},
|
||||
[searchHistory, setSearchHistory],
|
||||
)
|
||||
|
||||
const updateSelectedProfiles = React.useCallback(
|
||||
async (profile: AppBskyActorDefs.ProfileViewBasic) => {
|
||||
let newProfiles = [
|
||||
profile,
|
||||
...selectedProfiles.filter(p => p.did !== profile.did),
|
||||
]
|
||||
|
||||
if (newProfiles.length > 5) {
|
||||
newProfiles = newProfiles.slice(0, 5)
|
||||
}
|
||||
|
||||
setSelectedProfiles(newProfiles)
|
||||
try {
|
||||
await AsyncStorage.setItem(
|
||||
'selectedProfiles',
|
||||
JSON.stringify(newProfiles),
|
||||
)
|
||||
} catch (e: any) {
|
||||
logger.error('Failed to save selected profiles', {message: e})
|
||||
}
|
||||
},
|
||||
[selectedProfiles, setSelectedProfiles],
|
||||
)
|
||||
|
||||
const navigateToItem = React.useCallback(
|
||||
(item: string) => {
|
||||
scrollToTopWeb()
|
||||
@@ -774,10 +742,10 @@ export function SearchScreen(
|
||||
(profile: AppBskyActorDefs.ProfileViewBasic) => {
|
||||
// Slight delay to avoid updating during push nav animation.
|
||||
setTimeout(() => {
|
||||
updateSelectedProfiles(profile)
|
||||
updateProfileHistory(profile)
|
||||
}, 400)
|
||||
},
|
||||
[updateSelectedProfiles],
|
||||
[updateProfileHistory],
|
||||
)
|
||||
|
||||
const onSoftReset = React.useCallback(() => {
|
||||
@@ -798,36 +766,6 @@ export function SearchScreen(
|
||||
}, [onSoftReset, setMinimalShellMode]),
|
||||
)
|
||||
|
||||
const handleRemoveHistoryItem = React.useCallback(
|
||||
(itemToRemove: string) => {
|
||||
const updatedHistory = searchHistory.filter(item => item !== itemToRemove)
|
||||
setSearchHistory(updatedHistory)
|
||||
AsyncStorage.setItem(
|
||||
'searchHistory',
|
||||
JSON.stringify(updatedHistory),
|
||||
).catch(e => {
|
||||
logger.error('Failed to update search history', {message: e})
|
||||
})
|
||||
},
|
||||
[searchHistory],
|
||||
)
|
||||
|
||||
const handleRemoveProfile = React.useCallback(
|
||||
(profileToRemove: AppBskyActorDefs.ProfileViewBasic) => {
|
||||
const updatedProfiles = selectedProfiles.filter(
|
||||
profile => profile.did !== profileToRemove.did,
|
||||
)
|
||||
setSelectedProfiles(updatedProfiles)
|
||||
AsyncStorage.setItem(
|
||||
'selectedProfiles',
|
||||
JSON.stringify(updatedProfiles),
|
||||
).catch(e => {
|
||||
logger.error('Failed to update selected profiles', {message: e})
|
||||
})
|
||||
},
|
||||
[selectedProfiles],
|
||||
)
|
||||
|
||||
const onSearchInputFocus = React.useCallback(() => {
|
||||
if (isWeb) {
|
||||
// Prevent a jump on iPad by ensuring that
|
||||
@@ -843,75 +781,89 @@ export function SearchScreen(
|
||||
return (
|
||||
<Layout.Screen testID="searchScreen">
|
||||
<View
|
||||
ref={headerRef}
|
||||
onLayout={evt => {
|
||||
if (isWeb) setHeaderHeight(evt.nativeEvent.layout.height)
|
||||
}}
|
||||
style={[
|
||||
a.relative,
|
||||
a.z_10,
|
||||
web({
|
||||
height: headerHeight,
|
||||
position: 'sticky',
|
||||
top: 0,
|
||||
zIndex: 1,
|
||||
}),
|
||||
]}>
|
||||
<Layout.Center>
|
||||
<View style={[a.p_md, a.pb_sm, a.gap_sm, t.atoms.bg]}>
|
||||
<View style={[a.flex_row, a.gap_sm]}>
|
||||
{!gtMobile && !showAutocomplete && (
|
||||
<Button
|
||||
testID="viewHeaderBackOrMenuBtn"
|
||||
onPress={onPressMenu}
|
||||
hitSlop={HITSLOP_10}
|
||||
label={_(msg`Menu`)}
|
||||
accessibilityHint={_(
|
||||
msg`Access navigation links and settings`,
|
||||
)}
|
||||
size="large"
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
shape="square">
|
||||
<ButtonIcon icon={Menu} size="lg" />
|
||||
</Button>
|
||||
)}
|
||||
<View style={[a.flex_1]}>
|
||||
<SearchInput
|
||||
ref={textInput}
|
||||
value={searchText}
|
||||
onFocus={onSearchInputFocus}
|
||||
onChangeText={onChangeText}
|
||||
onClearText={onPressClearQuery}
|
||||
onSubmitEditing={onSubmit}
|
||||
/>
|
||||
</View>
|
||||
{showAutocomplete && (
|
||||
<Button
|
||||
label={_(msg`Cancel search`)}
|
||||
size="large"
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
style={[a.px_sm]}
|
||||
onPress={onPressCancelSearch}
|
||||
hitSlop={HITSLOP_10}>
|
||||
<ButtonText>
|
||||
<Trans>Cancel</Trans>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
)}
|
||||
<Layout.Center style={t.atoms.bg}>
|
||||
{!gtMobile && (
|
||||
<View
|
||||
// HACK: shift up search input. we can't remove the top padding
|
||||
// on the search input because it messes up the layout animation
|
||||
// if we add it only when the header is hidden
|
||||
style={{marginBottom: tokens.space.xs * -1}}>
|
||||
<Layout.Header.Outer noBottomBorder>
|
||||
<Layout.Header.MenuButton />
|
||||
<Layout.Header.Content align="left">
|
||||
<Layout.Header.TitleText>
|
||||
<Trans>Search</Trans>
|
||||
</Layout.Header.TitleText>
|
||||
</Layout.Header.Content>
|
||||
{showFilters ? (
|
||||
<SearchLanguageDropdown
|
||||
value={params.lang}
|
||||
onChange={params.setLang}
|
||||
/>
|
||||
) : (
|
||||
<Layout.Header.Slot />
|
||||
)}
|
||||
</Layout.Header.Outer>
|
||||
</View>
|
||||
)}
|
||||
<View style={[a.px_md, a.pt_sm, a.pb_sm, a.overflow_hidden]}>
|
||||
<View style={[a.gap_sm]}>
|
||||
<View style={[a.w_full, a.flex_row, a.align_stretch, a.gap_xs]}>
|
||||
<View style={[a.flex_1]}>
|
||||
<SearchInput
|
||||
ref={textInput}
|
||||
value={searchText}
|
||||
onFocus={onSearchInputFocus}
|
||||
onChangeText={onChangeText}
|
||||
onClearText={onPressClearQuery}
|
||||
onSubmitEditing={onSubmit}
|
||||
placeholder={_(msg`Search for posts, users, or feeds`)}
|
||||
hitSlop={{...HITSLOP_20, top: 0}}
|
||||
/>
|
||||
</View>
|
||||
{showAutocomplete && (
|
||||
<Button
|
||||
label={_(msg`Cancel search`)}
|
||||
size="large"
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
style={[a.px_sm]}
|
||||
onPress={onPressCancelSearch}
|
||||
hitSlop={HITSLOP_10}>
|
||||
<ButtonText>
|
||||
<Trans>Cancel</Trans>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{showFilters && (
|
||||
<View
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.align_center,
|
||||
a.justify_between,
|
||||
a.gap_sm,
|
||||
]}>
|
||||
<View style={[{width: 140}]}>
|
||||
{showFilters && gtMobile && (
|
||||
<View
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.align_center,
|
||||
a.justify_between,
|
||||
a.gap_sm,
|
||||
]}>
|
||||
<SearchLanguageDropdown
|
||||
value={params.lang}
|
||||
onChange={params.setLang}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</Layout.Center>
|
||||
</View>
|
||||
@@ -932,12 +884,12 @@ export function SearchScreen(
|
||||
/>
|
||||
) : (
|
||||
<SearchHistory
|
||||
searchHistory={searchHistory}
|
||||
selectedProfiles={selectedProfiles}
|
||||
searchHistory={termHistory}
|
||||
selectedProfiles={accountHistoryProfiles?.profiles || []}
|
||||
onItemClick={handleHistoryItemClick}
|
||||
onProfileClick={handleProfileClick}
|
||||
onRemoveItemClick={handleRemoveHistoryItem}
|
||||
onRemoveProfileClick={handleRemoveProfile}
|
||||
onRemoveItemClick={deleteSearchHistoryItem}
|
||||
onRemoveProfileClick={deleteProfileHistoryItem}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
@@ -1087,7 +1039,7 @@ function SearchHistory({
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Remove profile`)}
|
||||
accessibilityHint={_(
|
||||
msg`Remove profile from search history`,
|
||||
msg`Removes profile from search history`,
|
||||
)}
|
||||
onPress={() => onRemoveProfileClick(profile)}
|
||||
hitSlop={createHitslop(6)}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {useFocusGuards} from '@radix-ui/react-focus-guards'
|
||||
import {FocusScope} from '@radix-ui/react-focus-scope'
|
||||
import {RemoveScrollBar} from 'react-remove-scroll-bar'
|
||||
|
||||
import {useA11y} from '#/state/a11y'
|
||||
import {useModals} from '#/state/modals'
|
||||
import {ComposerOpts, useComposerState} from '#/state/shell/composer'
|
||||
import {
|
||||
@@ -12,7 +13,7 @@ import {
|
||||
EmojiPickerPosition,
|
||||
EmojiPickerState,
|
||||
} from '#/view/com/composer/text-input/web/EmojiPicker.web'
|
||||
import {useBreakpoints, useTheme} from '#/alf'
|
||||
import {atoms as a, flatten, useBreakpoints, useTheme} from '#/alf'
|
||||
import {ComposePost, useComposerCancelRef} from '../com/composer/Composer'
|
||||
|
||||
const BOTTOM_BAR_HEIGHT = 61
|
||||
@@ -41,6 +42,7 @@ function Inner({state}: {state: ComposerOpts}) {
|
||||
const {isModalActive} = useModals()
|
||||
const t = useTheme()
|
||||
const {gtMobile} = useBreakpoints()
|
||||
const {reduceMotionEnabled} = useA11y()
|
||||
const [pickerState, setPickerState] = React.useState<EmojiPickerState>({
|
||||
isOpen: false,
|
||||
pos: {top: 0, left: 0, right: 0, bottom: 0, nextFocusRef: null},
|
||||
@@ -71,17 +73,15 @@ function Inner({state}: {state: ComposerOpts}) {
|
||||
<DismissableLayer
|
||||
role="dialog"
|
||||
aria-modal
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
backgroundColor: '#000c',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
style={flatten([
|
||||
{position: 'fixed'},
|
||||
a.inset_0,
|
||||
{backgroundColor: '#000c'},
|
||||
a.flex,
|
||||
a.flex_col,
|
||||
a.align_center,
|
||||
!reduceMotionEnabled && a.fade_in,
|
||||
])}
|
||||
onFocusOutside={evt => evt.preventDefault()}
|
||||
onInteractOutside={evt => evt.preventDefault()}
|
||||
onDismiss={() => {
|
||||
@@ -96,6 +96,11 @@ function Inner({state}: {state: ComposerOpts}) {
|
||||
!gtMobile && styles.containerMobile,
|
||||
t.atoms.bg,
|
||||
t.atoms.border_contrast_medium,
|
||||
!reduceMotionEnabled && [
|
||||
a.zoom_fade_in,
|
||||
{animationDelay: 0.1},
|
||||
{animationFillMode: 'backwards'},
|
||||
],
|
||||
]}>
|
||||
<ComposePost
|
||||
cancelRef={ref}
|
||||
@@ -123,14 +128,14 @@ const styles = StyleSheet.create({
|
||||
borderRadius: 8,
|
||||
marginBottom: 0,
|
||||
borderWidth: 1,
|
||||
// @ts-ignore web only
|
||||
// @ts-expect-error web only
|
||||
maxHeight: 'calc(100% - (40px * 2))',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
containerMobile: {
|
||||
borderRadius: 0,
|
||||
marginBottom: BOTTOM_BAR_HEIGHT,
|
||||
// @ts-ignore web only
|
||||
// @ts-expect-error web only
|
||||
maxHeight: `calc(100% - ${BOTTOM_BAR_HEIGHT}px)`,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -33,7 +33,7 @@ import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||
import {PressableWithHover} from '#/view/com/util/PressableWithHover'
|
||||
import {UserAvatar} from '#/view/com/util/UserAvatar'
|
||||
import {NavSignupCard} from '#/view/shell/NavSignupCard'
|
||||
import {atoms as a, tokens, useBreakpoints, useTheme} from '#/alf'
|
||||
import {atoms as a, tokens, useBreakpoints, useTheme, web} from '#/alf'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import {DialogControlProps} from '#/components/Dialog'
|
||||
import {ArrowBoxLeft_Stroke2_Corner0_Rounded as LeaveIcon} from '#/components/icons/ArrowBoxLeft'
|
||||
@@ -235,7 +235,10 @@ function SwitchMenuItems({
|
||||
closeEverything()
|
||||
}
|
||||
return (
|
||||
<Menu.Outer>
|
||||
<Menu.Outer
|
||||
// HACKFIX: Currently there is no height limit for Radix dropdowns,
|
||||
// so if it's too tall it just goes off screen. TODO: fix internally -sfn
|
||||
style={web({maxHeight: '70vh'})}>
|
||||
{accounts && accounts.length > 0 && (
|
||||
<>
|
||||
<Menu.Group>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, {useEffect} from 'react'
|
||||
import {useEffect, useLayoutEffect, useState} from 'react'
|
||||
import {StyleSheet, TouchableWithoutFeedback, View} from 'react-native'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
@@ -33,6 +33,20 @@ function ShellInner() {
|
||||
const closeAllActiveElements = useCloseAllActiveElements()
|
||||
const {_} = useLingui()
|
||||
const showDrawer = !isDesktop && isDrawerOpen
|
||||
const [showDrawerDelayedExit, setShowDrawerDelayedExit] = useState(showDrawer)
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (showDrawer !== showDrawerDelayedExit) {
|
||||
if (showDrawer) {
|
||||
setShowDrawerDelayedExit(true)
|
||||
} else {
|
||||
const timeout = setTimeout(() => {
|
||||
setShowDrawerDelayedExit(false)
|
||||
}, 160)
|
||||
return () => clearTimeout(timeout)
|
||||
}
|
||||
}
|
||||
}, [showDrawer, showDrawerDelayedExit])
|
||||
|
||||
useComposerKeyboardShortcut()
|
||||
useIntentHandler()
|
||||
@@ -56,7 +70,7 @@ function ShellInner() {
|
||||
<Lightbox />
|
||||
<PortalOutlet />
|
||||
|
||||
{showDrawer && (
|
||||
{showDrawerDelayedExit && (
|
||||
<>
|
||||
<RemoveScrollBar />
|
||||
<TouchableWithoutFeedback
|
||||
@@ -66,20 +80,27 @@ function ShellInner() {
|
||||
setDrawerOpen(false)
|
||||
}
|
||||
}}
|
||||
accessibilityLabel={_(msg`Close navigation footer`)}
|
||||
accessibilityHint={_(msg`Closes bottom navigation bar`)}>
|
||||
accessibilityLabel={_(msg`Close drawer menu`)}
|
||||
accessibilityHint="">
|
||||
<View
|
||||
style={[
|
||||
styles.drawerMask,
|
||||
{
|
||||
backgroundColor: select(t.name, {
|
||||
light: 'rgba(0, 57, 117, 0.1)',
|
||||
dark: 'rgba(1, 82, 168, 0.1)',
|
||||
dim: 'rgba(10, 13, 16, 0.8)',
|
||||
}),
|
||||
backgroundColor: showDrawer
|
||||
? select(t.name, {
|
||||
light: 'rgba(0, 57, 117, 0.1)',
|
||||
dark: 'rgba(1, 82, 168, 0.1)',
|
||||
dim: 'rgba(10, 13, 16, 0.8)',
|
||||
})
|
||||
: 'transparent',
|
||||
},
|
||||
a.transition_color,
|
||||
]}>
|
||||
<View style={styles.drawerContainer}>
|
||||
<View
|
||||
style={[
|
||||
styles.drawerContainer,
|
||||
showDrawer ? a.slide_in_left : a.slide_out_left,
|
||||
]}>
|
||||
<DrawerContent />
|
||||
</View>
|
||||
</View>
|
||||
@@ -109,7 +130,6 @@ const styles = StyleSheet.create({
|
||||
backgroundColor: colors.black, // TODO
|
||||
},
|
||||
drawerMask: {
|
||||
// @ts-ignore web only
|
||||
position: 'fixed',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
@@ -118,10 +138,11 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
drawerContainer: {
|
||||
display: 'flex',
|
||||
// @ts-ignore web only
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
height: '100%',
|
||||
width: 330,
|
||||
maxWidth: '80%',
|
||||
},
|
||||
})
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"module": "esnext",
|
||||
"types": ["node"],
|
||||
"types": ["node", "jest"],
|
||||
"paths": {
|
||||
"#/*": ["./src/*"],
|
||||
"lib/*": ["./src/lib/*"],
|
||||
|
||||
@@ -277,7 +277,7 @@
|
||||
multiformats "^9.9.0"
|
||||
zod "^3.23.8"
|
||||
|
||||
"@atproto/lexicon@^0.4.7":
|
||||
"@atproto/lexicon@^0.4.4", "@atproto/lexicon@^0.4.7":
|
||||
version "0.4.7"
|
||||
resolved "https://registry.yarnpkg.com/@atproto/lexicon/-/lexicon-0.4.7.tgz#f5d31615c21bcfd3e655f1e4f11a40a62fea9f86"
|
||||
integrity sha512-/x6h3tAiDNzSi4eXtC8ke65B7UzsagtlGRHmUD95698x5lBRpDnpizj0fZWTZVYed5qnOmz/ZEue+v3wDmO61g==
|
||||
|
||||
Reference in New Issue
Block a user