+2
-1
.eslintrc.js
+2
-1
.eslintrc.js
···
37
'Toast.Action',
38
'AgeAssuranceAdmonition',
39
'Span',
40
],
41
impliedTextProps: [],
42
suggestedTextWrappers: {
···
88
'no-unused-vars': 'off',
89
'@typescript-eslint/no-unused-vars': [
90
'error',
91
-
{argsIgnorePattern: '^_', varsIgnorePattern: '^_'},
92
],
93
'@typescript-eslint/consistent-type-imports': [
94
'warn',
···
37
'Toast.Action',
38
'AgeAssuranceAdmonition',
39
'Span',
40
+
'StackedButton',
41
],
42
impliedTextProps: [],
43
suggestedTextWrappers: {
···
89
'no-unused-vars': 'off',
90
'@typescript-eslint/no-unused-vars': [
91
'error',
92
+
{argsIgnorePattern: '^_', varsIgnorePattern: '^_.+'},
93
],
94
'@typescript-eslint/consistent-type-imports': [
95
'warn',
+2
README.md
+2
README.md
···
70
71
See [./LICENSE](./LICENSE) for the full license.
72
73
+
Bluesky Social PBC has committed to a software patent non-aggression pledge. For details see [the original announcement](https://bsky.social/about/blog/10-01-2025-patent-pledge).
74
+
75
## P.S.
76
77
We โค๏ธ you and all of the ways you support us. Thank you for making Bluesky a great place!
+8
-12
jest/test-utils.tsx
+8
-12
jest/test-utils.tsx
···
1
-
import React from 'react'
2
-
import {render} from '@testing-library/react-native'
3
import {GestureHandlerRootView} from 'react-native-gesture-handler'
4
-
import {RootSiblingParent} from 'react-native-root-siblings'
5
import {SafeAreaProvider} from 'react-native-safe-area-context'
6
-
import {RootStoreProvider, RootStoreModel} from '../src/state'
7
import {ThemeProvider} from '../src/lib/ThemeContext'
8
9
const customRender = (ui: any, rootStore: RootStoreModel) =>
10
render(
11
-
// eslint-disable-next-line react-native/no-inline-styles
12
<GestureHandlerRootView style={{flex: 1}}>
13
-
<RootSiblingParent>
14
-
<RootStoreProvider value={rootStore}>
15
-
<ThemeProvider theme="light">
16
-
<SafeAreaProvider>{ui}</SafeAreaProvider>
17
-
</ThemeProvider>
18
-
</RootStoreProvider>
19
-
</RootSiblingParent>
20
</GestureHandlerRootView>,
21
)
22
···
1
import {GestureHandlerRootView} from 'react-native-gesture-handler'
2
import {SafeAreaProvider} from 'react-native-safe-area-context'
3
+
import {render} from '@testing-library/react-native'
4
+
5
import {ThemeProvider} from '../src/lib/ThemeContext'
6
+
import {type RootStoreModel, RootStoreProvider} from '../src/state'
7
8
const customRender = (ui: any, rootStore: RootStoreModel) =>
9
render(
10
<GestureHandlerRootView style={{flex: 1}}>
11
+
<RootStoreProvider value={rootStore}>
12
+
<ThemeProvider theme="light">
13
+
<SafeAreaProvider>{ui}</SafeAreaProvider>
14
+
</ThemeProvider>
15
+
</RootStoreProvider>
16
</GestureHandlerRootView>,
17
)
18
+2
-3
package.json
+2
-3
package.json
···
197
"react-native-progress": "bluesky-social/react-native-progress",
198
"react-native-qrcode-styled": "^0.3.3",
199
"react-native-reanimated": "^3.19.1",
200
-
"react-native-root-siblings": "^5.0.1",
201
"react-native-safe-area-context": "~5.6.0",
202
"react-native-screens": "~4.16.0",
203
"react-native-svg": "15.12.1",
···
247
"babel-jest": "^29.7.0",
248
"babel-plugin-macros": "^3.1.0",
249
"babel-plugin-module-resolver": "^5.0.2",
250
-
"babel-plugin-react-compiler": "^19.1.0-rc.1",
251
"babel-preset-expo": "~54.0.0",
252
"eslint": "^8.19.0",
253
"eslint-plugin-bsky-internal": "link:./eslint",
···
255
"eslint-plugin-import": "^2.31.0",
256
"eslint-plugin-lingui": "^0.2.0",
257
"eslint-plugin-react": "^7.33.2",
258
-
"eslint-plugin-react-compiler": "^19.1.0-rc.1",
259
"eslint-plugin-react-native-a11y": "^3.3.0",
260
"eslint-plugin-simple-import-sort": "^12.0.0",
261
"file-loader": "6.2.0",
···
197
"react-native-progress": "bluesky-social/react-native-progress",
198
"react-native-qrcode-styled": "^0.3.3",
199
"react-native-reanimated": "^3.19.1",
200
"react-native-safe-area-context": "~5.6.0",
201
"react-native-screens": "~4.16.0",
202
"react-native-svg": "15.12.1",
···
246
"babel-jest": "^29.7.0",
247
"babel-plugin-macros": "^3.1.0",
248
"babel-plugin-module-resolver": "^5.0.2",
249
+
"babel-plugin-react-compiler": "^19.1.0-rc.3",
250
"babel-preset-expo": "~54.0.0",
251
"eslint": "^8.19.0",
252
"eslint-plugin-bsky-internal": "link:./eslint",
···
254
"eslint-plugin-import": "^2.31.0",
255
"eslint-plugin-lingui": "^0.2.0",
256
"eslint-plugin-react": "^7.33.2",
257
+
"eslint-plugin-react-compiler": "^19.1.0-rc.2",
258
"eslint-plugin-react-native-a11y": "^3.3.0",
259
"eslint-plugin-simple-import-sort": "^12.0.0",
260
"file-loader": "6.2.0",
+61
-60
src/App.native.tsx
+61
-60
src/App.native.tsx
···
4
5
import React, {useEffect, useState} from 'react'
6
import {GestureHandlerRootView} from 'react-native-gesture-handler'
7
-
import {RootSiblingParent} from 'react-native-root-siblings'
8
import {
9
initialWindowMetrics,
10
SafeAreaProvider,
···
84
}
85
if (isAndroid) {
86
// iOS is handled by the config plugin -sfn
87
-
ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT_UP)
88
}
89
90
/**
···
133
<ThemeProvider theme={theme}>
134
<ContextMenuProvider>
135
<Splash isReady={isReady && hasCheckedReferrer}>
136
-
<RootSiblingParent>
137
-
<VideoVolumeProvider>
138
-
<React.Fragment
139
-
// Resets the entire tree below when it changes:
140
-
key={currentAccount?.did}>
141
-
<QueryProvider currentDid={currentAccount?.did}>
142
-
<PolicyUpdateOverlayProvider>
143
-
<StatsigProvider>
144
-
<AgeAssuranceProvider>
145
-
<ComposerProvider>
146
-
<MessagesProvider>
147
-
{/* LabelDefsProvider MUST come before ModerationOptsProvider */}
148
-
<LabelDefsProvider>
149
-
<ModerationOptsProvider>
150
-
<LoggedOutViewProvider>
151
-
<SelectedFeedProvider>
152
-
<HiddenRepliesProvider>
153
-
<HomeBadgeProvider>
154
-
<UnreadNotifsProvider>
155
-
<BackgroundNotificationPreferencesProvider>
156
-
<MutedThreadsProvider>
157
-
<ProgressGuideProvider>
158
-
<ServiceAccountManager>
159
-
<EmailVerificationProvider>
160
-
<HideBottomBarBorderProvider>
161
-
<GestureHandlerRootView
162
-
style={s.h100pct}>
163
-
<GlobalGestureEventsProvider>
164
-
<IntentDialogProvider>
165
-
<TestCtrls />
166
-
<Shell />
167
-
<NuxDialogs />
168
-
<ToastOutlet />
169
-
</IntentDialogProvider>
170
-
</GlobalGestureEventsProvider>
171
-
</GestureHandlerRootView>
172
-
</HideBottomBarBorderProvider>
173
-
</EmailVerificationProvider>
174
-
</ServiceAccountManager>
175
-
</ProgressGuideProvider>
176
-
</MutedThreadsProvider>
177
-
</BackgroundNotificationPreferencesProvider>
178
-
</UnreadNotifsProvider>
179
-
</HomeBadgeProvider>
180
-
</HiddenRepliesProvider>
181
-
</SelectedFeedProvider>
182
-
</LoggedOutViewProvider>
183
-
</ModerationOptsProvider>
184
-
</LabelDefsProvider>
185
-
</MessagesProvider>
186
-
</ComposerProvider>
187
-
</AgeAssuranceProvider>
188
-
</StatsigProvider>
189
-
</PolicyUpdateOverlayProvider>
190
-
</QueryProvider>
191
-
</React.Fragment>
192
-
</VideoVolumeProvider>
193
-
</RootSiblingParent>
194
</Splash>
195
</ContextMenuProvider>
196
</ThemeProvider>
···
4
5
import React, {useEffect, useState} from 'react'
6
import {GestureHandlerRootView} from 'react-native-gesture-handler'
7
import {
8
initialWindowMetrics,
9
SafeAreaProvider,
···
83
}
84
if (isAndroid) {
85
// iOS is handled by the config plugin -sfn
86
+
ScreenOrientation.lockAsync(
87
+
ScreenOrientation.OrientationLock.PORTRAIT_UP,
88
+
).catch(error =>
89
+
logger.debug('Could not lock orientation', {safeMessage: error}),
90
+
)
91
}
92
93
/**
···
136
<ThemeProvider theme={theme}>
137
<ContextMenuProvider>
138
<Splash isReady={isReady && hasCheckedReferrer}>
139
+
<VideoVolumeProvider>
140
+
<React.Fragment
141
+
// Resets the entire tree below when it changes:
142
+
key={currentAccount?.did}>
143
+
<QueryProvider currentDid={currentAccount?.did}>
144
+
<PolicyUpdateOverlayProvider>
145
+
<StatsigProvider>
146
+
<AgeAssuranceProvider>
147
+
<ComposerProvider>
148
+
<MessagesProvider>
149
+
{/* LabelDefsProvider MUST come before ModerationOptsProvider */}
150
+
<LabelDefsProvider>
151
+
<ModerationOptsProvider>
152
+
<LoggedOutViewProvider>
153
+
<SelectedFeedProvider>
154
+
<HiddenRepliesProvider>
155
+
<HomeBadgeProvider>
156
+
<UnreadNotifsProvider>
157
+
<BackgroundNotificationPreferencesProvider>
158
+
<MutedThreadsProvider>
159
+
<ProgressGuideProvider>
160
+
<ServiceAccountManager>
161
+
<EmailVerificationProvider>
162
+
<HideBottomBarBorderProvider>
163
+
<GestureHandlerRootView
164
+
style={s.h100pct}>
165
+
<GlobalGestureEventsProvider>
166
+
<IntentDialogProvider>
167
+
<TestCtrls />
168
+
<Shell />
169
+
<NuxDialogs />
170
+
<ToastOutlet />
171
+
</IntentDialogProvider>
172
+
</GlobalGestureEventsProvider>
173
+
</GestureHandlerRootView>
174
+
</HideBottomBarBorderProvider>
175
+
</EmailVerificationProvider>
176
+
</ServiceAccountManager>
177
+
</ProgressGuideProvider>
178
+
</MutedThreadsProvider>
179
+
</BackgroundNotificationPreferencesProvider>
180
+
</UnreadNotifsProvider>
181
+
</HomeBadgeProvider>
182
+
</HiddenRepliesProvider>
183
+
</SelectedFeedProvider>
184
+
</LoggedOutViewProvider>
185
+
</ModerationOptsProvider>
186
+
</LabelDefsProvider>
187
+
</MessagesProvider>
188
+
</ComposerProvider>
189
+
</AgeAssuranceProvider>
190
+
</StatsigProvider>
191
+
</PolicyUpdateOverlayProvider>
192
+
</QueryProvider>
193
+
</React.Fragment>
194
+
</VideoVolumeProvider>
195
</Splash>
196
</ContextMenuProvider>
197
</ThemeProvider>
+54
-57
src/App.web.tsx
+54
-57
src/App.web.tsx
···
3
import './style.css'
4
5
import React, {useEffect, useState} from 'react'
6
-
import {RootSiblingParent} from 'react-native-root-siblings'
7
import {SafeAreaProvider} from 'react-native-safe-area-context'
8
import {msg} from '@lingui/macro'
9
import {useLingui} from '@lingui/react'
···
111
<Alf theme={theme}>
112
<ThemeProvider theme={theme}>
113
<ContextMenuProvider>
114
-
<RootSiblingParent>
115
-
<VideoVolumeProvider>
116
-
<ActiveVideoProvider>
117
-
<React.Fragment
118
-
// Resets the entire tree below when it changes:
119
-
key={currentAccount?.did}>
120
-
<QueryProvider currentDid={currentAccount?.did}>
121
-
<PolicyUpdateOverlayProvider>
122
-
<StatsigProvider>
123
-
<AgeAssuranceProvider>
124
-
<ComposerProvider>
125
-
<MessagesProvider>
126
-
{/* LabelDefsProvider MUST come before ModerationOptsProvider */}
127
-
<LabelDefsProvider>
128
-
<ModerationOptsProvider>
129
-
<LoggedOutViewProvider>
130
-
<SelectedFeedProvider>
131
-
<HiddenRepliesProvider>
132
-
<HomeBadgeProvider>
133
-
<UnreadNotifsProvider>
134
-
<BackgroundNotificationPreferencesProvider>
135
-
<MutedThreadsProvider>
136
-
<SafeAreaProvider>
137
-
<ProgressGuideProvider>
138
-
<ServiceConfigProvider>
139
-
<EmailVerificationProvider>
140
-
<HideBottomBarBorderProvider>
141
-
<IntentDialogProvider>
142
-
<Shell />
143
-
<NuxDialogs />
144
-
<ToastOutlet />
145
-
</IntentDialogProvider>
146
-
</HideBottomBarBorderProvider>
147
-
</EmailVerificationProvider>
148
-
</ServiceConfigProvider>
149
-
</ProgressGuideProvider>
150
-
</SafeAreaProvider>
151
-
</MutedThreadsProvider>
152
-
</BackgroundNotificationPreferencesProvider>
153
-
</UnreadNotifsProvider>
154
-
</HomeBadgeProvider>
155
-
</HiddenRepliesProvider>
156
-
</SelectedFeedProvider>
157
-
</LoggedOutViewProvider>
158
-
</ModerationOptsProvider>
159
-
</LabelDefsProvider>
160
-
</MessagesProvider>
161
-
</ComposerProvider>
162
-
</AgeAssuranceProvider>
163
-
</StatsigProvider>
164
-
</PolicyUpdateOverlayProvider>
165
-
</QueryProvider>
166
-
</React.Fragment>
167
-
</ActiveVideoProvider>
168
-
</VideoVolumeProvider>
169
-
</RootSiblingParent>
170
</ContextMenuProvider>
171
</ThemeProvider>
172
</Alf>
···
3
import './style.css'
4
5
import React, {useEffect, useState} from 'react'
6
import {SafeAreaProvider} from 'react-native-safe-area-context'
7
import {msg} from '@lingui/macro'
8
import {useLingui} from '@lingui/react'
···
110
<Alf theme={theme}>
111
<ThemeProvider theme={theme}>
112
<ContextMenuProvider>
113
+
<VideoVolumeProvider>
114
+
<ActiveVideoProvider>
115
+
<React.Fragment
116
+
// Resets the entire tree below when it changes:
117
+
key={currentAccount?.did}>
118
+
<QueryProvider currentDid={currentAccount?.did}>
119
+
<PolicyUpdateOverlayProvider>
120
+
<StatsigProvider>
121
+
<AgeAssuranceProvider>
122
+
<ComposerProvider>
123
+
<MessagesProvider>
124
+
{/* LabelDefsProvider MUST come before ModerationOptsProvider */}
125
+
<LabelDefsProvider>
126
+
<ModerationOptsProvider>
127
+
<LoggedOutViewProvider>
128
+
<SelectedFeedProvider>
129
+
<HiddenRepliesProvider>
130
+
<HomeBadgeProvider>
131
+
<UnreadNotifsProvider>
132
+
<BackgroundNotificationPreferencesProvider>
133
+
<MutedThreadsProvider>
134
+
<SafeAreaProvider>
135
+
<ProgressGuideProvider>
136
+
<ServiceConfigProvider>
137
+
<EmailVerificationProvider>
138
+
<HideBottomBarBorderProvider>
139
+
<IntentDialogProvider>
140
+
<Shell />
141
+
<NuxDialogs />
142
+
<ToastOutlet />
143
+
</IntentDialogProvider>
144
+
</HideBottomBarBorderProvider>
145
+
</EmailVerificationProvider>
146
+
</ServiceConfigProvider>
147
+
</ProgressGuideProvider>
148
+
</SafeAreaProvider>
149
+
</MutedThreadsProvider>
150
+
</BackgroundNotificationPreferencesProvider>
151
+
</UnreadNotifsProvider>
152
+
</HomeBadgeProvider>
153
+
</HiddenRepliesProvider>
154
+
</SelectedFeedProvider>
155
+
</LoggedOutViewProvider>
156
+
</ModerationOptsProvider>
157
+
</LabelDefsProvider>
158
+
</MessagesProvider>
159
+
</ComposerProvider>
160
+
</AgeAssuranceProvider>
161
+
</StatsigProvider>
162
+
</PolicyUpdateOverlayProvider>
163
+
</QueryProvider>
164
+
</React.Fragment>
165
+
</ActiveVideoProvider>
166
+
</VideoVolumeProvider>
167
</ContextMenuProvider>
168
</ThemeProvider>
169
</Alf>
+10
-4
src/alf/util/systemUI.ts
+10
-4
src/alf/util/systemUI.ts
···
1
import * as SystemUI from 'expo-system-ui'
2
import {type Theme} from '@bsky.app/alf'
3
4
import {isAndroid} from '#/platform/detection'
5
6
export function setSystemUITheme(themeType: 'theme' | 'lightbox', t: Theme) {
7
if (isAndroid) {
8
-
if (themeType === 'theme') {
9
-
SystemUI.setBackgroundColorAsync(t.atoms.bg.backgroundColor)
10
-
} else {
11
-
SystemUI.setBackgroundColorAsync('black')
12
}
13
}
14
}
···
1
import * as SystemUI from 'expo-system-ui'
2
import {type Theme} from '@bsky.app/alf'
3
4
+
import {logger} from '#/logger'
5
import {isAndroid} from '#/platform/detection'
6
7
export function setSystemUITheme(themeType: 'theme' | 'lightbox', t: Theme) {
8
if (isAndroid) {
9
+
try {
10
+
if (themeType === 'theme') {
11
+
SystemUI.setBackgroundColorAsync(t.atoms.bg.backgroundColor)
12
+
} else {
13
+
SystemUI.setBackgroundColorAsync('black')
14
+
}
15
+
} catch (error) {
16
+
// Can reject with 'The current activity is no longer available' - no big deal
17
+
logger.debug('Could not set system UI theme', {safeMessage: error})
18
}
19
}
20
}
+44
-24
src/components/Admonition.tsx
+44
-24
src/components/Admonition.tsx
···
3
4
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
5
import {Button as BaseButton, type ButtonProps} from '#/components/Button'
6
-
import {CircleInfo_Stroke2_Corner0_Rounded as ErrorIcon} from '#/components/icons/CircleInfo'
7
-
import {Eye_Stroke2_Corner0_Rounded as InfoIcon} from '#/components/icons/Eye'
8
-
import {Leaf_Stroke2_Corner0_Rounded as TipIcon} from '#/components/icons/Leaf'
9
import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning'
10
import {Text as BaseText, type TextProps} from '#/components/Typography'
11
12
export const colors = {
13
-
warning: {
14
-
light: '#DFBC00',
15
-
dark: '#BFAF1F',
16
-
},
17
}
18
19
type Context = {
···
29
const t = useTheme()
30
const {type} = useContext(Context)
31
const Icon = {
32
-
info: InfoIcon,
33
-
tip: TipIcon,
34
warning: WarningIcon,
35
-
error: ErrorIcon,
36
}[type]
37
const fill = {
38
info: t.atoms.text_contrast_medium.color,
39
tip: t.palette.primary_500,
40
-
warning: colors.warning.light,
41
error: t.palette.negative_500,
42
}[type]
43
return <Icon fill={fill} size="md" />
44
}
45
46
export function Text({
47
children,
48
style,
49
...rest
50
}: Pick<TextProps, 'children' | 'style'>) {
51
return (
52
-
<BaseText
53
-
{...rest}
54
-
style={[a.flex_1, a.text_sm, a.leading_snug, a.pr_md, style]}>
55
{children}
56
</BaseText>
57
)
···
60
export function Button({
61
children,
62
...props
63
-
}: Omit<ButtonProps, 'size' | 'variant' | 'color'>) {
64
return (
65
-
<BaseButton size="tiny" variant="outline" color="secondary" {...props}>
66
{children}
67
</BaseButton>
68
)
69
}
70
71
-
export function Row({children}: {children: React.ReactNode}) {
72
return (
73
-
<View style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}>
74
{children}
75
</View>
76
)
···
88
const t = useTheme()
89
const {gtMobile} = useBreakpoints()
90
const borderColor = {
91
-
info: t.atoms.border_contrast_low.borderColor,
92
-
tip: t.atoms.border_contrast_low.borderColor,
93
-
warning: t.atoms.border_contrast_low.borderColor,
94
-
error: t.atoms.border_contrast_low.borderColor,
95
}[type]
96
return (
97
<Context.Provider value={{type}}>
98
<View
99
style={[
100
gtMobile ? a.p_md : a.p_sm,
101
a.rounded_sm,
102
a.border,
103
-
t.atoms.bg_contrast_25,
104
{borderColor},
105
style,
106
]}>
···
123
<Outer type={type} style={style}>
124
<Row>
125
<Icon />
126
-
<Text>{children}</Text>
127
</Row>
128
</Outer>
129
)
···
3
4
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
5
import {Button as BaseButton, type ButtonProps} from '#/components/Button'
6
+
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfoIcon} from '#/components/icons/CircleInfo'
7
+
import {CircleX_Stroke2_Corner0_Rounded as CircleXIcon} from '#/components/icons/CircleX'
8
import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning'
9
import {Text as BaseText, type TextProps} from '#/components/Typography'
10
11
export const colors = {
12
+
warning: '#FFC404',
13
}
14
15
type Context = {
···
25
const t = useTheme()
26
const {type} = useContext(Context)
27
const Icon = {
28
+
info: CircleInfoIcon,
29
+
tip: CircleInfoIcon,
30
warning: WarningIcon,
31
+
error: CircleXIcon,
32
}[type]
33
const fill = {
34
info: t.atoms.text_contrast_medium.color,
35
tip: t.palette.primary_500,
36
+
warning: colors.warning,
37
error: t.palette.negative_500,
38
}[type]
39
return <Icon fill={fill} size="md" />
40
}
41
42
+
export function Content({
43
+
children,
44
+
style,
45
+
...rest
46
+
}: {
47
+
children: React.ReactNode
48
+
style?: StyleProp<ViewStyle>
49
+
}) {
50
+
return (
51
+
<View
52
+
style={[a.gap_sm, a.flex_1, {minHeight: 20}, a.justify_center, style]}
53
+
{...rest}>
54
+
{children}
55
+
</View>
56
+
)
57
+
}
58
+
59
export function Text({
60
children,
61
style,
62
...rest
63
}: Pick<TextProps, 'children' | 'style'>) {
64
return (
65
+
<BaseText {...rest} style={[a.text_sm, a.leading_snug, a.pr_md, style]}>
66
{children}
67
</BaseText>
68
)
···
71
export function Button({
72
children,
73
...props
74
+
}: Omit<ButtonProps, 'size' | 'variant'>) {
75
return (
76
+
<BaseButton size="tiny" {...props}>
77
{children}
78
</BaseButton>
79
)
80
}
81
82
+
export function Row({
83
+
children,
84
+
style,
85
+
}: {
86
+
children: React.ReactNode
87
+
style?: StyleProp<ViewStyle>
88
+
}) {
89
return (
90
+
<View style={[a.flex_1, a.flex_row, a.align_start, a.gap_sm, style]}>
91
{children}
92
</View>
93
)
···
105
const t = useTheme()
106
const {gtMobile} = useBreakpoints()
107
const borderColor = {
108
+
info: t.atoms.border_contrast_high.borderColor,
109
+
tip: t.palette.primary_500,
110
+
warning: colors.warning,
111
+
error: t.palette.negative_500,
112
}[type]
113
return (
114
<Context.Provider value={{type}}>
115
<View
116
style={[
117
gtMobile ? a.p_md : a.p_sm,
118
+
a.p_md,
119
a.rounded_sm,
120
a.border,
121
+
t.atoms.bg,
122
{borderColor},
123
style,
124
]}>
···
141
<Outer type={type} style={style}>
142
<Row>
143
<Icon />
144
+
<Content>
145
+
<Text>{children}</Text>
146
+
</Content>
147
</Row>
148
</Outer>
149
)
+53
-41
src/components/Button.tsx
+53
-41
src/components/Button.tsx
···
274
} else if (color === 'primary_subtle') {
275
if (!disabled) {
276
baseStyles.push({
277
-
backgroundColor: select(t.name, {
278
-
light: t.palette.primary_50,
279
-
dim: t.palette.primary_100,
280
-
dark: t.palette.primary_100,
281
-
}),
282
})
283
hoverStyles.push({
284
-
backgroundColor: select(t.name, {
285
-
light: t.palette.primary_100,
286
-
dim: t.palette.primary_200,
287
-
dark: t.palette.primary_200,
288
-
}),
289
})
290
} else {
291
baseStyles.push({
···
295
} else if (color === 'negative_subtle') {
296
if (!disabled) {
297
baseStyles.push({
298
-
backgroundColor: select(t.name, {
299
-
light: t.palette.negative_50,
300
-
dim: t.palette.negative_100,
301
-
dark: t.palette.negative_100,
302
-
}),
303
})
304
hoverStyles.push({
305
-
backgroundColor: select(t.name, {
306
-
light: t.palette.negative_100,
307
-
dim: t.palette.negative_200,
308
-
dark: t.palette.negative_200,
309
-
}),
310
})
311
} else {
312
baseStyles.push({
···
618
} else if (color === 'primary_subtle') {
619
if (!disabled) {
620
baseStyles.push({
621
-
color: select(t.name, {
622
-
light: t.palette.primary_600,
623
-
dim: t.palette.primary_800,
624
-
dark: t.palette.primary_800,
625
-
}),
626
})
627
} else {
628
baseStyles.push({
629
-
color: select(t.name, {
630
-
light: t.palette.primary_200,
631
-
dim: t.palette.primary_200,
632
-
dark: t.palette.primary_200,
633
-
}),
634
})
635
}
636
} else if (color === 'negative_subtle') {
637
if (!disabled) {
638
baseStyles.push({
639
-
color: select(t.name, {
640
-
light: t.palette.negative_600,
641
-
dim: t.palette.negative_800,
642
-
dark: t.palette.negative_800,
643
-
}),
644
})
645
} else {
646
baseStyles.push({
647
-
color: select(t.name, {
648
-
light: t.palette.negative_200,
649
-
dim: t.palette.negative_200,
650
-
dark: t.palette.negative_200,
651
-
}),
652
})
653
}
654
}
···
755
} else if (size === 'small') {
756
baseStyles.push(a.text_sm, a.leading_snug, a.font_medium)
757
} else if (size === 'tiny') {
758
-
baseStyles.push(a.text_xs, a.leading_snug, a.font_medium)
759
}
760
761
return StyleSheet.flatten(baseStyles)
···
869
</View>
870
)
871
}
···
274
} else if (color === 'primary_subtle') {
275
if (!disabled) {
276
baseStyles.push({
277
+
backgroundColor: t.palette.primary_50,
278
})
279
hoverStyles.push({
280
+
backgroundColor: t.palette.primary_100,
281
})
282
} else {
283
baseStyles.push({
···
287
} else if (color === 'negative_subtle') {
288
if (!disabled) {
289
baseStyles.push({
290
+
backgroundColor: t.palette.negative_50,
291
})
292
hoverStyles.push({
293
+
backgroundColor: t.palette.negative_100,
294
})
295
} else {
296
baseStyles.push({
···
602
} else if (color === 'primary_subtle') {
603
if (!disabled) {
604
baseStyles.push({
605
+
color: t.palette.primary_600,
606
})
607
} else {
608
baseStyles.push({
609
+
color: t.palette.primary_200,
610
})
611
}
612
} else if (color === 'negative_subtle') {
613
if (!disabled) {
614
baseStyles.push({
615
+
color: t.palette.negative_600,
616
})
617
} else {
618
baseStyles.push({
619
+
color: t.palette.negative_200,
620
})
621
}
622
}
···
723
} else if (size === 'small') {
724
baseStyles.push(a.text_sm, a.leading_snug, a.font_medium)
725
} else if (size === 'tiny') {
726
+
baseStyles.push(a.text_xs, a.leading_snug, a.font_semi_bold)
727
}
728
729
return StyleSheet.flatten(baseStyles)
···
837
</View>
838
)
839
}
840
+
841
+
export type StackedButtonProps = Omit<
842
+
ButtonProps,
843
+
keyof VariantProps | 'children'
844
+
> &
845
+
Pick<VariantProps, 'color'> & {
846
+
children: React.ReactNode
847
+
icon: React.ComponentType<SVGIconProps>
848
+
}
849
+
850
+
export function StackedButton({children, ...props}: StackedButtonProps) {
851
+
return (
852
+
<Button
853
+
{...props}
854
+
size="tiny"
855
+
style={[
856
+
a.flex_col,
857
+
{
858
+
height: 72,
859
+
paddingHorizontal: 16,
860
+
borderRadius: 20,
861
+
gap: 4,
862
+
},
863
+
props.style,
864
+
]}>
865
+
<StackedButtonInnerText icon={props.icon}>
866
+
{children}
867
+
</StackedButtonInnerText>
868
+
</Button>
869
+
)
870
+
}
871
+
872
+
function StackedButtonInnerText({
873
+
children,
874
+
icon: Icon,
875
+
}: Pick<StackedButtonProps, 'icon' | 'children'>) {
876
+
const textStyles = useSharedButtonTextStyles()
877
+
return (
878
+
<>
879
+
<Icon width={24} fill={textStyles.color} />
880
+
<ButtonText>{children}</ButtonText>
881
+
</>
882
+
)
883
+
}
+2
-2
src/components/Link.tsx
+2
-2
src/components/Link.tsx
···
165
if (isNative && screen !== 'NotFound') {
166
const state = navigation.getState()
167
// if screen is not in the current navigator, it means it's
168
+
// most likely a tab screen. note: state can be undefined
169
+
if (!state?.routeNames.includes(screen)) {
170
const parent = navigation.getParent()
171
if (
172
parent &&
-1
src/components/dialogs/StarterPackDialog.tsx
-1
src/components/dialogs/StarterPackDialog.tsx
+3
-1
src/components/moderation/LabelsOnMeDialog.tsx
+3
-1
src/components/moderation/LabelsOnMeDialog.tsx
+3
-1
src/components/moderation/ModerationDetailsDialog.tsx
+3
-1
src/components/moderation/ModerationDetailsDialog.tsx
+2
-1
src/components/moderation/ProfileHeaderAlerts.tsx
+2
-1
src/components/moderation/ProfileHeaderAlerts.tsx
+6
-3
src/components/moderation/ReportDialog/index.tsx
+6
-3
src/components/moderation/ReportDialog/index.tsx
···
219
<Admonition.Outer type="error">
220
<Admonition.Row>
221
<Admonition.Icon />
222
-
<Admonition.Text>
223
-
<Trans>Something went wrong, please try again</Trans>
224
-
</Admonition.Text>
225
<Admonition.Button
226
label={_(msg`Retry loading report options`)}
227
onPress={() => refetchLabelers()}>
228
<ButtonText>
···
219
<Admonition.Outer type="error">
220
<Admonition.Row>
221
<Admonition.Icon />
222
+
<Admonition.Content>
223
+
<Admonition.Text>
224
+
<Trans>Something went wrong, please try again</Trans>
225
+
</Admonition.Text>
226
+
</Admonition.Content>
227
<Admonition.Button
228
+
color="negative_subtle"
229
label={_(msg`Retry loading report options`)}
230
onPress={() => refetchLabelers()}>
231
<ButtonText>
+1
-1
src/lib/hooks/useIntentHandler.ts
+1
-1
src/lib/hooks/useIntentHandler.ts
···
51
}
52
53
const urlp = new URL(url)
54
-
const [_, intent, intentType] = urlp.pathname.split('/')
55
56
// On native, our links look like bluesky://intent/SomeIntent, so we have to check the hostname for the
57
// intent check. On web, we have to check the first part of the path since we have an actual hostname
···
51
}
52
53
const urlp = new URL(url)
54
+
const [__, intent, intentType] = urlp.pathname.split('/')
55
56
// On native, our links look like bluesky://intent/SomeIntent, so we have to check the hostname for the
57
// intent check. On web, we have to check the first part of the path since we have an actual hostname
+9
-4
src/lib/hooks/useOTAUpdates.ts
+9
-4
src/lib/hooks/useOTAUpdates.ts
···
10
useUpdates,
11
} from 'expo-updates'
12
13
import {logger} from '#/logger'
14
import {isIOS} from '#/platform/detection'
15
import {IS_TESTFLIGHT} from '#/env'
···
145
} else {
146
logger.debug('No update available.')
147
}
148
-
} catch (e) {
149
-
logger.error('OTA Update Error', {error: `${e}`})
150
}
151
}, 10e3)
152
}, [])
···
154
const onIsTestFlight = React.useCallback(async () => {
155
try {
156
await updateTestflight()
157
-
} catch (e: any) {
158
-
logger.error('Internal OTA Update Error', {error: `${e}`})
159
}
160
}, [])
161
···
10
useUpdates,
11
} from 'expo-updates'
12
13
+
import {isNetworkError} from '#/lib/strings/errors'
14
import {logger} from '#/logger'
15
import {isIOS} from '#/platform/detection'
16
import {IS_TESTFLIGHT} from '#/env'
···
146
} else {
147
logger.debug('No update available.')
148
}
149
+
} catch (err) {
150
+
if (!isNetworkError(err)) {
151
+
logger.error('OTA Update Error', {safeMessage: err})
152
+
}
153
}
154
}, 10e3)
155
}, [])
···
157
const onIsTestFlight = React.useCallback(async () => {
158
try {
159
await updateTestflight()
160
+
} catch (err: any) {
161
+
if (!isNetworkError(err)) {
162
+
logger.error('Internal OTA Update Error', {safeMessage: err})
163
+
}
164
}
165
}, [])
166
+11
-11
src/lib/strings/embed-player.ts
+11
-11
src/lib/strings/embed-player.ts
···
105
urlp.hostname === 'm.youtube.com' ||
106
urlp.hostname === 'music.youtube.com'
107
) {
108
-
const [_, page, shortOrLiveVideoId] = urlp.pathname.split('/')
109
110
const isShorts = page === 'shorts'
111
const isLive = page === 'live'
···
137
window.location.hostname
138
: 'localhost'
139
140
-
const [_, channelOrVideo, clipOrId, id] = urlp.pathname.split('/')
141
142
if (channelOrVideo === 'videos') {
143
return {
···
162
163
// spotify
164
if (urlp.hostname === 'open.spotify.com') {
165
-
const [_, typeOrLocale, idOrType, id] = urlp.pathname.split('/')
166
167
if (idOrType) {
168
if (typeOrLocale === 'playlist' || idOrType === 'playlist') {
···
210
urlp.hostname === 'soundcloud.com' ||
211
urlp.hostname === 'www.soundcloud.com'
212
) {
213
-
const [_, user, trackOrSets, set] = urlp.pathname.split('/')
214
215
if (user && trackOrSets) {
216
if (trackOrSets === 'sets' && set) {
···
270
}
271
272
if (urlp.hostname === 'vimeo.com' || urlp.hostname === 'www.vimeo.com') {
273
-
const [_, videoId] = urlp.pathname.split('/')
274
if (videoId) {
275
return {
276
type: 'vimeo_video',
···
281
}
282
283
if (urlp.hostname === 'giphy.com' || urlp.hostname === 'www.giphy.com') {
284
-
const [_, gifs, nameAndId] = urlp.pathname.split('/')
285
286
/*
287
* nameAndId is a string that consists of the name (dash separated) and the id of the gif (the last part of the name)
···
309
// These can include (presumably) a tracking id in the path name, so we have to check for that as well
310
if (giphyRegex.test(urlp.hostname)) {
311
// We can link directly to the gif, if its a proper link
312
-
const [_, media, trackingOrId, idOrFilename, filename] =
313
urlp.pathname.split('/')
314
315
if (media === 'media') {
···
338
// Finally, we should see if it is a link to i.giphy.com. These links don't necessarily end in .gif but can also
339
// be .webp
340
if (urlp.hostname === 'i.giphy.com' || urlp.hostname === 'www.i.giphy.com') {
341
-
const [_, mediaOrFilename, filename] = urlp.pathname.split('/')
342
343
if (mediaOrFilename === 'media' && filename) {
344
const gifId = filename.split('.')[0]
···
389
const path_components = urlp.pathname.slice(1, i + 1).split('/')
390
if (path_components.length === 4) {
391
// discard username - it's not relevant
392
-
const [photos, _, albums, id] = path_components
393
if (photos === 'photos' && albums === 'albums') {
394
// this at least has the shape of a valid photo-album URL!
395
return {
···
417
// link shortened flickr path
418
if (urlp.hostname === 'flic.kr') {
419
const b58alph = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'
420
-
let [_, type, idBase58Enc] = urlp.pathname.split('/')
421
let id = 0n
422
for (const char of idBase58Enc) {
423
const nextIdx = b58alph.indexOf(char)
···
528
return {success: false}
529
}
530
531
-
let [_, id, filename] = urlp.pathname.split('/')
532
533
if (!id || !filename) {
534
return {success: false}
···
105
urlp.hostname === 'm.youtube.com' ||
106
urlp.hostname === 'music.youtube.com'
107
) {
108
+
const [__, page, shortOrLiveVideoId] = urlp.pathname.split('/')
109
110
const isShorts = page === 'shorts'
111
const isLive = page === 'live'
···
137
window.location.hostname
138
: 'localhost'
139
140
+
const [__, channelOrVideo, clipOrId, id] = urlp.pathname.split('/')
141
142
if (channelOrVideo === 'videos') {
143
return {
···
162
163
// spotify
164
if (urlp.hostname === 'open.spotify.com') {
165
+
const [__, typeOrLocale, idOrType, id] = urlp.pathname.split('/')
166
167
if (idOrType) {
168
if (typeOrLocale === 'playlist' || idOrType === 'playlist') {
···
210
urlp.hostname === 'soundcloud.com' ||
211
urlp.hostname === 'www.soundcloud.com'
212
) {
213
+
const [__, user, trackOrSets, set] = urlp.pathname.split('/')
214
215
if (user && trackOrSets) {
216
if (trackOrSets === 'sets' && set) {
···
270
}
271
272
if (urlp.hostname === 'vimeo.com' || urlp.hostname === 'www.vimeo.com') {
273
+
const [__, videoId] = urlp.pathname.split('/')
274
if (videoId) {
275
return {
276
type: 'vimeo_video',
···
281
}
282
283
if (urlp.hostname === 'giphy.com' || urlp.hostname === 'www.giphy.com') {
284
+
const [__, gifs, nameAndId] = urlp.pathname.split('/')
285
286
/*
287
* nameAndId is a string that consists of the name (dash separated) and the id of the gif (the last part of the name)
···
309
// These can include (presumably) a tracking id in the path name, so we have to check for that as well
310
if (giphyRegex.test(urlp.hostname)) {
311
// We can link directly to the gif, if its a proper link
312
+
const [__, media, trackingOrId, idOrFilename, filename] =
313
urlp.pathname.split('/')
314
315
if (media === 'media') {
···
338
// Finally, we should see if it is a link to i.giphy.com. These links don't necessarily end in .gif but can also
339
// be .webp
340
if (urlp.hostname === 'i.giphy.com' || urlp.hostname === 'www.i.giphy.com') {
341
+
const [__, mediaOrFilename, filename] = urlp.pathname.split('/')
342
343
if (mediaOrFilename === 'media' && filename) {
344
const gifId = filename.split('.')[0]
···
389
const path_components = urlp.pathname.slice(1, i + 1).split('/')
390
if (path_components.length === 4) {
391
// discard username - it's not relevant
392
+
const [photos, __, albums, id] = path_components
393
if (photos === 'photos' && albums === 'albums') {
394
// this at least has the shape of a valid photo-album URL!
395
return {
···
417
// link shortened flickr path
418
if (urlp.hostname === 'flic.kr') {
419
const b58alph = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'
420
+
let [__, type, idBase58Enc] = urlp.pathname.split('/')
421
let id = 0n
422
for (const char of idBase58Enc) {
423
const nextIdx = b58alph.indexOf(char)
···
528
return {success: false}
529
}
530
531
+
let [__, id, filename] = urlp.pathname.split('/')
532
533
if (!id || !filename) {
534
return {success: false}
-17
src/lib/strings/helpers.ts
-17
src/lib/strings/helpers.ts
···
62
}, [splitter, maxCount, text])
63
}
64
65
-
// https://stackoverflow.com/a/52171480
66
-
export function toHashCode(str: string, seed = 0): number {
67
-
let h1 = 0xdeadbeef ^ seed,
68
-
h2 = 0x41c6ce57 ^ seed
69
-
for (let i = 0, ch; i < str.length; i++) {
70
-
ch = str.charCodeAt(i)
71
-
h1 = Math.imul(h1 ^ ch, 2654435761)
72
-
h2 = Math.imul(h2 ^ ch, 1597334677)
73
-
}
74
-
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507)
75
-
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909)
76
-
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507)
77
-
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909)
78
-
79
-
return 4294967296 * (2097151 & h2) + (h1 >>> 0)
80
-
}
81
-
82
export function countLines(str: string | undefined): number {
83
if (!str) return 0
84
return str.match(/\n/g)?.length ?? 0
+1
-1
src/lib/strings/starter-pack.ts
+1
-1
src/lib/strings/starter-pack.ts
+225
-215
src/locale/locales/en/messages.po
+225
-215
src/locale/locales/en/messages.po
···
124
msgid "{0, plural, other {# people have}} used this starter pack!"
125
msgstr ""
126
127
-
#: src/components/dialogs/StarterPackDialog.tsx:357
128
msgid "{0, plural, other {+# more}}"
129
msgstr ""
130
···
521
msgid "7 days"
522
msgstr ""
523
524
-
#: src/screens/Onboarding/StepFinished.tsx:341
525
msgid "A collection of popular feeds you can find on Bluesky, including News, Booksky, Game Dev, Blacksky, and Fountain Pens"
526
msgstr ""
527
···
568
msgid "Accept Request"
569
msgstr ""
570
571
#: src/screens/Settings/AccessibilitySettings.tsx:44
572
#: src/screens/Settings/Settings.tsx:220
573
#: src/screens/Settings/Settings.tsx:223
···
604
msgid "Account muted"
605
msgstr ""
606
607
-
#: src/components/moderation/ModerationDetailsDialog.tsx:103
608
#: src/lib/moderation/useModerationCauseDescription.ts:98
609
msgid "Account Muted"
610
msgstr ""
611
612
-
#: src/components/moderation/ModerationDetailsDialog.tsx:89
613
msgid "Account Muted by List"
614
msgstr ""
615
···
654
655
#: src/components/dialogs/lists/ListAddRemoveUsersDialog.tsx:169
656
#: src/components/dialogs/MutedWords.tsx:333
657
-
#: src/components/dialogs/StarterPackDialog.tsx:371
658
-
#: src/components/dialogs/StarterPackDialog.tsx:377
659
#: src/view/com/modals/UserAddRemoveLists.tsx:235
660
msgid "Add"
661
msgstr ""
···
708
msgid "Add another account"
709
msgstr ""
710
711
-
#: src/view/com/composer/Composer.tsx:811
712
msgid "Add another post"
713
msgstr ""
714
715
-
#: src/view/com/composer/Composer.tsx:1444
716
msgid "Add another post to thread"
717
msgstr ""
718
···
735
msgid "Add media to post"
736
msgstr ""
737
738
-
#: src/components/moderation/ReportDialog/index.tsx:403
739
-
#: src/components/moderation/ReportDialog/index.tsx:407
740
msgid "Add more details (optional)"
741
msgstr ""
742
···
790
msgid "Add to saved posts"
791
msgstr ""
792
793
-
#: src/components/dialogs/StarterPackDialog.tsx:176
794
#: src/view/com/profile/ProfileMenu.tsx:308
795
#: src/view/com/profile/ProfileMenu.tsx:311
796
msgid "Add to starter packs"
···
805
msgid "Added to list"
806
msgstr ""
807
808
-
#: src/components/dialogs/StarterPackDialog.tsx:258
809
msgid "Added to starter pack"
810
msgstr ""
811
···
813
msgid "Additional details (limit 1000 characters)"
814
msgstr ""
815
816
-
#: src/components/moderation/ReportDialog/index.tsx:421
817
msgid "Additional details (limit 300 characters)"
818
msgstr ""
819
···
879
#: src/screens/Search/components/SearchLanguageDropdown.tsx:64
880
#: src/screens/Search/components/SearchLanguageDropdown.tsx:99
881
#: src/screens/Search/components/SearchLanguageDropdown.tsx:101
882
-
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:216
883
msgid "All languages"
884
msgstr ""
885
···
903
msgstr ""
904
905
#: src/screens/Settings/ActivityPrivacySettings.tsx:52
906
-
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:92
907
msgid "Allow others to be notified of your posts"
908
msgstr ""
909
···
967
msgstr ""
968
969
#: src/components/dialogs/GifSelect.tsx:253
970
-
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:298
971
msgid "An error has occurred"
972
msgstr ""
973
···
1012
msgid "An error occurred while uploading the video."
1013
msgstr ""
1014
1015
-
#: src/screens/Onboarding/StepFinished.tsx:359
1016
msgid "An illustration of several Bluesky posts alongside repost, like, and comment icons"
1017
msgstr ""
1018
···
1051
msgid "an unknown error occurred"
1052
msgstr ""
1053
1054
-
#: src/components/moderation/ModerationDetailsDialog.tsx:134
1055
#: src/lib/moderation/useModerationCauseDescription.ts:144
1056
msgid "an unknown labeler"
1057
msgstr ""
···
1089
1090
#: src/screens/Settings/ActivityPrivacySettings.tsx:111
1091
#: src/screens/Settings/ActivityPrivacySettings.tsx:116
1092
-
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:163
1093
msgid "Anyone who follows me"
1094
msgstr ""
1095
···
1125
msgid "App password names must be at least 4 characters long"
1126
msgstr ""
1127
1128
-
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:72
1129
-
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:75
1130
msgid "App passwords"
1131
msgstr ""
1132
···
1135
msgid "App Passwords"
1136
msgstr ""
1137
1138
-
#: src/components/moderation/LabelsOnMeDialog.tsx:150
1139
-
#: src/components/moderation/LabelsOnMeDialog.tsx:153
1140
msgid "Appeal"
1141
msgstr ""
1142
1143
-
#: src/components/moderation/LabelsOnMeDialog.tsx:268
1144
msgid "Appeal \"{0}\" label"
1145
msgstr ""
1146
1147
-
#: src/components/moderation/LabelsOnMeDialog.tsx:258
1148
#: src/screens/Messages/components/ChatDisabled.tsx:103
1149
msgctxt "toast"
1150
msgid "Appeal submitted"
···
1221
msgid "Are you sure you want to remove this from your feeds?"
1222
msgstr ""
1223
1224
-
#: src/view/com/composer/Composer.tsx:760
1225
msgid "Are you sure you'd like to discard this draft?"
1226
msgstr ""
1227
1228
-
#: src/view/com/composer/Composer.tsx:950
1229
msgid "Are you sure you'd like to discard this post?"
1230
msgstr ""
1231
···
1233
msgid "Are you sure?"
1234
msgstr ""
1235
1236
-
#: src/view/com/composer/select-language/SuggestedLanguage.tsx:89
1237
msgid "Are you writing in <0>{suggestedLanguageName}</0>?"
1238
msgstr ""
1239
···
1274
msgid "Available"
1275
msgstr ""
1276
1277
-
#: src/components/moderation/LabelsOnMeDialog.tsx:315
1278
-
#: src/components/moderation/LabelsOnMeDialog.tsx:316
1279
#: src/screens/Login/ChooseAccountForm.tsx:90
1280
#: src/screens/Login/ChooseAccountForm.tsx:95
1281
#: src/screens/Login/ForgotPasswordForm.tsx:123
···
1307
msgid "Before creating a post or replying, you must first verify your email."
1308
msgstr ""
1309
1310
-
#: src/components/dialogs/StarterPackDialog.tsx:71
1311
#: src/components/StarterPack/ProfileStarterPacks.tsx:231
1312
#: src/components/StarterPack/ProfileStarterPacks.tsx:241
1313
msgid "Before creating a starter pack, you must first verify your email."
···
1558
msgstr ""
1559
1560
#: src/components/LabelingServiceCard/index.tsx:62
1561
-
#: src/components/moderation/ReportDialog/index.tsx:683
1562
#: src/screens/Search/components/StarterPackCard.tsx:106
1563
#: src/screens/Search/Explore.tsx:930
1564
msgid "By {0}"
···
1626
#: src/screens/Settings/Settings.tsx:289
1627
#: src/screens/Takendown.tsx:108
1628
#: src/screens/Takendown.tsx:111
1629
-
#: src/view/com/composer/Composer.tsx:1005
1630
-
#: src/view/com/composer/Composer.tsx:1016
1631
#: src/view/com/composer/photos/EditImageDialog.web.tsx:43
1632
#: src/view/com/composer/photos/EditImageDialog.web.tsx:52
1633
#: src/view/shell/desktop/LeftNav.tsx:213
···
1695
msgid "Change Handle"
1696
msgstr ""
1697
1698
-
#: src/components/moderation/ReportDialog/index.tsx:325
1699
msgid "Change moderation service"
1700
msgstr ""
1701
···
1708
msgid "Change password dialog"
1709
msgstr ""
1710
1711
-
#: src/view/com/composer/select-language/SuggestedLanguage.tsx:100
1712
-
msgid "Change post language to {suggestedLanguageName}"
1713
-
msgstr ""
1714
-
1715
-
#: src/components/moderation/ReportDialog/index.tsx:244
1716
msgid "Change report reason"
1717
msgstr ""
1718
···
1823
msgid "Choose People"
1824
msgstr ""
1825
1826
-
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:162
1827
msgid "Choose Post Languages"
1828
msgstr ""
1829
1830
-
#: src/screens/Onboarding/StepFinished.tsx:575
1831
msgid "Choose the algorithms that power your custom feeds."
1832
msgstr ""
1833
···
1920
#: src/components/dialogs/nuxs/InitialVerificationAnnouncement.tsx:178
1921
#: src/components/dialogs/nuxs/InitialVerificationAnnouncement.tsx:187
1922
#: src/components/dialogs/SearchablePeopleList.tsx:295
1923
-
#: src/components/dialogs/StarterPackDialog.tsx:179
1924
#: src/components/dms/EmojiPopup.android.tsx:58
1925
#: src/components/dms/ReportDialog.tsx:387
1926
#: src/components/dms/ReportDialog.tsx:396
···
1938
#: src/components/WhoCanReply.tsx:209
1939
#: src/screens/Settings/components/ChangePasswordDialog.tsx:286
1940
#: src/screens/Settings/components/ChangePasswordDialog.tsx:291
1941
-
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:313
1942
#: src/view/com/feeds/MissingFeed.tsx:210
1943
#: src/view/com/feeds/MissingFeed.tsx:217
1944
msgid "Close"
···
1962
#: src/components/dialogs/GifSelect.tsx:263
1963
#: src/components/verification/VerificationsDialog.tsx:136
1964
#: src/components/verification/VerifierDialog.tsx:142
1965
-
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:182
1966
-
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:276
1967
-
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:308
1968
msgid "Close dialog"
1969
msgstr ""
1970
···
2007
msgid "Closes password update alert"
2008
msgstr ""
2009
2010
-
#: src/view/com/composer/Composer.tsx:1013
2011
msgid "Closes post composer and discards post draft"
2012
msgstr ""
2013
···
2053
msgid "Community Guidelines"
2054
msgstr ""
2055
2056
-
#: src/screens/Onboarding/StepFinished.tsx:473
2057
-
#: src/screens/Onboarding/StepFinished.tsx:588
2058
msgid "Complete onboarding and start using your account"
2059
msgstr ""
2060
···
2066
msgid "Compose new post"
2067
msgstr ""
2068
2069
-
#: src/view/com/composer/Composer.tsx:914
2070
msgid "Compose posts up to {0, plural, other {# characters}} in length"
2071
msgstr ""
2072
···
2074
msgid "Compose reply"
2075
msgstr ""
2076
2077
-
#: src/view/com/composer/Composer.tsx:1834
2078
msgid "Compressing video..."
2079
msgstr ""
2080
···
2186
msgid "Content Languages"
2187
msgstr ""
2188
2189
-
#: src/components/moderation/ModerationDetailsDialog.tsx:82
2190
#: src/lib/moderation/useModerationCauseDescription.ts:82
2191
msgid "Content Not Available"
2192
msgstr ""
2193
2194
-
#: src/components/moderation/ModerationDetailsDialog.tsx:50
2195
#: src/components/moderation/ScreenHider.tsx:99
2196
#: src/lib/moderation/useGlobalLabelStrings.ts:22
2197
#: src/lib/moderation/useModerationCauseDescription.ts:45
···
2411
2412
#. Text on button to create a new starter pack
2413
#. Text on button to create a new starter pack
2414
-
#: src/components/dialogs/StarterPackDialog.tsx:112
2415
-
#: src/components/dialogs/StarterPackDialog.tsx:201
2416
#: src/components/StarterPack/ProfileStarterPacks.tsx:296
2417
msgid "Create"
2418
msgstr ""
···
2478
msgid "Create new account"
2479
msgstr ""
2480
2481
-
#: src/components/moderation/ReportDialog/index.tsx:585
2482
#: src/components/ReportDialog/SelectReportOptionView.tsx:102
2483
msgid "Create report for {0}"
2484
msgstr ""
2485
2486
-
#: src/components/dialogs/StarterPackDialog.tsx:107
2487
-
#: src/components/dialogs/StarterPackDialog.tsx:196
2488
msgid "Create starter pack"
2489
msgstr ""
2490
···
2639
2640
#: src/components/PostControls/PostMenu/PostMenuItems.tsx:685
2641
#: src/components/PostControls/PostMenu/PostMenuItems.tsx:687
2642
-
#: src/view/com/composer/Composer.tsx:924
2643
msgid "Delete post"
2644
msgstr ""
2645
···
2752
2753
#: src/components/dialogs/lists/CreateOrEditListDialog.tsx:92
2754
#: src/screens/Profile/Header/EditProfileDialog.tsx:82
2755
-
#: src/view/com/composer/Composer.tsx:762
2756
-
#: src/view/com/composer/Composer.tsx:957
2757
msgid "Discard"
2758
msgstr ""
2759
···
2762
msgid "Discard changes?"
2763
msgstr ""
2764
2765
-
#: src/view/com/composer/Composer.tsx:759
2766
msgid "Discard draft?"
2767
msgstr ""
2768
2769
-
#: src/view/com/composer/Composer.tsx:949
2770
msgid "Discard post?"
2771
msgstr ""
2772
···
2789
msgid "Dismiss"
2790
msgstr ""
2791
2792
-
#: src/view/com/composer/Composer.tsx:1758
2793
msgid "Dismiss error"
2794
msgstr ""
2795
···
2815
msgid "Display name"
2816
msgstr ""
2817
2818
-
#: src/screens/Onboarding/StepFinished.tsx:347
2819
msgid "Ditch the trolls and clickbait. Find real people and conversations that matter to you."
2820
msgstr ""
2821
···
2868
#: src/view/com/auth/server-input/index.tsx:233
2869
#: src/view/com/composer/labels/LabelsBtn.tsx:223
2870
#: src/view/com/composer/labels/LabelsBtn.tsx:230
2871
-
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:281
2872
#: src/view/com/composer/videos/SubtitleDialog.tsx:168
2873
#: src/view/com/composer/videos/SubtitleDialog.tsx:178
2874
msgid "Done"
···
3055
msgid "Email 2FA disabled"
3056
msgstr ""
3057
3058
-
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:63
3059
msgid "Email 2FA enabled"
3060
msgstr ""
3061
···
3126
msgstr ""
3127
3128
#: src/screens/Settings/NotificationSettings/ActivityNotificationSettings.tsx:139
3129
msgid "Enable notifications for an account by visiting their profile and pressing the <0>bell icon</0> <1/>."
3130
msgstr ""
3131
···
3222
msgid "Entertainment"
3223
msgstr ""
3224
3225
-
#: src/view/com/composer/Composer.tsx:1843
3226
#: src/view/com/util/error/ErrorScreen.tsx:42
3227
msgid "Error"
3228
msgstr ""
···
3231
msgid "Error loading post"
3232
msgstr ""
3233
3234
-
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:154
3235
msgid "Error loading preference"
3236
msgstr ""
3237
···
3345
msgid "Expires {0}"
3346
msgstr ""
3347
3348
-
#: src/components/moderation/LabelsOnMeDialog.tsx:199
3349
-
#: src/components/moderation/ModerationDetailsDialog.tsx:208
3350
msgid "Expires in {0}"
3351
msgstr ""
3352
···
3414
msgid "Failed to add emoji reaction"
3415
msgstr ""
3416
3417
-
#: src/components/dialogs/StarterPackDialog.tsx:270
3418
msgid "Failed to add to starter pack"
3419
msgstr ""
3420
···
3520
msgid "Failed to remove emoji reaction"
3521
msgstr ""
3522
3523
-
#: src/components/dialogs/StarterPackDialog.tsx:289
3524
msgid "Failed to remove from starter pack"
3525
msgstr ""
3526
···
3554
msgid "Failed to send email, please try again."
3555
msgstr ""
3556
3557
-
#: src/components/moderation/LabelsOnMeDialog.tsx:254
3558
#: src/screens/Messages/components/ChatDisabled.tsx:99
3559
msgid "Failed to submit appeal, please try again."
3560
msgstr ""
···
3697
msgid "Filter who you receive notifications from"
3698
msgstr ""
3699
3700
-
#: src/screens/Onboarding/StepFinished.tsx:479
3701
-
#: src/screens/Onboarding/StepFinished.tsx:591
3702
msgid "Finalizing"
3703
msgstr ""
3704
···
3718
msgid "Find people to follow"
3719
msgstr ""
3720
3721
-
#: src/screens/Search/Shell.tsx:476
3722
msgid "Find posts, users, and feeds on Bluesky"
3723
msgstr ""
3724
3725
-
#: src/screens/Onboarding/StepFinished.tsx:345
3726
msgid "Find your people"
3727
msgstr ""
3728
···
3749
msgid "Flat White"
3750
msgstr ""
3751
3752
-
#: src/screens/Onboarding/StepFinished.tsx:571
3753
msgid "Flexible"
3754
msgstr ""
3755
···
3923
msgid "Forever"
3924
msgstr ""
3925
3926
-
#: src/screens/Onboarding/StepFinished.tsx:354
3927
msgid "Forget the noise"
3928
msgstr ""
3929
···
3940
msgid "Forgot?"
3941
msgstr ""
3942
3943
-
#: src/screens/Onboarding/StepFinished.tsx:336
3944
msgid "Free your feed"
3945
msgstr ""
3946
···
4418
msgstr ""
4419
4420
#: src/screens/Settings/NotificationSettings/ActivityNotificationSettings.tsx:157
4421
msgid "If you want to restrict who can receive notifications for your account's activity, you can change this in <0>Settings โ Privacy and Security</0>."
4422
msgstr ""
4423
···
4596
msgid "It's just you right now! Add more people to your starter pack by searching above."
4597
msgstr ""
4598
4599
-
#: src/view/com/composer/Composer.tsx:1777
4600
msgid "Job ID: {0}"
4601
msgstr ""
4602
···
4660
msgid "Labels are annotations on users and content. They can be used to hide, warn, and categorize the network."
4661
msgstr ""
4662
4663
-
#: src/components/moderation/LabelsOnMeDialog.tsx:72
4664
msgid "Labels on your account"
4665
msgstr ""
4666
4667
-
#: src/components/moderation/LabelsOnMeDialog.tsx:74
4668
msgid "Labels on your content"
4669
msgstr ""
4670
···
4746
msgid "Learn more about verification on Bluesky"
4747
msgstr ""
4748
4749
-
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:128
4750
-
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:131
4751
msgid "Learn more about what is public on Bluesky."
4752
msgstr ""
4753
···
4799
msgid "Let's get your password reset!"
4800
msgstr ""
4801
4802
-
#: src/screens/Onboarding/StepFinished.tsx:481
4803
-
#: src/screens/Onboarding/StepFinished.tsx:591
4804
msgid "Let's go!"
4805
msgstr ""
4806
···
4852
4853
#: src/screens/Post/PostLikedBy.tsx:41
4854
#: src/screens/Profile/ProfileLabelerLikedBy.tsx:32
4855
-
#: src/view/screens/ProfileFeedLikedBy.tsx:34
4856
msgid "Liked By"
4857
msgstr ""
4858
···
5031
msgid "Log"
5032
msgstr ""
5033
5034
-
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:107
5035
msgid "Logged-out visibility"
5036
msgstr ""
5037
···
5205
msgid "Moderation"
5206
msgstr ""
5207
5208
-
#: src/components/moderation/ModerationDetailsDialog.tsx:138
5209
msgid "Moderation details"
5210
msgstr ""
5211
···
5254
msgid "Moderation tools"
5255
msgstr ""
5256
5257
-
#: src/components/moderation/ModerationDetailsDialog.tsx:52
5258
#: src/lib/moderation/useModerationCauseDescription.ts:47
5259
msgid "Moderator has chosen to set a general warning on the content."
5260
msgstr ""
···
5264
msgid "More feeds"
5265
msgstr ""
5266
5267
-
#: src/view/com/composer/select-language/PostLanguageSelect.tsx:70
5268
-
#: src/view/com/composer/select-language/PostLanguageSelect.tsx:73
5269
msgid "More languages..."
5270
msgstr ""
5271
···
5370
msgstr ""
5371
5372
#: src/Navigation.tsx:187
5373
-
#: src/view/screens/ModerationMutedAccounts.tsx:118
5374
msgid "Muted Accounts"
5375
msgstr ""
5376
5377
-
#: src/view/screens/ModerationMutedAccounts.tsx:204
5378
msgid "Muted accounts have their posts removed from your feed and from your notifications. Mutes are completely private."
5379
msgstr ""
5380
···
5437
msgid "Navigates to your profile"
5438
msgstr ""
5439
5440
-
#: src/components/moderation/ReportDialog/index.tsx:271
5441
-
#: src/components/moderation/ReportDialog/index.tsx:288
5442
msgid "Need to report a copyright violation, legal request, or regulatory compliance issue?"
5443
msgstr ""
5444
···
5446
msgid "Need to report a copyright violation?"
5447
msgstr ""
5448
5449
-
#: src/screens/Onboarding/StepFinished.tsx:559
5450
msgid "Never lose access to your followers or data."
5451
msgstr ""
5452
···
5546
msgid "New posts from {firstAuthorName} and {additionalAuthorsCount, plural, one {{formattedAuthorsCount} other} other {{formattedAuthorsCount} others}}"
5547
msgstr ""
5548
5549
-
#: src/components/dialogs/StarterPackDialog.tsx:193
5550
msgid "New starter pack"
5551
msgstr ""
5552
···
5573
#: src/screens/Login/LoginForm.tsx:350
5574
#: src/screens/Login/SetNewPasswordForm.tsx:182
5575
#: src/screens/Login/SetNewPasswordForm.tsx:188
5576
-
#: src/screens/Onboarding/StepFinished.tsx:474
5577
-
#: src/screens/Onboarding/StepFinished.tsx:483
5578
#: src/screens/Settings/components/AddAppPasswordDialog.tsx:157
5579
#: src/screens/Settings/components/AddAppPasswordDialog.tsx:165
5580
#: src/screens/Signup/BackNextButtons.tsx:67
···
5594
msgid "Next image"
5595
msgstr ""
5596
5597
-
#: src/screens/Onboarding/StepFinished.tsx:356
5598
msgid "No ads, no invasive tracking, no engagement traps. Bluesky respects your time and attention."
5599
msgstr ""
5600
···
5637
msgid "No messages yet"
5638
msgstr ""
5639
5640
-
#: src/screens/Onboarding/StepFinished.tsx:338
5641
msgid "No more doomscrolling junk-filled algorithms. Find feeds that work for you, not against you."
5642
msgstr ""
5643
···
5649
#: src/screens/Messages/Settings.tsx:109
5650
#: src/screens/Settings/ActivityPrivacySettings.tsx:129
5651
#: src/screens/Settings/ActivityPrivacySettings.tsx:134
5652
-
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:160
5653
msgid "No one"
5654
msgstr ""
5655
···
5760
msgid "Note about sharing"
5761
msgstr ""
5762
5763
-
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:117
5764
msgid "Note: Bluesky is an open and public network. This setting only limits the visibility of your content on the Bluesky app and website, and other apps may not respect this setting. Your content may still be shown to logged-out users by other apps and websites."
5765
msgstr ""
5766
···
5841
msgstr ""
5842
5843
#: src/components/dialogs/GifSelect.tsx:256
5844
-
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:301
5845
#: src/view/com/util/ErrorBoundary.tsx:57
5846
msgid "Oh no!"
5847
msgstr ""
···
5878
msgid "Onboarding reset"
5879
msgstr ""
5880
5881
-
#: src/view/com/composer/Composer.tsx:359
5882
msgid "One or more GIFs is missing alt text."
5883
msgstr ""
5884
5885
-
#: src/view/com/composer/Composer.tsx:356
5886
msgid "One or more images is missing alt text."
5887
msgstr ""
5888
···
5894
msgid "One or more of your selected files are too large. Maximum size is 100ย MB."
5895
msgstr ""
5896
5897
-
#: src/view/com/composer/Composer.tsx:366
5898
msgid "One or more videos is missing alt text."
5899
msgstr ""
5900
···
5908
5909
#: src/screens/Settings/ActivityPrivacySettings.tsx:120
5910
#: src/screens/Settings/ActivityPrivacySettings.tsx:125
5911
-
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:158
5912
msgid "Only followers who I follow"
5913
msgstr ""
5914
···
5933
msgid "Oops!"
5934
msgstr ""
5935
5936
-
#: src/screens/Onboarding/StepFinished.tsx:555
5937
msgid "Open"
5938
msgstr ""
5939
···
5951
msgstr ""
5952
5953
#: src/screens/Messages/components/MessageInput.web.tsx:181
5954
-
#: src/view/com/composer/Composer.tsx:1429
5955
msgid "Open emoji picker"
5956
msgstr ""
5957
···
6051
msgid "Opens device gallery to select up to {MAX_IMAGES, plural, other {# images}}, or a single video or GIF."
6052
msgstr ""
6053
6054
-
#: src/view/com/composer/Composer.tsx:1430
6055
msgid "Opens emoji picker"
6056
msgstr ""
6057
···
6085
msgid "Opens password reset form"
6086
msgstr ""
6087
6088
-
#: src/view/com/composer/select-language/PostLanguageSelect.tsx:103
6089
msgid "Opens post language settings"
6090
msgstr ""
6091
···
6411
msgid "Please enter your username"
6412
msgstr ""
6413
6414
-
#: src/components/moderation/LabelsOnMeDialog.tsx:290
6415
msgid "Please explain why you think this label was incorrectly applied by {0}"
6416
msgstr ""
6417
···
6463
msgid "Porn"
6464
msgstr ""
6465
6466
-
#: src/screens/PostThread/index.tsx:502
6467
msgctxt "description"
6468
msgid "Post"
6469
msgstr ""
6470
6471
-
#: src/view/com/composer/Composer.tsx:1076
6472
msgctxt "action"
6473
msgid "Post"
6474
msgstr ""
6475
6476
-
#: src/view/com/composer/Composer.tsx:1074
6477
msgctxt "action"
6478
msgid "Post All"
6479
msgstr ""
···
6505
msgid "Post has been deleted"
6506
msgstr ""
6507
6508
-
#: src/components/moderation/ModerationDetailsDialog.tsx:107
6509
#: src/lib/moderation/useModerationCauseDescription.ts:106
6510
msgid "Post Hidden by Muted Word"
6511
msgstr ""
6512
6513
-
#: src/components/moderation/ModerationDetailsDialog.tsx:110
6514
#: src/lib/moderation/useModerationCauseDescription.ts:115
6515
msgid "Post Hidden by You"
6516
msgstr ""
···
6525
msgstr ""
6526
6527
#. Accessibility label for button that opens dialog to choose post language settings
6528
-
#: src/view/com/composer/select-language/PostLanguageSelect.tsx:98
6529
msgid "Post language selection"
6530
msgstr ""
6531
···
6618
#: src/Navigation.tsx:407
6619
#: src/Navigation.tsx:415
6620
#: src/screens/Settings/ActivityPrivacySettings.tsx:40
6621
-
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:45
6622
msgid "Privacy and Security"
6623
msgstr ""
6624
6625
#: src/screens/Settings/NotificationSettings/ActivityNotificationSettings.tsx:161
6626
msgid "Privacy and Security settings"
6627
msgstr ""
6628
···
6637
msgid "Privacy Policy"
6638
msgstr ""
6639
6640
-
#: src/view/com/composer/Composer.tsx:1840
6641
msgid "Processing video..."
6642
msgstr ""
6643
···
6663
msgid "Profile updated"
6664
msgstr ""
6665
6666
-
#: src/screens/Onboarding/StepFinished.tsx:541
6667
msgid "Public"
6668
msgstr ""
6669
···
6676
msgstr ""
6677
6678
#. Accessibility label for button to publish a single post
6679
-
#: src/view/com/composer/Composer.tsx:1056
6680
msgid "Publish post"
6681
msgstr ""
6682
6683
#. Accessibility label for button to publish multiple posts in a thread
6684
-
#: src/view/com/composer/Composer.tsx:1049
6685
msgid "Publish posts"
6686
msgstr ""
6687
6688
#. Accessibility label for button to publish multiple replies in a thread
6689
-
#: src/view/com/composer/Composer.tsx:1034
6690
msgid "Publish replies"
6691
msgstr ""
6692
6693
#. Accessibility label for button to publish a single reply
6694
-
#: src/view/com/composer/Composer.tsx:1041
6695
msgid "Publish reply"
6696
msgstr ""
6697
···
6850
msgid "Recent Searches"
6851
msgstr ""
6852
6853
-
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:210
6854
msgid "Recently used"
6855
msgstr ""
6856
···
6878
6879
#: src/components/dialogs/lists/ListAddRemoveUsersDialog.tsx:171
6880
#: src/components/dialogs/MutedWords.tsx:443
6881
-
#: src/components/dialogs/StarterPackDialog.tsx:371
6882
-
#: src/components/dialogs/StarterPackDialog.tsx:377
6883
#: src/components/FeedCard.tsx:343
6884
#: src/components/StarterPack/Wizard/WizardListCard.tsx:104
6885
#: src/components/StarterPack/Wizard/WizardListCard.tsx:111
···
7024
msgid "Removed from saved posts"
7025
msgstr ""
7026
7027
-
#: src/components/dialogs/StarterPackDialog.tsx:277
7028
msgid "Removed from starter pack"
7029
msgstr ""
7030
···
7081
msgid "Replies to this post are disabled."
7082
msgstr ""
7083
7084
-
#: src/view/com/composer/Composer.tsx:1072
7085
msgctxt "action"
7086
msgid "Reply"
7087
msgstr ""
···
7091
msgid "Reply ({0, plural, one {# reply} other {# replies}})"
7092
msgstr ""
7093
7094
-
#: src/components/moderation/ModerationDetailsDialog.tsx:116
7095
#: src/lib/moderation/useModerationCauseDescription.ts:125
7096
msgid "Reply Hidden by Thread Author"
7097
msgstr ""
7098
7099
-
#: src/components/moderation/ModerationDetailsDialog.tsx:115
7100
#: src/lib/moderation/useModerationCauseDescription.ts:124
7101
msgid "Reply Hidden by You"
7102
msgstr ""
···
7345
#: src/components/dms/MessageItem.tsx:322
7346
#: src/components/Error.tsx:65
7347
#: src/components/Lists.tsx:110
7348
-
#: src/components/moderation/ReportDialog/index.tsx:229
7349
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx:55
7350
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx:58
7351
#: src/components/StarterPack/ProfileStarterPacks.tsx:342
···
7363
#: src/screens/Signup/BackNextButtons.tsx:53
7364
#: src/view/com/util/error/ErrorMessage.tsx:60
7365
#: src/view/com/util/error/ErrorScreen.tsx:97
7366
msgid "Retry"
7367
msgstr ""
7368
7369
-
#: src/components/moderation/ReportDialog/index.tsx:226
7370
msgid "Retry loading report options"
7371
msgstr ""
7372
···
7496
#: src/components/forms/SearchInput.tsx:34
7497
#: src/components/forms/SearchInput.tsx:36
7498
#: src/screens/Search/Shell.tsx:307
7499
-
#: src/screens/Search/Shell.tsx:464
7500
#: src/view/shell/bottom-bar/BottomBar.tsx:198
7501
msgid "Search"
7502
msgstr ""
···
7556
msgid "Search is currently unavailable when logged out"
7557
msgstr ""
7558
7559
-
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:193
7560
-
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:194
7561
msgid "Search languages"
7562
msgstr ""
7563
···
7639
msgid "Select {0}"
7640
msgstr ""
7641
7642
-
#: src/view/com/composer/select-language/PostLanguageSelect.tsx:58
7643
msgid "Select {langName}"
7644
msgstr ""
7645
···
7705
msgstr ""
7706
7707
#: src/screens/Settings/LanguageSettings.tsx:178
7708
-
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:226
7709
msgid "Select languages"
7710
msgstr ""
7711
7712
-
#: src/components/moderation/ReportDialog/index.tsx:310
7713
msgid "Select moderation service"
7714
msgstr ""
7715
···
7717
msgid "Select moderator"
7718
msgstr ""
7719
7720
-
#: src/view/com/composer/select-language/PostLanguageSelect.tsx:45
7721
msgid "Select post language"
7722
msgstr ""
7723
···
7738
msgid "Select the moderation service(s) to report to"
7739
msgstr ""
7740
7741
-
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:172
7742
msgid "Select up to 3 languages used in this post"
7743
msgstr ""
7744
···
7800
msgid "Send message"
7801
msgstr ""
7802
7803
-
#: src/components/PostControls/ShareMenu/RecentChats.tsx:123
7804
msgid "Send post to {name}"
7805
msgstr ""
7806
···
7819
msgid "Send report to {0}"
7820
msgstr ""
7821
7822
-
#: src/components/moderation/ReportDialog/index.tsx:649
7823
msgid "Send report to {title}"
7824
msgstr ""
7825
···
7875
msgid "Settings for activity from others"
7876
msgstr ""
7877
7878
-
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:85
7879
msgid "Settings for allowing others to be notified of your posts"
7880
msgstr ""
7881
···
8206
msgid "Similar accounts"
8207
msgstr ""
8208
8209
-
#: src/screens/Onboarding/StepFinished.tsx:380
8210
-
#: src/screens/Onboarding/StepFinished.tsx:462
8211
#: src/screens/Onboarding/StepInterests/index.tsx:240
8212
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:222
8213
#: src/screens/StarterPack/Wizard/index.tsx:218
8214
msgid "Skip"
8215
msgstr ""
8216
8217
-
#: src/screens/Onboarding/StepFinished.tsx:373
8218
-
#: src/screens/Onboarding/StepFinished.tsx:459
8219
msgid "Skip introduction and start using your account"
8220
msgstr ""
8221
···
8270
msgstr ""
8271
8272
#: src/components/ageAssurance/AgeAssuranceInitDialog.tsx:138
8273
-
#: src/components/moderation/ReportDialog/index.tsx:223
8274
#: src/screens/Deactivated.tsx:94
8275
#: src/screens/Settings/components/DeactivateAccountDialog.tsx:59
8276
msgid "Something went wrong, please try again"
8277
msgstr ""
8278
···
8298
msgid "Something wrong? Let us know."
8299
msgstr ""
8300
8301
-
#: src/App.native.tsx:125
8302
-
#: src/App.web.tsx:101
8303
msgid "Sorry! Your session expired. Please sign in again."
8304
msgstr ""
8305
···
8315
msgid "Sort replies to the same post by:"
8316
msgstr ""
8317
8318
-
#: src/components/moderation/LabelsOnMeDialog.tsx:178
8319
-
#: src/components/moderation/ModerationDetailsDialog.tsx:186
8320
msgid "Source: <0>{sourceName}</0>"
8321
msgstr ""
8322
···
8338
msgid "Sports"
8339
msgstr ""
8340
8341
-
#: src/components/PostControls/ShareMenu/RecentChats.tsx:208
8342
msgid "Start a conversation, and it will appear here."
8343
msgstr ""
8344
···
8412
8413
#: src/components/ageAssurance/AgeAssuranceAppealDialog.tsx:117
8414
#: src/components/ageAssurance/AgeAssuranceAppealDialog.tsx:123
8415
-
#: src/components/moderation/LabelsOnMeDialog.tsx:324
8416
-
#: src/components/moderation/LabelsOnMeDialog.tsx:325
8417
#: src/screens/Messages/components/ChatDisabled.tsx:154
8418
#: src/screens/Messages/components/ChatDisabled.tsx:155
8419
msgid "Submit"
···
8427
msgid "Submit Appeal"
8428
msgstr ""
8429
8430
-
#: src/components/moderation/ReportDialog/index.tsx:387
8431
-
#: src/components/moderation/ReportDialog/index.tsx:444
8432
-
#: src/components/moderation/ReportDialog/index.tsx:451
8433
msgid "Submit report"
8434
msgstr ""
8435
···
8630
msgid "Text field"
8631
msgstr ""
8632
8633
-
#: src/components/moderation/LabelsOnMeDialog.tsx:288
8634
#: src/screens/Messages/components/ChatDisabled.tsx:120
8635
msgid "Text input field"
8636
msgstr ""
···
8687
msgid "The app will be restarted"
8688
msgstr ""
8689
8690
-
#: src/components/moderation/ModerationDetailsDialog.tsx:119
8691
#: src/lib/moderation/useModerationCauseDescription.ts:128
8692
msgid "The author of this thread has hidden this reply."
8693
msgstr ""
···
8724
msgid "The feed has been replaced with Discover."
8725
msgstr ""
8726
8727
-
#: src/components/moderation/LabelsOnMeDialog.tsx:59
8728
msgid "The following labels were applied to your account."
8729
msgstr ""
8730
8731
-
#: src/components/moderation/LabelsOnMeDialog.tsx:60
8732
msgid "The following labels were applied to your content."
8733
msgstr ""
8734
···
8748
msgid "The open social network."
8749
msgstr ""
8750
8751
#: src/view/screens/PrivacyPolicy.tsx:38
8752
msgid "The Privacy Policy has been moved to <0/>"
8753
msgstr ""
···
8878
msgstr ""
8879
8880
#: src/components/dialogs/GifSelect.tsx:258
8881
-
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:303
8882
#: src/view/com/util/ErrorBoundary.tsx:59
8883
msgid "There was an unexpected issue in the application. Please let us know if this happened to you!"
8884
msgstr ""
···
8915
msgid "This action can be undone at any time."
8916
msgstr ""
8917
8918
-
#: src/components/moderation/LabelsOnMeDialog.tsx:271
8919
msgid "This appeal will be sent to <0>{sourceName}</0>."
8920
msgstr ""
8921
···
8943
msgid "This content is hosted by {0}. Do you want to enable external media?"
8944
msgstr ""
8945
8946
-
#: src/components/moderation/ModerationDetailsDialog.tsx:84
8947
#: src/lib/moderation/useModerationCauseDescription.ts:84
8948
msgid "This content is not available because one of the users involved has blocked the other."
8949
msgstr ""
···
9007
msgid "This is not a valid link"
9008
msgstr ""
9009
9010
-
#: src/components/moderation/ModerationDetailsDialog.tsx:168
9011
msgid "This label was applied by the author."
9012
msgstr ""
9013
9014
-
#: src/components/moderation/LabelsOnMeDialog.tsx:165
9015
msgid "This label was applied by you."
9016
msgstr ""
9017
···
9059
msgid "This post will be hidden from feeds and threads. This cannot be undone."
9060
msgstr ""
9061
9062
-
#: src/view/com/composer/Composer.tsx:475
9063
msgid "This post's author has disabled quote posts."
9064
msgstr ""
9065
···
9095
msgid "This user has blocked you"
9096
msgstr ""
9097
9098
-
#: src/components/moderation/ModerationDetailsDialog.tsx:79
9099
#: src/lib/moderation/useModerationCauseDescription.ts:75
9100
msgid "This user has blocked you. You cannot view their content."
9101
msgstr ""
···
9104
msgid "This user has requested that their content only be shown to signed-in users."
9105
msgstr ""
9106
9107
-
#: src/components/moderation/ModerationDetailsDialog.tsx:59
9108
msgid "This user is included in the <0>{0}</0> list which you have blocked."
9109
msgstr ""
9110
9111
-
#: src/components/moderation/ModerationDetailsDialog.tsx:91
9112
msgid "This user is included in the <0>{0}</0> list which you have muted."
9113
msgstr ""
9114
···
9240
msgid "TV"
9241
msgstr ""
9242
9243
-
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:65
9244
msgid "Two-factor authentication (2FA)"
9245
msgstr ""
9246
···
9365
msgid "Unfortunately, Bluesky is unavailable in Mississippi right now."
9366
msgstr ""
9367
9368
-
#: src/components/moderation/ReportDialog/index.tsx:372
9369
msgid "Unfortunately, none of your subscribed labelers supports this report type."
9370
msgstr ""
9371
···
9487
msgid "Unsubscribed from list"
9488
msgstr ""
9489
9490
-
#: src/view/com/composer/Composer.tsx:852
9491
msgid "Unsupported video type: {mimeType}"
9492
msgstr ""
9493
···
9573
msgid "Uploading link thumbnail..."
9574
msgstr ""
9575
9576
-
#: src/view/com/composer/Composer.tsx:1837
9577
msgid "Uploading video..."
9578
msgstr ""
9579
···
9617
msgid "User blocked"
9618
msgstr ""
9619
9620
-
#: src/components/moderation/ModerationDetailsDialog.tsx:71
9621
#: src/lib/moderation/useModerationCauseDescription.ts:63
9622
msgid "User Blocked"
9623
msgstr ""
···
9630
msgid "User blocked by list"
9631
msgstr ""
9632
9633
-
#: src/components/moderation/ModerationDetailsDialog.tsx:57
9634
msgid "User Blocked by List"
9635
msgstr ""
9636
···
9638
msgid "User Blocking You"
9639
msgstr ""
9640
9641
-
#: src/components/moderation/ModerationDetailsDialog.tsx:77
9642
msgid "User Blocks You"
9643
msgstr ""
9644
···
9831
msgid "Video settings"
9832
msgstr ""
9833
9834
-
#: src/view/com/composer/Composer.tsx:1847
9835
msgid "Video uploaded"
9836
msgstr ""
9837
···
9847
msgid "Videos must be less than 3 minutes long."
9848
msgstr ""
9849
9850
-
#: src/view/com/composer/Composer.tsx:546
9851
msgctxt "Action to view the post the user just created"
9852
msgid "View"
9853
msgstr ""
9854
9855
-
#: src/screens/Profile/Header/Shell.tsx:229
9856
msgid "View {0}'s avatar"
9857
msgstr ""
9858
···
9900
9901
#: src/components/interstitials/TrendingVideos.tsx:198
9902
#: src/components/interstitials/TrendingVideos.tsx:220
9903
-
#: src/screens/Search/modules/ExploreTrendingVideos.tsx:194
9904
-
#: src/screens/Search/modules/ExploreTrendingVideos.tsx:213
9905
msgid "View more"
9906
msgstr ""
9907
···
9909
msgid "View more trending videos"
9910
msgstr ""
9911
9912
-
#: src/view/com/composer/Composer.tsx:541
9913
msgid "View post"
9914
msgstr ""
9915
···
10040
msgid "We have sent another verification email to <0>{0}</0>."
10041
msgstr ""
10042
10043
-
#: src/screens/Onboarding/StepFinished.tsx:533
10044
msgid "We hope you have a wonderful time. Remember, Bluesky is:"
10045
msgstr ""
10046
···
10137
msgid "We're sorry, but your search could not be completed. Please try again in a few minutes."
10138
msgstr ""
10139
10140
-
#: src/view/com/composer/Composer.tsx:472
10141
msgid "We're sorry! The post you are replying to has been deleted."
10142
msgstr ""
10143
···
10188
10189
#: src/view/com/auth/SplashScreen.tsx:51
10190
#: src/view/com/auth/SplashScreen.web.tsx:103
10191
-
#: src/view/com/composer/Composer.tsx:812
10192
msgid "What's up?"
10193
msgstr ""
10194
···
10219
msgstr ""
10220
10221
#: src/components/interstitials/TrendingVideos.tsx:125
10222
-
#: src/screens/Search/modules/ExploreTrendingVideos.tsx:108
10223
msgid "Whoops! Trending videos failed to load."
10224
msgstr ""
10225
···
10266
msgid "Write a message"
10267
msgstr ""
10268
10269
-
#: src/view/com/composer/Composer.tsx:912
10270
msgid "Write post"
10271
msgstr ""
10272
10273
#: src/screens/PostThread/components/ThreadComposePrompt.tsx:90
10274
-
#: src/view/com/composer/Composer.tsx:810
10275
msgid "Write your reply"
10276
msgstr ""
10277
···
10289
msgid "www.mylivestream.tv"
10290
msgstr ""
10291
10292
-
#: src/view/com/composer/select-language/SuggestedLanguage.tsx:102
10293
msgid "Yes"
10294
msgstr ""
10295
···
10456
msgid "You have blocked this user"
10457
msgstr ""
10458
10459
-
#: src/components/moderation/ModerationDetailsDialog.tsx:73
10460
#: src/lib/moderation/useModerationCauseDescription.ts:57
10461
#: src/lib/moderation/useModerationCauseDescription.ts:65
10462
msgid "You have blocked this user. You cannot view their content."
···
10476
msgid "You have hidden this post"
10477
msgstr ""
10478
10479
-
#: src/components/moderation/ModerationDetailsDialog.tsx:111
10480
msgid "You have hidden this post."
10481
msgstr ""
10482
10483
-
#: src/components/moderation/ModerationDetailsDialog.tsx:104
10484
#: src/lib/moderation/useModerationCauseDescription.ts:99
10485
msgid "You have muted this account."
10486
msgstr ""
···
10502
msgid "You have no lists."
10503
msgstr ""
10504
10505
-
#: src/components/dialogs/StarterPackDialog.tsx:101
10506
msgid "You have no starter packs."
10507
msgstr ""
10508
···
10510
msgid "You have not blocked any accounts yet. To block an account, go to their profile and select \"Block account\" from the menu on their account."
10511
msgstr ""
10512
10513
-
#: src/view/screens/ModerationMutedAccounts.tsx:179
10514
msgid "You have not muted any accounts yet. To mute an account, go to their profile and select \"Mute account\" from the menu on their account."
10515
msgstr ""
10516
···
10534
msgid "You haven't muted any words or tags yet"
10535
msgstr ""
10536
10537
-
#: src/components/moderation/ModerationDetailsDialog.tsx:118
10538
#: src/lib/moderation/useModerationCauseDescription.ts:127
10539
msgid "You hid this reply."
10540
msgstr ""
···
10551
msgid "You joined Bluesky using a starter pack {timeAgoString} ago"
10552
msgstr ""
10553
10554
-
#: src/components/moderation/LabelsOnMeDialog.tsx:79
10555
msgid "You may appeal non-self labels if you feel they were placed in error."
10556
msgstr ""
10557
10558
-
#: src/components/moderation/LabelsOnMeDialog.tsx:84
10559
msgid "You may appeal these labels if you feel they were placed in error."
10560
msgstr ""
10561
···
10685
msgid "You're in line"
10686
msgstr ""
10687
10688
-
#: src/screens/Onboarding/StepFinished.tsx:530
10689
msgid "You're ready to go!"
10690
msgstr ""
10691
···
10694
msgid "You're signed in with an App Password. Please sign in with your main password to continue deactivating your account."
10695
msgstr ""
10696
10697
-
#: src/components/moderation/ModerationDetailsDialog.tsx:108
10698
#: src/lib/moderation/useModerationCauseDescription.ts:108
10699
msgid "You've chosen to hide a word or tag within this post."
10700
msgstr ""
···
10849
msgid "Your password must be at least 8 characters long."
10850
msgstr ""
10851
10852
-
#: src/view/com/composer/Composer.tsx:537
10853
msgid "Your post was sent"
10854
msgstr ""
10855
10856
-
#: src/view/com/composer/Composer.tsx:534
10857
msgid "Your posts were sent"
10858
msgstr ""
10859
10860
-
#: src/screens/Onboarding/StepFinished.tsx:545
10861
msgid "Your posts, likes, and blocks are public. Mutes are private."
10862
msgstr ""
10863
···
10865
msgid "Your preferred language"
10866
msgstr ""
10867
10868
-
#: src/screens/Onboarding/StepFinished.tsx:422
10869
msgid "Your profile picture"
10870
msgstr ""
10871
10872
-
#: src/screens/Onboarding/StepFinished.tsx:350
10873
msgid "Your profile picture surrounded by concentric circles of other users' profile pictures"
10874
msgstr ""
10875
···
10877
msgid "Your profile, posts, feeds, and lists will no longer be visible to other Bluesky users. You can reactivate your account at any time by logging in."
10878
msgstr ""
10879
10880
-
#: src/view/com/composer/Composer.tsx:536
10881
msgid "Your reply was sent"
10882
msgstr ""
10883
10884
-
#: src/components/moderation/ReportDialog/index.tsx:394
10885
msgid "Your report will be sent to <0>{0}</0>."
10886
msgstr ""
10887
···
124
msgid "{0, plural, other {# people have}} used this starter pack!"
125
msgstr ""
126
127
+
#: src/components/dialogs/StarterPackDialog.tsx:356
128
msgid "{0, plural, other {+# more}}"
129
msgstr ""
130
···
521
msgid "7 days"
522
msgstr ""
523
524
+
#: src/screens/Onboarding/StepFinished.tsx:340
525
msgid "A collection of popular feeds you can find on Bluesky, including News, Booksky, Game Dev, Blacksky, and Fountain Pens"
526
msgstr ""
527
···
568
msgid "Accept Request"
569
msgstr ""
570
571
+
#: src/view/com/composer/select-language/SuggestedLanguage.tsx:179
572
+
msgid "Accept this language suggestion"
573
+
msgstr ""
574
+
575
#: src/screens/Settings/AccessibilitySettings.tsx:44
576
#: src/screens/Settings/Settings.tsx:220
577
#: src/screens/Settings/Settings.tsx:223
···
608
msgid "Account muted"
609
msgstr ""
610
611
+
#: src/components/moderation/ModerationDetailsDialog.tsx:105
612
#: src/lib/moderation/useModerationCauseDescription.ts:98
613
msgid "Account Muted"
614
msgstr ""
615
616
+
#: src/components/moderation/ModerationDetailsDialog.tsx:91
617
msgid "Account Muted by List"
618
msgstr ""
619
···
658
659
#: src/components/dialogs/lists/ListAddRemoveUsersDialog.tsx:169
660
#: src/components/dialogs/MutedWords.tsx:333
661
+
#: src/components/dialogs/StarterPackDialog.tsx:370
662
+
#: src/components/dialogs/StarterPackDialog.tsx:376
663
#: src/view/com/modals/UserAddRemoveLists.tsx:235
664
msgid "Add"
665
msgstr ""
···
712
msgid "Add another account"
713
msgstr ""
714
715
+
#: src/view/com/composer/Composer.tsx:853
716
msgid "Add another post"
717
msgstr ""
718
719
+
#: src/view/com/composer/Composer.tsx:1490
720
msgid "Add another post to thread"
721
msgstr ""
722
···
739
msgid "Add media to post"
740
msgstr ""
741
742
+
#: src/components/moderation/ReportDialog/index.tsx:406
743
+
#: src/components/moderation/ReportDialog/index.tsx:410
744
msgid "Add more details (optional)"
745
msgstr ""
746
···
794
msgid "Add to saved posts"
795
msgstr ""
796
797
+
#: src/components/dialogs/StarterPackDialog.tsx:175
798
#: src/view/com/profile/ProfileMenu.tsx:308
799
#: src/view/com/profile/ProfileMenu.tsx:311
800
msgid "Add to starter packs"
···
809
msgid "Added to list"
810
msgstr ""
811
812
+
#: src/components/dialogs/StarterPackDialog.tsx:257
813
msgid "Added to starter pack"
814
msgstr ""
815
···
817
msgid "Additional details (limit 1000 characters)"
818
msgstr ""
819
820
+
#: src/components/moderation/ReportDialog/index.tsx:424
821
msgid "Additional details (limit 300 characters)"
822
msgstr ""
823
···
883
#: src/screens/Search/components/SearchLanguageDropdown.tsx:64
884
#: src/screens/Search/components/SearchLanguageDropdown.tsx:99
885
#: src/screens/Search/components/SearchLanguageDropdown.tsx:101
886
+
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:238
887
msgid "All languages"
888
msgstr ""
889
···
907
msgstr ""
908
909
#: src/screens/Settings/ActivityPrivacySettings.tsx:52
910
+
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:91
911
msgid "Allow others to be notified of your posts"
912
msgstr ""
913
···
971
msgstr ""
972
973
#: src/components/dialogs/GifSelect.tsx:253
974
+
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:320
975
msgid "An error has occurred"
976
msgstr ""
977
···
1016
msgid "An error occurred while uploading the video."
1017
msgstr ""
1018
1019
+
#: src/screens/Onboarding/StepFinished.tsx:358
1020
msgid "An illustration of several Bluesky posts alongside repost, like, and comment icons"
1021
msgstr ""
1022
···
1055
msgid "an unknown error occurred"
1056
msgstr ""
1057
1058
+
#: src/components/moderation/ModerationDetailsDialog.tsx:136
1059
#: src/lib/moderation/useModerationCauseDescription.ts:144
1060
msgid "an unknown labeler"
1061
msgstr ""
···
1093
1094
#: src/screens/Settings/ActivityPrivacySettings.tsx:111
1095
#: src/screens/Settings/ActivityPrivacySettings.tsx:116
1096
+
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:162
1097
msgid "Anyone who follows me"
1098
msgstr ""
1099
···
1129
msgid "App password names must be at least 4 characters long"
1130
msgstr ""
1131
1132
+
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:71
1133
+
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:74
1134
msgid "App passwords"
1135
msgstr ""
1136
···
1139
msgid "App Passwords"
1140
msgstr ""
1141
1142
+
#: src/components/moderation/LabelsOnMeDialog.tsx:152
1143
+
#: src/components/moderation/LabelsOnMeDialog.tsx:155
1144
msgid "Appeal"
1145
msgstr ""
1146
1147
+
#: src/components/moderation/LabelsOnMeDialog.tsx:270
1148
msgid "Appeal \"{0}\" label"
1149
msgstr ""
1150
1151
+
#: src/components/moderation/LabelsOnMeDialog.tsx:260
1152
#: src/screens/Messages/components/ChatDisabled.tsx:103
1153
msgctxt "toast"
1154
msgid "Appeal submitted"
···
1225
msgid "Are you sure you want to remove this from your feeds?"
1226
msgstr ""
1227
1228
+
#: src/view/com/composer/Composer.tsx:802
1229
msgid "Are you sure you'd like to discard this draft?"
1230
msgstr ""
1231
1232
+
#: src/view/com/composer/Composer.tsx:992
1233
msgid "Are you sure you'd like to discard this post?"
1234
msgstr ""
1235
···
1237
msgid "Are you sure?"
1238
msgstr ""
1239
1240
+
#: src/view/com/composer/select-language/SuggestedLanguage.tsx:99
1241
msgid "Are you writing in <0>{suggestedLanguageName}</0>?"
1242
msgstr ""
1243
···
1278
msgid "Available"
1279
msgstr ""
1280
1281
+
#: src/components/moderation/LabelsOnMeDialog.tsx:317
1282
+
#: src/components/moderation/LabelsOnMeDialog.tsx:318
1283
#: src/screens/Login/ChooseAccountForm.tsx:90
1284
#: src/screens/Login/ChooseAccountForm.tsx:95
1285
#: src/screens/Login/ForgotPasswordForm.tsx:123
···
1311
msgid "Before creating a post or replying, you must first verify your email."
1312
msgstr ""
1313
1314
+
#: src/components/dialogs/StarterPackDialog.tsx:70
1315
#: src/components/StarterPack/ProfileStarterPacks.tsx:231
1316
#: src/components/StarterPack/ProfileStarterPacks.tsx:241
1317
msgid "Before creating a starter pack, you must first verify your email."
···
1562
msgstr ""
1563
1564
#: src/components/LabelingServiceCard/index.tsx:62
1565
+
#: src/components/moderation/ReportDialog/index.tsx:686
1566
#: src/screens/Search/components/StarterPackCard.tsx:106
1567
#: src/screens/Search/Explore.tsx:930
1568
msgid "By {0}"
···
1630
#: src/screens/Settings/Settings.tsx:289
1631
#: src/screens/Takendown.tsx:108
1632
#: src/screens/Takendown.tsx:111
1633
+
#: src/view/com/composer/Composer.tsx:1047
1634
+
#: src/view/com/composer/Composer.tsx:1058
1635
#: src/view/com/composer/photos/EditImageDialog.web.tsx:43
1636
#: src/view/com/composer/photos/EditImageDialog.web.tsx:52
1637
#: src/view/shell/desktop/LeftNav.tsx:213
···
1699
msgid "Change Handle"
1700
msgstr ""
1701
1702
+
#: src/components/moderation/ReportDialog/index.tsx:328
1703
msgid "Change moderation service"
1704
msgstr ""
1705
···
1712
msgid "Change password dialog"
1713
msgstr ""
1714
1715
+
#: src/components/moderation/ReportDialog/index.tsx:247
1716
msgid "Change report reason"
1717
msgstr ""
1718
···
1823
msgid "Choose People"
1824
msgstr ""
1825
1826
+
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:184
1827
msgid "Choose Post Languages"
1828
msgstr ""
1829
1830
+
#: src/screens/Onboarding/StepFinished.tsx:573
1831
msgid "Choose the algorithms that power your custom feeds."
1832
msgstr ""
1833
···
1920
#: src/components/dialogs/nuxs/InitialVerificationAnnouncement.tsx:178
1921
#: src/components/dialogs/nuxs/InitialVerificationAnnouncement.tsx:187
1922
#: src/components/dialogs/SearchablePeopleList.tsx:295
1923
+
#: src/components/dialogs/StarterPackDialog.tsx:178
1924
#: src/components/dms/EmojiPopup.android.tsx:58
1925
#: src/components/dms/ReportDialog.tsx:387
1926
#: src/components/dms/ReportDialog.tsx:396
···
1938
#: src/components/WhoCanReply.tsx:209
1939
#: src/screens/Settings/components/ChangePasswordDialog.tsx:286
1940
#: src/screens/Settings/components/ChangePasswordDialog.tsx:291
1941
+
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:335
1942
#: src/view/com/feeds/MissingFeed.tsx:210
1943
#: src/view/com/feeds/MissingFeed.tsx:217
1944
msgid "Close"
···
1962
#: src/components/dialogs/GifSelect.tsx:263
1963
#: src/components/verification/VerificationsDialog.tsx:136
1964
#: src/components/verification/VerifierDialog.tsx:142
1965
+
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:204
1966
+
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:298
1967
+
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:330
1968
msgid "Close dialog"
1969
msgstr ""
1970
···
2007
msgid "Closes password update alert"
2008
msgstr ""
2009
2010
+
#: src/view/com/composer/Composer.tsx:1055
2011
msgid "Closes post composer and discards post draft"
2012
msgstr ""
2013
···
2053
msgid "Community Guidelines"
2054
msgstr ""
2055
2056
+
#: src/screens/Onboarding/StepFinished.tsx:472
2057
+
#: src/screens/Onboarding/StepFinished.tsx:586
2058
msgid "Complete onboarding and start using your account"
2059
msgstr ""
2060
···
2066
msgid "Compose new post"
2067
msgstr ""
2068
2069
+
#: src/view/com/composer/Composer.tsx:956
2070
msgid "Compose posts up to {0, plural, other {# characters}} in length"
2071
msgstr ""
2072
···
2074
msgid "Compose reply"
2075
msgstr ""
2076
2077
+
#: src/view/com/composer/Composer.tsx:1883
2078
msgid "Compressing video..."
2079
msgstr ""
2080
···
2186
msgid "Content Languages"
2187
msgstr ""
2188
2189
+
#: src/components/moderation/ModerationDetailsDialog.tsx:84
2190
#: src/lib/moderation/useModerationCauseDescription.ts:82
2191
msgid "Content Not Available"
2192
msgstr ""
2193
2194
+
#: src/components/moderation/ModerationDetailsDialog.tsx:52
2195
#: src/components/moderation/ScreenHider.tsx:99
2196
#: src/lib/moderation/useGlobalLabelStrings.ts:22
2197
#: src/lib/moderation/useModerationCauseDescription.ts:45
···
2411
2412
#. Text on button to create a new starter pack
2413
#. Text on button to create a new starter pack
2414
+
#: src/components/dialogs/StarterPackDialog.tsx:111
2415
+
#: src/components/dialogs/StarterPackDialog.tsx:200
2416
#: src/components/StarterPack/ProfileStarterPacks.tsx:296
2417
msgid "Create"
2418
msgstr ""
···
2478
msgid "Create new account"
2479
msgstr ""
2480
2481
+
#: src/components/moderation/ReportDialog/index.tsx:588
2482
#: src/components/ReportDialog/SelectReportOptionView.tsx:102
2483
msgid "Create report for {0}"
2484
msgstr ""
2485
2486
+
#: src/components/dialogs/StarterPackDialog.tsx:106
2487
+
#: src/components/dialogs/StarterPackDialog.tsx:195
2488
msgid "Create starter pack"
2489
msgstr ""
2490
···
2639
2640
#: src/components/PostControls/PostMenu/PostMenuItems.tsx:685
2641
#: src/components/PostControls/PostMenu/PostMenuItems.tsx:687
2642
+
#: src/view/com/composer/Composer.tsx:966
2643
msgid "Delete post"
2644
msgstr ""
2645
···
2752
2753
#: src/components/dialogs/lists/CreateOrEditListDialog.tsx:92
2754
#: src/screens/Profile/Header/EditProfileDialog.tsx:82
2755
+
#: src/view/com/composer/Composer.tsx:804
2756
+
#: src/view/com/composer/Composer.tsx:999
2757
msgid "Discard"
2758
msgstr ""
2759
···
2762
msgid "Discard changes?"
2763
msgstr ""
2764
2765
+
#: src/view/com/composer/Composer.tsx:801
2766
msgid "Discard draft?"
2767
msgstr ""
2768
2769
+
#: src/view/com/composer/Composer.tsx:991
2770
msgid "Discard post?"
2771
msgstr ""
2772
···
2789
msgid "Dismiss"
2790
msgstr ""
2791
2792
+
#: src/view/com/composer/Composer.tsx:1807
2793
msgid "Dismiss error"
2794
msgstr ""
2795
···
2815
msgid "Display name"
2816
msgstr ""
2817
2818
+
#: src/screens/Onboarding/StepFinished.tsx:346
2819
msgid "Ditch the trolls and clickbait. Find real people and conversations that matter to you."
2820
msgstr ""
2821
···
2868
#: src/view/com/auth/server-input/index.tsx:233
2869
#: src/view/com/composer/labels/LabelsBtn.tsx:223
2870
#: src/view/com/composer/labels/LabelsBtn.tsx:230
2871
+
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:303
2872
#: src/view/com/composer/videos/SubtitleDialog.tsx:168
2873
#: src/view/com/composer/videos/SubtitleDialog.tsx:178
2874
msgid "Done"
···
3055
msgid "Email 2FA disabled"
3056
msgstr ""
3057
3058
+
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:62
3059
msgid "Email 2FA enabled"
3060
msgstr ""
3061
···
3126
msgstr ""
3127
3128
#: src/screens/Settings/NotificationSettings/ActivityNotificationSettings.tsx:139
3129
+
#: src/view/screens/Storybook/Admonitions.tsx:75
3130
msgid "Enable notifications for an account by visiting their profile and pressing the <0>bell icon</0> <1/>."
3131
msgstr ""
3132
···
3223
msgid "Entertainment"
3224
msgstr ""
3225
3226
+
#: src/view/com/composer/Composer.tsx:1892
3227
#: src/view/com/util/error/ErrorScreen.tsx:42
3228
msgid "Error"
3229
msgstr ""
···
3232
msgid "Error loading post"
3233
msgstr ""
3234
3235
+
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:153
3236
msgid "Error loading preference"
3237
msgstr ""
3238
···
3346
msgid "Expires {0}"
3347
msgstr ""
3348
3349
+
#: src/components/moderation/LabelsOnMeDialog.tsx:201
3350
+
#: src/components/moderation/ModerationDetailsDialog.tsx:210
3351
msgid "Expires in {0}"
3352
msgstr ""
3353
···
3415
msgid "Failed to add emoji reaction"
3416
msgstr ""
3417
3418
+
#: src/components/dialogs/StarterPackDialog.tsx:269
3419
msgid "Failed to add to starter pack"
3420
msgstr ""
3421
···
3521
msgid "Failed to remove emoji reaction"
3522
msgstr ""
3523
3524
+
#: src/components/dialogs/StarterPackDialog.tsx:288
3525
msgid "Failed to remove from starter pack"
3526
msgstr ""
3527
···
3555
msgid "Failed to send email, please try again."
3556
msgstr ""
3557
3558
+
#: src/components/moderation/LabelsOnMeDialog.tsx:256
3559
#: src/screens/Messages/components/ChatDisabled.tsx:99
3560
msgid "Failed to submit appeal, please try again."
3561
msgstr ""
···
3698
msgid "Filter who you receive notifications from"
3699
msgstr ""
3700
3701
+
#: src/screens/Onboarding/StepFinished.tsx:478
3702
+
#: src/screens/Onboarding/StepFinished.tsx:589
3703
msgid "Finalizing"
3704
msgstr ""
3705
···
3719
msgid "Find people to follow"
3720
msgstr ""
3721
3722
+
#: src/screens/Search/Shell.tsx:475
3723
msgid "Find posts, users, and feeds on Bluesky"
3724
msgstr ""
3725
3726
+
#: src/screens/Onboarding/StepFinished.tsx:344
3727
msgid "Find your people"
3728
msgstr ""
3729
···
3750
msgid "Flat White"
3751
msgstr ""
3752
3753
+
#: src/screens/Onboarding/StepFinished.tsx:569
3754
msgid "Flexible"
3755
msgstr ""
3756
···
3924
msgid "Forever"
3925
msgstr ""
3926
3927
+
#: src/screens/Onboarding/StepFinished.tsx:353
3928
msgid "Forget the noise"
3929
msgstr ""
3930
···
3941
msgid "Forgot?"
3942
msgstr ""
3943
3944
+
#: src/screens/Onboarding/StepFinished.tsx:335
3945
msgid "Free your feed"
3946
msgstr ""
3947
···
4419
msgstr ""
4420
4421
#: src/screens/Settings/NotificationSettings/ActivityNotificationSettings.tsx:157
4422
+
#: src/view/screens/Storybook/Admonitions.tsx:89
4423
msgid "If you want to restrict who can receive notifications for your account's activity, you can change this in <0>Settings โ Privacy and Security</0>."
4424
msgstr ""
4425
···
4598
msgid "It's just you right now! Add more people to your starter pack by searching above."
4599
msgstr ""
4600
4601
+
#: src/view/com/composer/Composer.tsx:1826
4602
msgid "Job ID: {0}"
4603
msgstr ""
4604
···
4662
msgid "Labels are annotations on users and content. They can be used to hide, warn, and categorize the network."
4663
msgstr ""
4664
4665
+
#: src/components/moderation/LabelsOnMeDialog.tsx:74
4666
msgid "Labels on your account"
4667
msgstr ""
4668
4669
+
#: src/components/moderation/LabelsOnMeDialog.tsx:76
4670
msgid "Labels on your content"
4671
msgstr ""
4672
···
4748
msgid "Learn more about verification on Bluesky"
4749
msgstr ""
4750
4751
+
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:127
4752
+
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:130
4753
msgid "Learn more about what is public on Bluesky."
4754
msgstr ""
4755
···
4801
msgid "Let's get your password reset!"
4802
msgstr ""
4803
4804
+
#: src/screens/Onboarding/StepFinished.tsx:480
4805
+
#: src/screens/Onboarding/StepFinished.tsx:589
4806
msgid "Let's go!"
4807
msgstr ""
4808
···
4854
4855
#: src/screens/Post/PostLikedBy.tsx:41
4856
#: src/screens/Profile/ProfileLabelerLikedBy.tsx:32
4857
+
#: src/view/screens/ProfileFeedLikedBy.tsx:32
4858
msgid "Liked By"
4859
msgstr ""
4860
···
5033
msgid "Log"
5034
msgstr ""
5035
5036
+
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:106
5037
msgid "Logged-out visibility"
5038
msgstr ""
5039
···
5207
msgid "Moderation"
5208
msgstr ""
5209
5210
+
#: src/components/moderation/ModerationDetailsDialog.tsx:140
5211
msgid "Moderation details"
5212
msgstr ""
5213
···
5256
msgid "Moderation tools"
5257
msgstr ""
5258
5259
+
#: src/components/moderation/ModerationDetailsDialog.tsx:54
5260
#: src/lib/moderation/useModerationCauseDescription.ts:47
5261
msgid "Moderator has chosen to set a general warning on the content."
5262
msgstr ""
···
5266
msgid "More feeds"
5267
msgstr ""
5268
5269
+
#: src/view/com/composer/select-language/PostLanguageSelect.tsx:87
5270
+
#: src/view/com/composer/select-language/PostLanguageSelect.tsx:90
5271
msgid "More languages..."
5272
msgstr ""
5273
···
5372
msgstr ""
5373
5374
#: src/Navigation.tsx:187
5375
+
#: src/view/screens/ModerationMutedAccounts.tsx:116
5376
msgid "Muted Accounts"
5377
msgstr ""
5378
5379
+
#: src/view/screens/ModerationMutedAccounts.tsx:202
5380
msgid "Muted accounts have their posts removed from your feed and from your notifications. Mutes are completely private."
5381
msgstr ""
5382
···
5439
msgid "Navigates to your profile"
5440
msgstr ""
5441
5442
+
#: src/components/moderation/ReportDialog/index.tsx:274
5443
+
#: src/components/moderation/ReportDialog/index.tsx:291
5444
msgid "Need to report a copyright violation, legal request, or regulatory compliance issue?"
5445
msgstr ""
5446
···
5448
msgid "Need to report a copyright violation?"
5449
msgstr ""
5450
5451
+
#: src/screens/Onboarding/StepFinished.tsx:557
5452
msgid "Never lose access to your followers or data."
5453
msgstr ""
5454
···
5548
msgid "New posts from {firstAuthorName} and {additionalAuthorsCount, plural, one {{formattedAuthorsCount} other} other {{formattedAuthorsCount} others}}"
5549
msgstr ""
5550
5551
+
#: src/components/dialogs/StarterPackDialog.tsx:192
5552
msgid "New starter pack"
5553
msgstr ""
5554
···
5575
#: src/screens/Login/LoginForm.tsx:350
5576
#: src/screens/Login/SetNewPasswordForm.tsx:182
5577
#: src/screens/Login/SetNewPasswordForm.tsx:188
5578
+
#: src/screens/Onboarding/StepFinished.tsx:473
5579
+
#: src/screens/Onboarding/StepFinished.tsx:482
5580
#: src/screens/Settings/components/AddAppPasswordDialog.tsx:157
5581
#: src/screens/Settings/components/AddAppPasswordDialog.tsx:165
5582
#: src/screens/Signup/BackNextButtons.tsx:67
···
5596
msgid "Next image"
5597
msgstr ""
5598
5599
+
#: src/screens/Onboarding/StepFinished.tsx:355
5600
msgid "No ads, no invasive tracking, no engagement traps. Bluesky respects your time and attention."
5601
msgstr ""
5602
···
5639
msgid "No messages yet"
5640
msgstr ""
5641
5642
+
#: src/screens/Onboarding/StepFinished.tsx:337
5643
msgid "No more doomscrolling junk-filled algorithms. Find feeds that work for you, not against you."
5644
msgstr ""
5645
···
5651
#: src/screens/Messages/Settings.tsx:109
5652
#: src/screens/Settings/ActivityPrivacySettings.tsx:129
5653
#: src/screens/Settings/ActivityPrivacySettings.tsx:134
5654
+
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:159
5655
msgid "No one"
5656
msgstr ""
5657
···
5762
msgid "Note about sharing"
5763
msgstr ""
5764
5765
+
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:116
5766
msgid "Note: Bluesky is an open and public network. This setting only limits the visibility of your content on the Bluesky app and website, and other apps may not respect this setting. Your content may still be shown to logged-out users by other apps and websites."
5767
msgstr ""
5768
···
5843
msgstr ""
5844
5845
#: src/components/dialogs/GifSelect.tsx:256
5846
+
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:323
5847
#: src/view/com/util/ErrorBoundary.tsx:57
5848
msgid "Oh no!"
5849
msgstr ""
···
5880
msgid "Onboarding reset"
5881
msgstr ""
5882
5883
+
#: src/view/com/composer/Composer.tsx:398
5884
msgid "One or more GIFs is missing alt text."
5885
msgstr ""
5886
5887
+
#: src/view/com/composer/Composer.tsx:395
5888
msgid "One or more images is missing alt text."
5889
msgstr ""
5890
···
5896
msgid "One or more of your selected files are too large. Maximum size is 100ย MB."
5897
msgstr ""
5898
5899
+
#: src/view/com/composer/Composer.tsx:405
5900
msgid "One or more videos is missing alt text."
5901
msgstr ""
5902
···
5910
5911
#: src/screens/Settings/ActivityPrivacySettings.tsx:120
5912
#: src/screens/Settings/ActivityPrivacySettings.tsx:125
5913
+
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:157
5914
msgid "Only followers who I follow"
5915
msgstr ""
5916
···
5935
msgid "Oops!"
5936
msgstr ""
5937
5938
+
#: src/screens/Onboarding/StepFinished.tsx:553
5939
msgid "Open"
5940
msgstr ""
5941
···
5953
msgstr ""
5954
5955
#: src/screens/Messages/components/MessageInput.web.tsx:181
5956
+
#: src/view/com/composer/Composer.tsx:1475
5957
msgid "Open emoji picker"
5958
msgstr ""
5959
···
6053
msgid "Opens device gallery to select up to {MAX_IMAGES, plural, other {# images}}, or a single video or GIF."
6054
msgstr ""
6055
6056
+
#: src/view/com/composer/Composer.tsx:1476
6057
msgid "Opens emoji picker"
6058
msgstr ""
6059
···
6087
msgid "Opens password reset form"
6088
msgstr ""
6089
6090
+
#: src/view/com/composer/select-language/PostLanguageSelect.tsx:129
6091
msgid "Opens post language settings"
6092
msgstr ""
6093
···
6413
msgid "Please enter your username"
6414
msgstr ""
6415
6416
+
#: src/components/moderation/LabelsOnMeDialog.tsx:292
6417
msgid "Please explain why you think this label was incorrectly applied by {0}"
6418
msgstr ""
6419
···
6465
msgid "Porn"
6466
msgstr ""
6467
6468
+
#: src/screens/PostThread/index.tsx:503
6469
msgctxt "description"
6470
msgid "Post"
6471
msgstr ""
6472
6473
+
#: src/view/com/composer/Composer.tsx:1118
6474
msgctxt "action"
6475
msgid "Post"
6476
msgstr ""
6477
6478
+
#: src/view/com/composer/Composer.tsx:1116
6479
msgctxt "action"
6480
msgid "Post All"
6481
msgstr ""
···
6507
msgid "Post has been deleted"
6508
msgstr ""
6509
6510
+
#: src/components/moderation/ModerationDetailsDialog.tsx:109
6511
#: src/lib/moderation/useModerationCauseDescription.ts:106
6512
msgid "Post Hidden by Muted Word"
6513
msgstr ""
6514
6515
+
#: src/components/moderation/ModerationDetailsDialog.tsx:112
6516
#: src/lib/moderation/useModerationCauseDescription.ts:115
6517
msgid "Post Hidden by You"
6518
msgstr ""
···
6527
msgstr ""
6528
6529
#. Accessibility label for button that opens dialog to choose post language settings
6530
+
#: src/view/com/composer/select-language/PostLanguageSelect.tsx:124
6531
msgid "Post language selection"
6532
msgstr ""
6533
···
6620
#: src/Navigation.tsx:407
6621
#: src/Navigation.tsx:415
6622
#: src/screens/Settings/ActivityPrivacySettings.tsx:40
6623
+
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:44
6624
msgid "Privacy and Security"
6625
msgstr ""
6626
6627
#: src/screens/Settings/NotificationSettings/ActivityNotificationSettings.tsx:161
6628
+
#: src/view/screens/Storybook/Admonitions.tsx:93
6629
msgid "Privacy and Security settings"
6630
msgstr ""
6631
···
6640
msgid "Privacy Policy"
6641
msgstr ""
6642
6643
+
#: src/view/com/composer/Composer.tsx:1889
6644
msgid "Processing video..."
6645
msgstr ""
6646
···
6666
msgid "Profile updated"
6667
msgstr ""
6668
6669
+
#: src/screens/Onboarding/StepFinished.tsx:539
6670
msgid "Public"
6671
msgstr ""
6672
···
6679
msgstr ""
6680
6681
#. Accessibility label for button to publish a single post
6682
+
#: src/view/com/composer/Composer.tsx:1098
6683
msgid "Publish post"
6684
msgstr ""
6685
6686
#. Accessibility label for button to publish multiple posts in a thread
6687
+
#: src/view/com/composer/Composer.tsx:1091
6688
msgid "Publish posts"
6689
msgstr ""
6690
6691
#. Accessibility label for button to publish multiple replies in a thread
6692
+
#: src/view/com/composer/Composer.tsx:1076
6693
msgid "Publish replies"
6694
msgstr ""
6695
6696
#. Accessibility label for button to publish a single reply
6697
+
#: src/view/com/composer/Composer.tsx:1083
6698
msgid "Publish reply"
6699
msgstr ""
6700
···
6853
msgid "Recent Searches"
6854
msgstr ""
6855
6856
+
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:232
6857
msgid "Recently used"
6858
msgstr ""
6859
···
6881
6882
#: src/components/dialogs/lists/ListAddRemoveUsersDialog.tsx:171
6883
#: src/components/dialogs/MutedWords.tsx:443
6884
+
#: src/components/dialogs/StarterPackDialog.tsx:370
6885
+
#: src/components/dialogs/StarterPackDialog.tsx:376
6886
#: src/components/FeedCard.tsx:343
6887
#: src/components/StarterPack/Wizard/WizardListCard.tsx:104
6888
#: src/components/StarterPack/Wizard/WizardListCard.tsx:111
···
7027
msgid "Removed from saved posts"
7028
msgstr ""
7029
7030
+
#: src/components/dialogs/StarterPackDialog.tsx:276
7031
msgid "Removed from starter pack"
7032
msgstr ""
7033
···
7084
msgid "Replies to this post are disabled."
7085
msgstr ""
7086
7087
+
#: src/view/com/composer/Composer.tsx:1114
7088
msgctxt "action"
7089
msgid "Reply"
7090
msgstr ""
···
7094
msgid "Reply ({0, plural, one {# reply} other {# replies}})"
7095
msgstr ""
7096
7097
+
#: src/components/moderation/ModerationDetailsDialog.tsx:118
7098
#: src/lib/moderation/useModerationCauseDescription.ts:125
7099
msgid "Reply Hidden by Thread Author"
7100
msgstr ""
7101
7102
+
#: src/components/moderation/ModerationDetailsDialog.tsx:117
7103
#: src/lib/moderation/useModerationCauseDescription.ts:124
7104
msgid "Reply Hidden by You"
7105
msgstr ""
···
7348
#: src/components/dms/MessageItem.tsx:322
7349
#: src/components/Error.tsx:65
7350
#: src/components/Lists.tsx:110
7351
+
#: src/components/moderation/ReportDialog/index.tsx:232
7352
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx:55
7353
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx:58
7354
#: src/components/StarterPack/ProfileStarterPacks.tsx:342
···
7366
#: src/screens/Signup/BackNextButtons.tsx:53
7367
#: src/view/com/util/error/ErrorMessage.tsx:60
7368
#: src/view/com/util/error/ErrorScreen.tsx:97
7369
+
#: src/view/screens/Storybook/Admonitions.tsx:63
7370
msgid "Retry"
7371
msgstr ""
7372
7373
+
#: src/components/moderation/ReportDialog/index.tsx:229
7374
+
#: src/view/screens/Storybook/Admonitions.tsx:60
7375
msgid "Retry loading report options"
7376
msgstr ""
7377
···
7501
#: src/components/forms/SearchInput.tsx:34
7502
#: src/components/forms/SearchInput.tsx:36
7503
#: src/screens/Search/Shell.tsx:307
7504
+
#: src/screens/Search/Shell.tsx:463
7505
#: src/view/shell/bottom-bar/BottomBar.tsx:198
7506
msgid "Search"
7507
msgstr ""
···
7561
msgid "Search is currently unavailable when logged out"
7562
msgstr ""
7563
7564
+
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:215
7565
+
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:216
7566
msgid "Search languages"
7567
msgstr ""
7568
···
7644
msgid "Select {0}"
7645
msgstr ""
7646
7647
+
#: src/view/com/composer/select-language/PostLanguageSelect.tsx:72
7648
msgid "Select {langName}"
7649
msgstr ""
7650
···
7710
msgstr ""
7711
7712
#: src/screens/Settings/LanguageSettings.tsx:178
7713
+
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:248
7714
msgid "Select languages"
7715
msgstr ""
7716
7717
+
#: src/components/moderation/ReportDialog/index.tsx:313
7718
msgid "Select moderation service"
7719
msgstr ""
7720
···
7722
msgid "Select moderator"
7723
msgstr ""
7724
7725
+
#: src/view/com/composer/select-language/PostLanguageSelect.tsx:57
7726
msgid "Select post language"
7727
msgstr ""
7728
···
7743
msgid "Select the moderation service(s) to report to"
7744
msgstr ""
7745
7746
+
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:194
7747
msgid "Select up to 3 languages used in this post"
7748
msgstr ""
7749
···
7805
msgid "Send message"
7806
msgstr ""
7807
7808
+
#: src/components/PostControls/ShareMenu/RecentChats.tsx:122
7809
msgid "Send post to {name}"
7810
msgstr ""
7811
···
7824
msgid "Send report to {0}"
7825
msgstr ""
7826
7827
+
#: src/components/moderation/ReportDialog/index.tsx:652
7828
msgid "Send report to {title}"
7829
msgstr ""
7830
···
7880
msgid "Settings for activity from others"
7881
msgstr ""
7882
7883
+
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:84
7884
msgid "Settings for allowing others to be notified of your posts"
7885
msgstr ""
7886
···
8211
msgid "Similar accounts"
8212
msgstr ""
8213
8214
+
#: src/screens/Onboarding/StepFinished.tsx:379
8215
+
#: src/screens/Onboarding/StepFinished.tsx:461
8216
#: src/screens/Onboarding/StepInterests/index.tsx:240
8217
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:222
8218
#: src/screens/StarterPack/Wizard/index.tsx:218
8219
msgid "Skip"
8220
msgstr ""
8221
8222
+
#: src/screens/Onboarding/StepFinished.tsx:372
8223
+
#: src/screens/Onboarding/StepFinished.tsx:458
8224
msgid "Skip introduction and start using your account"
8225
msgstr ""
8226
···
8275
msgstr ""
8276
8277
#: src/components/ageAssurance/AgeAssuranceInitDialog.tsx:138
8278
+
#: src/components/moderation/ReportDialog/index.tsx:224
8279
#: src/screens/Deactivated.tsx:94
8280
#: src/screens/Settings/components/DeactivateAccountDialog.tsx:59
8281
+
#: src/view/screens/Storybook/Admonitions.tsx:55
8282
msgid "Something went wrong, please try again"
8283
msgstr ""
8284
···
8304
msgid "Something wrong? Let us know."
8305
msgstr ""
8306
8307
+
#: src/App.native.tsx:128
8308
+
#: src/App.web.tsx:100
8309
msgid "Sorry! Your session expired. Please sign in again."
8310
msgstr ""
8311
···
8321
msgid "Sort replies to the same post by:"
8322
msgstr ""
8323
8324
+
#: src/components/moderation/LabelsOnMeDialog.tsx:180
8325
+
#: src/components/moderation/ModerationDetailsDialog.tsx:188
8326
msgid "Source: <0>{sourceName}</0>"
8327
msgstr ""
8328
···
8344
msgid "Sports"
8345
msgstr ""
8346
8347
+
#: src/components/PostControls/ShareMenu/RecentChats.tsx:207
8348
msgid "Start a conversation, and it will appear here."
8349
msgstr ""
8350
···
8418
8419
#: src/components/ageAssurance/AgeAssuranceAppealDialog.tsx:117
8420
#: src/components/ageAssurance/AgeAssuranceAppealDialog.tsx:123
8421
+
#: src/components/moderation/LabelsOnMeDialog.tsx:326
8422
+
#: src/components/moderation/LabelsOnMeDialog.tsx:327
8423
#: src/screens/Messages/components/ChatDisabled.tsx:154
8424
#: src/screens/Messages/components/ChatDisabled.tsx:155
8425
msgid "Submit"
···
8433
msgid "Submit Appeal"
8434
msgstr ""
8435
8436
+
#: src/components/moderation/ReportDialog/index.tsx:390
8437
+
#: src/components/moderation/ReportDialog/index.tsx:447
8438
+
#: src/components/moderation/ReportDialog/index.tsx:454
8439
msgid "Submit report"
8440
msgstr ""
8441
···
8636
msgid "Text field"
8637
msgstr ""
8638
8639
+
#: src/components/moderation/LabelsOnMeDialog.tsx:290
8640
#: src/screens/Messages/components/ChatDisabled.tsx:120
8641
msgid "Text input field"
8642
msgstr ""
···
8693
msgid "The app will be restarted"
8694
msgstr ""
8695
8696
+
#: src/components/moderation/ModerationDetailsDialog.tsx:121
8697
#: src/lib/moderation/useModerationCauseDescription.ts:128
8698
msgid "The author of this thread has hidden this reply."
8699
msgstr ""
···
8730
msgid "The feed has been replaced with Discover."
8731
msgstr ""
8732
8733
+
#: src/components/moderation/LabelsOnMeDialog.tsx:61
8734
msgid "The following labels were applied to your account."
8735
msgstr ""
8736
8737
+
#: src/components/moderation/LabelsOnMeDialog.tsx:62
8738
msgid "The following labels were applied to your content."
8739
msgstr ""
8740
···
8754
msgid "The open social network."
8755
msgstr ""
8756
8757
+
#: src/view/com/composer/select-language/SuggestedLanguage.tsx:119
8758
+
msgid "The post you're replying to was marked as being written in {suggestedLanguageName} by its author. Would you like to reply in <0>{suggestedLanguageName}</0>?"
8759
+
msgstr ""
8760
+
8761
#: src/view/screens/PrivacyPolicy.tsx:38
8762
msgid "The Privacy Policy has been moved to <0/>"
8763
msgstr ""
···
8888
msgstr ""
8889
8890
#: src/components/dialogs/GifSelect.tsx:258
8891
+
#: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:325
8892
#: src/view/com/util/ErrorBoundary.tsx:59
8893
msgid "There was an unexpected issue in the application. Please let us know if this happened to you!"
8894
msgstr ""
···
8925
msgid "This action can be undone at any time."
8926
msgstr ""
8927
8928
+
#: src/components/moderation/LabelsOnMeDialog.tsx:273
8929
msgid "This appeal will be sent to <0>{sourceName}</0>."
8930
msgstr ""
8931
···
8953
msgid "This content is hosted by {0}. Do you want to enable external media?"
8954
msgstr ""
8955
8956
+
#: src/components/moderation/ModerationDetailsDialog.tsx:86
8957
#: src/lib/moderation/useModerationCauseDescription.ts:84
8958
msgid "This content is not available because one of the users involved has blocked the other."
8959
msgstr ""
···
9017
msgid "This is not a valid link"
9018
msgstr ""
9019
9020
+
#: src/components/moderation/ModerationDetailsDialog.tsx:170
9021
msgid "This label was applied by the author."
9022
msgstr ""
9023
9024
+
#: src/components/moderation/LabelsOnMeDialog.tsx:167
9025
msgid "This label was applied by you."
9026
msgstr ""
9027
···
9069
msgid "This post will be hidden from feeds and threads. This cannot be undone."
9070
msgstr ""
9071
9072
+
#: src/view/com/composer/Composer.tsx:514
9073
msgid "This post's author has disabled quote posts."
9074
msgstr ""
9075
···
9105
msgid "This user has blocked you"
9106
msgstr ""
9107
9108
+
#: src/components/moderation/ModerationDetailsDialog.tsx:81
9109
#: src/lib/moderation/useModerationCauseDescription.ts:75
9110
msgid "This user has blocked you. You cannot view their content."
9111
msgstr ""
···
9114
msgid "This user has requested that their content only be shown to signed-in users."
9115
msgstr ""
9116
9117
+
#: src/components/moderation/ModerationDetailsDialog.tsx:61
9118
msgid "This user is included in the <0>{0}</0> list which you have blocked."
9119
msgstr ""
9120
9121
+
#: src/components/moderation/ModerationDetailsDialog.tsx:93
9122
msgid "This user is included in the <0>{0}</0> list which you have muted."
9123
msgstr ""
9124
···
9250
msgid "TV"
9251
msgstr ""
9252
9253
+
#: src/screens/Settings/PrivacyAndSecuritySettings.tsx:64
9254
msgid "Two-factor authentication (2FA)"
9255
msgstr ""
9256
···
9375
msgid "Unfortunately, Bluesky is unavailable in Mississippi right now."
9376
msgstr ""
9377
9378
+
#: src/components/moderation/ReportDialog/index.tsx:375
9379
msgid "Unfortunately, none of your subscribed labelers supports this report type."
9380
msgstr ""
9381
···
9497
msgid "Unsubscribed from list"
9498
msgstr ""
9499
9500
+
#: src/view/com/composer/Composer.tsx:894
9501
msgid "Unsupported video type: {mimeType}"
9502
msgstr ""
9503
···
9583
msgid "Uploading link thumbnail..."
9584
msgstr ""
9585
9586
+
#: src/view/com/composer/Composer.tsx:1886
9587
msgid "Uploading video..."
9588
msgstr ""
9589
···
9627
msgid "User blocked"
9628
msgstr ""
9629
9630
+
#: src/components/moderation/ModerationDetailsDialog.tsx:73
9631
#: src/lib/moderation/useModerationCauseDescription.ts:63
9632
msgid "User Blocked"
9633
msgstr ""
···
9640
msgid "User blocked by list"
9641
msgstr ""
9642
9643
+
#: src/components/moderation/ModerationDetailsDialog.tsx:59
9644
msgid "User Blocked by List"
9645
msgstr ""
9646
···
9648
msgid "User Blocking You"
9649
msgstr ""
9650
9651
+
#: src/components/moderation/ModerationDetailsDialog.tsx:79
9652
msgid "User Blocks You"
9653
msgstr ""
9654
···
9841
msgid "Video settings"
9842
msgstr ""
9843
9844
+
#: src/view/com/composer/Composer.tsx:1896
9845
msgid "Video uploaded"
9846
msgstr ""
9847
···
9857
msgid "Videos must be less than 3 minutes long."
9858
msgstr ""
9859
9860
+
#: src/view/com/composer/Composer.tsx:585
9861
msgctxt "Action to view the post the user just created"
9862
msgid "View"
9863
msgstr ""
9864
9865
+
#: src/screens/Profile/Header/Shell.tsx:241
9866
msgid "View {0}'s avatar"
9867
msgstr ""
9868
···
9910
9911
#: src/components/interstitials/TrendingVideos.tsx:198
9912
#: src/components/interstitials/TrendingVideos.tsx:220
9913
+
#: src/screens/Search/modules/ExploreTrendingVideos.tsx:193
9914
+
#: src/screens/Search/modules/ExploreTrendingVideos.tsx:212
9915
msgid "View more"
9916
msgstr ""
9917
···
9919
msgid "View more trending videos"
9920
msgstr ""
9921
9922
+
#: src/view/com/composer/Composer.tsx:580
9923
msgid "View post"
9924
msgstr ""
9925
···
10050
msgid "We have sent another verification email to <0>{0}</0>."
10051
msgstr ""
10052
10053
+
#: src/screens/Onboarding/StepFinished.tsx:531
10054
msgid "We hope you have a wonderful time. Remember, Bluesky is:"
10055
msgstr ""
10056
···
10147
msgid "We're sorry, but your search could not be completed. Please try again in a few minutes."
10148
msgstr ""
10149
10150
+
#: src/view/com/composer/Composer.tsx:511
10151
msgid "We're sorry! The post you are replying to has been deleted."
10152
msgstr ""
10153
···
10198
10199
#: src/view/com/auth/SplashScreen.tsx:51
10200
#: src/view/com/auth/SplashScreen.web.tsx:103
10201
+
#: src/view/com/composer/Composer.tsx:854
10202
msgid "What's up?"
10203
msgstr ""
10204
···
10229
msgstr ""
10230
10231
#: src/components/interstitials/TrendingVideos.tsx:125
10232
+
#: src/screens/Search/modules/ExploreTrendingVideos.tsx:107
10233
msgid "Whoops! Trending videos failed to load."
10234
msgstr ""
10235
···
10276
msgid "Write a message"
10277
msgstr ""
10278
10279
+
#: src/view/com/composer/Composer.tsx:954
10280
msgid "Write post"
10281
msgstr ""
10282
10283
#: src/screens/PostThread/components/ThreadComposePrompt.tsx:90
10284
+
#: src/view/com/composer/Composer.tsx:852
10285
msgid "Write your reply"
10286
msgstr ""
10287
···
10299
msgid "www.mylivestream.tv"
10300
msgstr ""
10301
10302
+
#: src/view/com/composer/select-language/SuggestedLanguage.tsx:181
10303
msgid "Yes"
10304
msgstr ""
10305
···
10466
msgid "You have blocked this user"
10467
msgstr ""
10468
10469
+
#: src/components/moderation/ModerationDetailsDialog.tsx:75
10470
#: src/lib/moderation/useModerationCauseDescription.ts:57
10471
#: src/lib/moderation/useModerationCauseDescription.ts:65
10472
msgid "You have blocked this user. You cannot view their content."
···
10486
msgid "You have hidden this post"
10487
msgstr ""
10488
10489
+
#: src/components/moderation/ModerationDetailsDialog.tsx:113
10490
msgid "You have hidden this post."
10491
msgstr ""
10492
10493
+
#: src/components/moderation/ModerationDetailsDialog.tsx:106
10494
#: src/lib/moderation/useModerationCauseDescription.ts:99
10495
msgid "You have muted this account."
10496
msgstr ""
···
10512
msgid "You have no lists."
10513
msgstr ""
10514
10515
+
#: src/components/dialogs/StarterPackDialog.tsx:100
10516
msgid "You have no starter packs."
10517
msgstr ""
10518
···
10520
msgid "You have not blocked any accounts yet. To block an account, go to their profile and select \"Block account\" from the menu on their account."
10521
msgstr ""
10522
10523
+
#: src/view/screens/ModerationMutedAccounts.tsx:177
10524
msgid "You have not muted any accounts yet. To mute an account, go to their profile and select \"Mute account\" from the menu on their account."
10525
msgstr ""
10526
···
10544
msgid "You haven't muted any words or tags yet"
10545
msgstr ""
10546
10547
+
#: src/components/moderation/ModerationDetailsDialog.tsx:120
10548
#: src/lib/moderation/useModerationCauseDescription.ts:127
10549
msgid "You hid this reply."
10550
msgstr ""
···
10561
msgid "You joined Bluesky using a starter pack {timeAgoString} ago"
10562
msgstr ""
10563
10564
+
#: src/components/moderation/LabelsOnMeDialog.tsx:81
10565
msgid "You may appeal non-self labels if you feel they were placed in error."
10566
msgstr ""
10567
10568
+
#: src/components/moderation/LabelsOnMeDialog.tsx:86
10569
msgid "You may appeal these labels if you feel they were placed in error."
10570
msgstr ""
10571
···
10695
msgid "You're in line"
10696
msgstr ""
10697
10698
+
#: src/screens/Onboarding/StepFinished.tsx:528
10699
msgid "You're ready to go!"
10700
msgstr ""
10701
···
10704
msgid "You're signed in with an App Password. Please sign in with your main password to continue deactivating your account."
10705
msgstr ""
10706
10707
+
#: src/components/moderation/ModerationDetailsDialog.tsx:110
10708
#: src/lib/moderation/useModerationCauseDescription.ts:108
10709
msgid "You've chosen to hide a word or tag within this post."
10710
msgstr ""
···
10859
msgid "Your password must be at least 8 characters long."
10860
msgstr ""
10861
10862
+
#: src/view/com/composer/Composer.tsx:576
10863
msgid "Your post was sent"
10864
msgstr ""
10865
10866
+
#: src/view/com/composer/Composer.tsx:573
10867
msgid "Your posts were sent"
10868
msgstr ""
10869
10870
+
#: src/screens/Onboarding/StepFinished.tsx:543
10871
msgid "Your posts, likes, and blocks are public. Mutes are private."
10872
msgstr ""
10873
···
10875
msgid "Your preferred language"
10876
msgstr ""
10877
10878
+
#: src/screens/Onboarding/StepFinished.tsx:421
10879
msgid "Your profile picture"
10880
msgstr ""
10881
10882
+
#: src/screens/Onboarding/StepFinished.tsx:349
10883
msgid "Your profile picture surrounded by concentric circles of other users' profile pictures"
10884
msgstr ""
10885
···
10887
msgid "Your profile, posts, feeds, and lists will no longer be visible to other Bluesky users. You can reactivate your account at any time by logging in."
10888
msgstr ""
10889
10890
+
#: src/view/com/composer/Composer.tsx:575
10891
msgid "Your reply was sent"
10892
msgstr ""
10893
10894
+
#: src/components/moderation/ReportDialog/index.tsx:397
10895
msgid "Your report will be sent to <0>{0}</0>."
10896
msgstr ""
10897
+10
-5
src/logger/metrics.ts
+10
-5
src/logger/metrics.ts
···
175
'feed:suggestion:press': {
176
feedUrl: string
177
}
178
-
'discover:showMore': {
179
feedContext: string
180
}
181
-
'discover:showLess': {
182
feedContext: string
183
}
184
-
'discover:clickthrough': {
185
count: number
186
}
187
-
'discover:engaged': {
188
count: number
189
}
190
-
'discover:seen': {
191
count: number
192
}
193
···
175
'feed:suggestion:press': {
176
feedUrl: string
177
}
178
+
'feed:showMore': {
179
+
feed: string
180
feedContext: string
181
}
182
+
'feed:showLess': {
183
+
feed: string
184
feedContext: string
185
}
186
+
'feed:clickthrough': {
187
+
feed: string
188
count: number
189
}
190
+
'feed:engaged': {
191
+
feed: string
192
count: number
193
}
194
+
'feed:seen': {
195
+
feed: string
196
count: number
197
}
198
-2
src/screens/Onboarding/StepFinished.tsx
-2
src/screens/Onboarding/StepFinished.tsx
···
69
import * as bsky from '#/types/bsky'
70
71
export function StepFinished() {
72
-
const {_} = useLingui()
73
const {state, dispatch} = useContext(Context)
74
const onboardDispatch = useOnboardingDispatch()
75
const [saving, setSaving] = useState(false)
···
495
496
function Dot({active}: {active: boolean}) {
497
const t = useTheme()
498
-
const {_} = useLingui()
499
500
return (
501
<View
···
69
import * as bsky from '#/types/bsky'
70
71
export function StepFinished() {
72
const {state, dispatch} = useContext(Context)
73
const onboardDispatch = useOnboardingDispatch()
74
const [saving, setSaving] = useState(false)
···
494
495
function Dot({active}: {active: boolean}) {
496
const t = useTheme()
497
498
return (
499
<View
+1
-1
src/screens/PostThread/components/ThreadItemAnchor.tsx
+1
-1
src/screens/PostThread/components/ThreadItemAnchor.tsx
+13
-12
src/screens/PostThread/index.tsx
+13
-12
src/screens/PostThread/index.tsx
···
148
*/
149
const shouldHandleScroll = useRef(true)
150
/**
151
-
* Called any time the content size of the list changes, _just_ before paint.
152
*
153
* We want this to fire every time we change params (which will reset
154
* `deferParents` via `onLayout` on the anchor post, due to the key change),
···
193
* will give us a _positive_ offset, which will scroll the anchor post
194
* back _up_ to the top of the screen.
195
*/
196
-
list.scrollToOffset({
197
-
offset: anchorOffsetTop - headerHeight,
198
-
})
199
200
/*
201
-
* After the second pass, `deferParents` will be `false`, and we need
202
-
* to ensure this doesn't run again until scroll handling is requested
203
-
* again via `shouldHandleScroll.current === true` and a params
204
-
* change via `prepareForParamsUpdate`.
205
*
206
* The `isRoot` here is needed because if we're looking at the anchor
207
* post, this handler will not fire after `deferParents` is set to
208
* `false`, since there are no parents to render above it. In this case,
209
-
* we want to make sure `shouldHandleScroll` is set to `false` so that
210
-
* subsequent size changes unrelated to a params change (like pagination)
211
-
* do not affect scroll.
212
*/
213
-
if (!deferParents || isRoot) shouldHandleScroll.current = false
214
}
215
})
216
···
148
*/
149
const shouldHandleScroll = useRef(true)
150
/**
151
+
* Called any time the content size of the list changes. Could be a fresh
152
+
* render, items being added to the list, or any resize that changes the
153
+
* scrollable size of the content.
154
*
155
* We want this to fire every time we change params (which will reset
156
* `deferParents` via `onLayout` on the anchor post, due to the key change),
···
195
* will give us a _positive_ offset, which will scroll the anchor post
196
* back _up_ to the top of the screen.
197
*/
198
+
const offset = anchorOffsetTop - headerHeight
199
+
list.scrollToOffset({offset})
200
201
/*
202
+
* After we manage to do a positive adjustment, we need to ensure this
203
+
* doesn't run again until scroll handling is requested again via
204
+
* `shouldHandleScroll.current === true` and a params change via
205
+
* `prepareForParamsUpdate`.
206
*
207
* The `isRoot` here is needed because if we're looking at the anchor
208
* post, this handler will not fire after `deferParents` is set to
209
* `false`, since there are no parents to render above it. In this case,
210
+
* we want to make sure `shouldHandleScroll` is set to `false` right away
211
+
* so that subsequent size changes unrelated to a params change (like
212
+
* pagination) do not affect scroll.
213
*/
214
+
if (offset > 0 || isRoot) shouldHandleScroll.current = false
215
}
216
})
217
+23
-11
src/screens/Profile/Header/Shell.tsx
+23
-11
src/screens/Profile/Header/Shell.tsx
···
209
210
{children}
211
212
-
{!isPlaceholderProfile && (
213
-
<View
214
-
style={[a.px_lg, a.pt_xs, a.pb_sm]}
215
-
pointerEvents={isIOS ? 'auto' : 'box-none'}>
216
-
{isMe ? (
217
-
<LabelsOnMe type="account" labels={profile.labels} />
218
-
) : (
219
-
<ProfileHeaderAlerts moderation={moderation} />
220
-
)}
221
-
</View>
222
-
)}
223
224
<GrowableAvatar style={[a.absolute, {top: 104, left: 10}]}>
225
<TouchableWithoutFeedback
···
209
210
{children}
211
212
+
{!isPlaceholderProfile &&
213
+
(isMe ? (
214
+
<LabelsOnMe
215
+
type="account"
216
+
labels={profile.labels}
217
+
style={[
218
+
a.px_lg,
219
+
a.pt_xs,
220
+
a.pb_sm,
221
+
isIOS ? a.pointer_events_auto : {pointerEvents: 'box-none'},
222
+
]}
223
+
/>
224
+
) : (
225
+
<ProfileHeaderAlerts
226
+
moderation={moderation}
227
+
style={[
228
+
a.px_lg,
229
+
a.pt_xs,
230
+
a.pb_sm,
231
+
isIOS ? a.pointer_events_auto : {pointerEvents: 'box-none'},
232
+
]}
233
+
/>
234
+
))}
235
236
<GrowableAvatar style={[a.absolute, {top: 104, left: 10}]}>
237
<TouchableWithoutFeedback
-1
src/screens/Search/Shell.tsx
-1
src/screens/Search/Shell.tsx
-1
src/screens/Search/modules/ExploreTrendingVideos.tsx
-1
src/screens/Search/modules/ExploreTrendingVideos.tsx
+1
-1
src/screens/Settings/AppPasswords.tsx
+1
-1
src/screens/Settings/AppPasswords.tsx
+2
-2
src/screens/Settings/NotificationSettings/ActivityNotificationSettings.tsx
+2
-2
src/screens/Settings/NotificationSettings/ActivityNotificationSettings.tsx
···
134
<Admonition.Outer type="tip">
135
<Admonition.Row>
136
<Admonition.Icon />
137
-
<View style={[a.flex_1, a.gap_sm]}>
138
<Admonition.Text>
139
<Trans>
140
Enable notifications for an account by visiting their
···
166
.
167
</Trans>
168
</Admonition.Text>
169
-
</View>
170
</Admonition.Row>
171
</Admonition.Outer>
172
) : (
···
134
<Admonition.Outer type="tip">
135
<Admonition.Row>
136
<Admonition.Icon />
137
+
<Admonition.Content>
138
<Admonition.Text>
139
<Trans>
140
Enable notifications for an account by visiting their
···
166
.
167
</Trans>
168
</Admonition.Text>
169
+
</Admonition.Content>
170
</Admonition.Row>
171
</Admonition.Outer>
172
) : (
+2
-3
src/screens/Settings/PrivacyAndSecuritySettings.tsx
+2
-3
src/screens/Settings/PrivacyAndSecuritySettings.tsx
···
1
-
import {View} from 'react-native'
2
import {type AppBskyNotificationDeclaration} from '@atproto/api'
3
import {msg, Trans} from '@lingui/macro'
4
import {useLingui} from '@lingui/react'
···
112
<Admonition.Outer type="tip" style={[a.flex_1]}>
113
<Admonition.Row>
114
<Admonition.Icon />
115
-
<View style={[a.flex_1, a.gap_sm]}>
116
<Admonition.Text>
117
<Trans>
118
Note: Bluesky is an open and public network. This setting
···
131
<Trans>Learn more about what is public on Bluesky.</Trans>
132
</InlineLinkText>
133
</Admonition.Text>
134
-
</View>
135
</Admonition.Row>
136
</Admonition.Outer>
137
</SettingsList.Item>
···
1
import {type AppBskyNotificationDeclaration} from '@atproto/api'
2
import {msg, Trans} from '@lingui/macro'
3
import {useLingui} from '@lingui/react'
···
111
<Admonition.Outer type="tip" style={[a.flex_1]}>
112
<Admonition.Row>
113
<Admonition.Icon />
114
+
<Admonition.Content>
115
<Admonition.Text>
116
<Trans>
117
Note: Bluesky is an open and public network. This setting
···
130
<Trans>Learn more about what is public on Bluesky.</Trans>
131
</InlineLinkText>
132
</Admonition.Text>
133
+
</Admonition.Content>
134
</Admonition.Row>
135
</Admonition.Outer>
136
</SettingsList.Item>
+41
-15
src/state/feed-feedback.tsx
+41
-15
src/state/feed-feedback.tsx
···
12
13
import {PROD_FEEDS, STAGING_FEEDS} from '#/lib/constants'
14
import {isNetworkError} from '#/lib/hooks/useCleanError'
15
-
import {logEvent} from '#/lib/statsig/statsig'
16
import {Logger} from '#/logger'
17
import {
18
type FeedSourceFeedInfo,
···
28
29
export const FEEDBACK_FEEDS = [...PROD_FEEDS, ...STAGING_FEEDS]
30
31
-
export const DIRECT_FEEDBACK_INTERACTIONS = new Set<
32
AppBskyFeedDefs.Interaction['event']
33
-
>(['app.bsky.feed.defs#requestLess', 'app.bsky.feed.defs#requestMore'])
34
35
const logger = Logger.create(Logger.Context.FeedFeedback)
36
···
78
const aggregatedStats = useRef<AggregatedStats | null>(null)
79
const throttledFlushAggregatedStats = useMemo(
80
() =>
81
-
throttle(() => flushToStatsig(aggregatedStats.current), 45e3, {
82
-
leading: true, // The outer call is already throttled somewhat.
83
-
trailing: true,
84
-
}),
85
-
[],
86
)
87
88
const sendToFeedNoDelay = useCallback(() => {
···
123
sendOrAggregateInteractionsForStats(
124
aggregatedStats.current,
125
interactionsToSend,
126
)
127
throttledFlushAggregatedStats()
128
logger.debug('flushed')
···
228
return false
229
}
230
const isDiscover = isDiscoverFeed(feed.feedDescriptor)
231
-
return isDiscover ? true : DIRECT_FEEDBACK_INTERACTIONS.has(interaction)
232
}
233
234
function toString(interaction: AppBskyFeedDefs.Interaction): string {
···
259
function sendOrAggregateInteractionsForStats(
260
stats: AggregatedStats,
261
interactions: AppBskyFeedDefs.Interaction[],
262
) {
263
for (let interaction of interactions) {
264
switch (interaction.event) {
265
// Pressing "Show more" / "Show less" is relatively uncommon so we won't aggregate them.
266
// This lets us send the feed context together with them.
267
case 'app.bsky.feed.defs#requestLess': {
268
-
logEvent('discover:showLess', {
269
feedContext: interaction.feedContext ?? '',
270
})
271
break
272
}
273
case 'app.bsky.feed.defs#requestMore': {
274
-
logEvent('discover:showMore', {
275
feedContext: interaction.feedContext ?? '',
276
})
277
break
···
301
}
302
}
303
304
-
function flushToStatsig(stats: AggregatedStats | null) {
305
if (stats === null) {
306
return
307
}
308
309
if (stats.clickthroughCount > 0) {
310
-
logEvent('discover:clickthrough', {
311
count: stats.clickthroughCount,
312
})
313
stats.clickthroughCount = 0
314
}
315
316
if (stats.engagedCount > 0) {
317
-
logEvent('discover:engaged', {
318
count: stats.engagedCount,
319
})
320
stats.engagedCount = 0
321
}
322
323
if (stats.seenCount > 0) {
324
-
logEvent('discover:seen', {
325
count: stats.seenCount,
326
})
327
stats.seenCount = 0
328
}
···
12
13
import {PROD_FEEDS, STAGING_FEEDS} from '#/lib/constants'
14
import {isNetworkError} from '#/lib/hooks/useCleanError'
15
import {Logger} from '#/logger'
16
import {
17
type FeedSourceFeedInfo,
···
27
28
export const FEEDBACK_FEEDS = [...PROD_FEEDS, ...STAGING_FEEDS]
29
30
+
export const THIRD_PARTY_ALLOWED_INTERACTIONS = new Set<
31
AppBskyFeedDefs.Interaction['event']
32
+
>([
33
+
// These are explicit actions and are therefore fine to send.
34
+
'app.bsky.feed.defs#requestLess',
35
+
'app.bsky.feed.defs#requestMore',
36
+
// These can be inferred from the firehose and are therefore fine to send.
37
+
'app.bsky.feed.defs#interactionLike',
38
+
'app.bsky.feed.defs#interactionQuote',
39
+
'app.bsky.feed.defs#interactionReply',
40
+
'app.bsky.feed.defs#interactionRepost',
41
+
// This can be inferred from pagination requests for everything except the very last page
42
+
// so it is fine to send. It is crucial for third party algorithmic feeds to receive these.
43
+
'app.bsky.feed.defs#interactionSeen',
44
+
])
45
46
const logger = Logger.create(Logger.Context.FeedFeedback)
47
···
89
const aggregatedStats = useRef<AggregatedStats | null>(null)
90
const throttledFlushAggregatedStats = useMemo(
91
() =>
92
+
throttle(
93
+
() =>
94
+
flushToStatsig(
95
+
aggregatedStats.current,
96
+
feed?.feedDescriptor ?? 'unknown',
97
+
),
98
+
45e3,
99
+
{
100
+
leading: true, // The outer call is already throttled somewhat.
101
+
trailing: true,
102
+
},
103
+
),
104
+
[feed?.feedDescriptor],
105
)
106
107
const sendToFeedNoDelay = useCallback(() => {
···
142
sendOrAggregateInteractionsForStats(
143
aggregatedStats.current,
144
interactionsToSend,
145
+
feed?.feedDescriptor ?? 'unknown',
146
)
147
throttledFlushAggregatedStats()
148
logger.debug('flushed')
···
248
return false
249
}
250
const isDiscover = isDiscoverFeed(feed.feedDescriptor)
251
+
return isDiscover ? true : THIRD_PARTY_ALLOWED_INTERACTIONS.has(interaction)
252
}
253
254
function toString(interaction: AppBskyFeedDefs.Interaction): string {
···
279
function sendOrAggregateInteractionsForStats(
280
stats: AggregatedStats,
281
interactions: AppBskyFeedDefs.Interaction[],
282
+
feed: string,
283
) {
284
for (let interaction of interactions) {
285
switch (interaction.event) {
286
// Pressing "Show more" / "Show less" is relatively uncommon so we won't aggregate them.
287
// This lets us send the feed context together with them.
288
case 'app.bsky.feed.defs#requestLess': {
289
+
logger.metric('feed:showLess', {
290
+
feed,
291
feedContext: interaction.feedContext ?? '',
292
})
293
break
294
}
295
case 'app.bsky.feed.defs#requestMore': {
296
+
logger.metric('feed:showMore', {
297
+
feed,
298
feedContext: interaction.feedContext ?? '',
299
})
300
break
···
324
}
325
}
326
327
+
function flushToStatsig(stats: AggregatedStats | null, feedDescriptor: string) {
328
if (stats === null) {
329
return
330
}
331
332
if (stats.clickthroughCount > 0) {
333
+
logger.metric('feed:clickthrough', {
334
count: stats.clickthroughCount,
335
+
feed: feedDescriptor,
336
})
337
stats.clickthroughCount = 0
338
}
339
340
if (stats.engagedCount > 0) {
341
+
logger.metric('feed:engaged', {
342
count: stats.engagedCount,
343
+
feed: feedDescriptor,
344
})
345
stats.engagedCount = 0
346
}
347
348
if (stats.seenCount > 0) {
349
+
logger.metric('feed:seen', {
350
count: stats.seenCount,
351
+
feed: feedDescriptor,
352
})
353
stats.seenCount = 0
354
}
+36
-1
src/state/geolocation/useSyncedDeviceGeolocation.ts
+36
-1
src/state/geolocation/useSyncedDeviceGeolocation.ts
···
1
import {useEffect, useRef} from 'react'
2
import * as Location from 'expo-location'
3
4
import {logger} from '#/state/geolocation/logger'
5
import {getDeviceGeolocation} from '#/state/geolocation/util'
6
import {device, useStorage} from '#/storage'
7
8
/**
9
* Hook to get and sync the device geolocation from the device GPS and store it
10
* using device storage. If permissions are not granted, it will clear any cached
11
* storage value.
12
*/
13
export function useSyncedDeviceGeolocation() {
14
const synced = useRef(false)
15
-
const [status] = Location.useForegroundPermissions()
16
const [deviceGeolocation, setDeviceGeolocation] = useStorage(device, [
17
'deviceGeolocation',
18
])
···
1
import {useEffect, useRef} from 'react'
2
import * as Location from 'expo-location'
3
+
import {createPermissionHook} from 'expo-modules-core'
4
5
import {logger} from '#/state/geolocation/logger'
6
import {getDeviceGeolocation} from '#/state/geolocation/util'
7
import {device, useStorage} from '#/storage'
8
9
/**
10
+
* Location.useForegroundPermissions on web just errors if the navigator.permissions API is not available.
11
+
* We need to catch and ignore it, since it's effectively denied.
12
+
* @see https://github.com/expo/expo/blob/72f1562ed9cce5ff6dfe04aa415b71632a3d4b87/packages/expo-location/src/Location.ts#L290-L293
13
+
*/
14
+
const useForegroundPermissions = createPermissionHook({
15
+
getMethod: () =>
16
+
Location.getForegroundPermissionsAsync().catch(error => {
17
+
logger.debug(
18
+
'useForegroundPermission: error getting location permissions',
19
+
{safeMessage: error},
20
+
)
21
+
return {
22
+
status: Location.PermissionStatus.DENIED,
23
+
granted: false,
24
+
canAskAgain: false,
25
+
expires: 0,
26
+
}
27
+
}),
28
+
requestMethod: () =>
29
+
Location.requestForegroundPermissionsAsync().catch(error => {
30
+
logger.debug(
31
+
'useForegroundPermission: error requesting location permissions',
32
+
{safeMessage: error},
33
+
)
34
+
return {
35
+
status: Location.PermissionStatus.DENIED,
36
+
granted: false,
37
+
canAskAgain: false,
38
+
expires: 0,
39
+
}
40
+
}),
41
+
})
42
+
43
+
/**
44
* Hook to get and sync the device geolocation from the device GPS and store it
45
* using device storage. If permissions are not granted, it will clear any cached
46
* storage value.
47
*/
48
export function useSyncedDeviceGeolocation() {
49
const synced = useRef(false)
50
+
const [status] = useForegroundPermissions()
51
const [deviceGeolocation, setDeviceGeolocation] = useStorage(device, [
52
'deviceGeolocation',
53
])
+1
-1
src/state/persisted/schema.ts
+1
-1
src/state/persisted/schema.ts
+4
src/state/preferences/languages.tsx
+4
src/state/preferences/languages.tsx
···
156
return postLanguage.split(',').filter(Boolean)
157
}
158
159
+
export function fromPostLanguages(languages: string[]): string {
160
+
return languages.filter(Boolean).join(',')
161
+
}
162
+
163
export function hasPostLanguage(postLanguage: string, code2: string): boolean {
164
return toPostLanguages(postLanguage).includes(code2)
165
}
+5
-5
src/state/queries/post-feed.ts
+5
-5
src/state/queries/post-feed.ts
···
492
}
493
}
494
} else if (feedDesc.startsWith('author')) {
495
-
const [_, actor, filter] = feedDesc.split('|')
496
return new AuthorFeedAPI({agent, feedParams: {actor, filter}})
497
} else if (feedDesc.startsWith('likes')) {
498
-
const [_, actor] = feedDesc.split('|')
499
return new LikesFeedAPI({agent, feedParams: {actor}})
500
} else if (feedDesc.startsWith('feedgen')) {
501
-
const [_, feed] = feedDesc.split('|')
502
return new CustomFeedAPI({
503
agent,
504
feedParams: {feed},
505
userInterests,
506
})
507
} else if (feedDesc.startsWith('list')) {
508
-
const [_, list] = feedDesc.split('|')
509
return new ListFeedAPI({agent, feedParams: {list}})
510
} else if (feedDesc.startsWith('posts')) {
511
-
const [_, uriList] = feedDesc.split('|')
512
return new PostListFeedAPI({agent, feedParams: {uris: uriList.split(',')}})
513
} else if (feedDesc === 'demo') {
514
return new DemoFeedAPI({agent})
···
492
}
493
}
494
} else if (feedDesc.startsWith('author')) {
495
+
const [__, actor, filter] = feedDesc.split('|')
496
return new AuthorFeedAPI({agent, feedParams: {actor, filter}})
497
} else if (feedDesc.startsWith('likes')) {
498
+
const [__, actor] = feedDesc.split('|')
499
return new LikesFeedAPI({agent, feedParams: {actor}})
500
} else if (feedDesc.startsWith('feedgen')) {
501
+
const [__, feed] = feedDesc.split('|')
502
return new CustomFeedAPI({
503
agent,
504
feedParams: {feed},
505
userInterests,
506
})
507
} else if (feedDesc.startsWith('list')) {
508
+
const [__, list] = feedDesc.split('|')
509
return new ListFeedAPI({agent, feedParams: {list}})
510
} else if (feedDesc.startsWith('posts')) {
511
+
const [__, uriList] = feedDesc.split('|')
512
return new PostListFeedAPI({agent, feedParams: {uris: uriList.split(',')}})
513
} else if (feedDesc === 'demo') {
514
return new DemoFeedAPI({agent})
+1
-1
src/state/queries/trending/useGetSuggestedUsersQuery.ts
+1
-1
src/state/queries/trending/useGetSuggestedUsersQuery.ts
+55
-6
src/view/com/composer/Composer.tsx
+55
-6
src/view/com/composer/Composer.tsx
···
88
import {useModalControls} from '#/state/modals'
89
import {useRequireAltTextEnabled} from '#/state/preferences'
90
import {
91
toPostLanguages,
92
useLanguagePrefs,
93
useLanguagePrefsApi,
···
197
const [publishingStage, setPublishingStage] = useState('')
198
const [error, setError] = useState('')
199
200
const [composerState, composerDispatch] = useReducer(
201
composerReducer,
202
{
···
414
thread,
415
replyTo: replyTo?.uri,
416
onStateChange: setPublishingStage,
417
-
langs: toPostLanguages(langPrefs.postLanguage),
418
})
419
).uris[0]
420
···
490
isPartOfThread: thread.posts.length > 1,
491
hasLink: !!post.embed.link,
492
hasQuote: !!post.embed.quote,
493
-
langs: langPrefs.postLanguage,
494
logContext: 'Composer',
495
})
496
index++
···
557
thread,
558
canPost,
559
isPublishing,
560
-
langPrefs.postLanguage,
561
onClose,
562
onPost,
563
onPostSuccess,
···
654
<>
655
<SuggestedLanguage
656
text={activePost.richtext.text}
657
-
// NOTE(@elijaharita): currently just choosing the first language if any exists
658
-
replyToLanguage={replyTo?.langs?.[0]}
659
/>
660
<ComposerPills
661
isReply={!!replyTo}
···
678
type: 'add_post',
679
})
680
}}
681
/>
682
</>
683
)
···
1289
onEmojiButtonPress,
1290
onSelectVideo,
1291
onAddPost,
1292
}: {
1293
post: PostDraft
1294
dispatch: (action: PostAction) => void
···
1297
onError: (error: string) => void
1298
onSelectVideo: (postId: string, asset: ImagePickerAsset) => void
1299
onAddPost: () => void
1300
}) {
1301
const t = useTheme()
1302
const {_} = useLingui()
···
1450
<PlusIcon size="lg" />
1451
</Button>
1452
)}
1453
-
<PostLanguageSelect />
1454
<CharProgress
1455
count={post.shortenedGraphemeLength}
1456
style={{width: 65}}
···
88
import {useModalControls} from '#/state/modals'
89
import {useRequireAltTextEnabled} from '#/state/preferences'
90
import {
91
+
fromPostLanguages,
92
toPostLanguages,
93
useLanguagePrefs,
94
useLanguagePrefsApi,
···
198
const [publishingStage, setPublishingStage] = useState('')
199
const [error, setError] = useState('')
200
201
+
/**
202
+
* A temporary local reference to a language suggestion that the user has
203
+
* accepted. This overrides the global post language preference, but is not
204
+
* stored permanently.
205
+
*/
206
+
const [acceptedLanguageSuggestion, setAcceptedLanguageSuggestion] = useState<
207
+
string | null
208
+
>(null)
209
+
210
+
/**
211
+
* The language(s) of the post being replied to.
212
+
*/
213
+
const [replyToLanguages, setReplyToLanguages] = useState<string[]>(
214
+
replyTo?.langs || [],
215
+
)
216
+
217
+
/**
218
+
* The currently selected languages of the post. Prefer local temporary
219
+
* language suggestion over global lang prefs, if available.
220
+
*/
221
+
const currentLanguages = useMemo(
222
+
() =>
223
+
acceptedLanguageSuggestion
224
+
? [acceptedLanguageSuggestion]
225
+
: toPostLanguages(langPrefs.postLanguage),
226
+
[acceptedLanguageSuggestion, langPrefs.postLanguage],
227
+
)
228
+
229
+
/**
230
+
* When the user selects a language from the composer language selector,
231
+
* clear any temporary language suggestions they may have selected
232
+
* previously, and any we might try to suggest to them.
233
+
*/
234
+
const onSelectLanguage = () => {
235
+
setAcceptedLanguageSuggestion(null)
236
+
setReplyToLanguages([])
237
+
}
238
+
239
const [composerState, composerDispatch] = useReducer(
240
composerReducer,
241
{
···
453
thread,
454
replyTo: replyTo?.uri,
455
onStateChange: setPublishingStage,
456
+
langs: currentLanguages,
457
})
458
).uris[0]
459
···
529
isPartOfThread: thread.posts.length > 1,
530
hasLink: !!post.embed.link,
531
hasQuote: !!post.embed.quote,
532
+
langs: fromPostLanguages(currentLanguages),
533
logContext: 'Composer',
534
})
535
index++
···
596
thread,
597
canPost,
598
isPublishing,
599
+
currentLanguages,
600
onClose,
601
onPost,
602
onPostSuccess,
···
693
<>
694
<SuggestedLanguage
695
text={activePost.richtext.text}
696
+
replyToLanguages={replyToLanguages}
697
+
currentLanguages={currentLanguages}
698
+
onAcceptSuggestedLanguage={setAcceptedLanguageSuggestion}
699
/>
700
<ComposerPills
701
isReply={!!replyTo}
···
718
type: 'add_post',
719
})
720
}}
721
+
currentLanguages={currentLanguages}
722
+
onSelectLanguage={onSelectLanguage}
723
/>
724
</>
725
)
···
1331
onEmojiButtonPress,
1332
onSelectVideo,
1333
onAddPost,
1334
+
currentLanguages,
1335
+
onSelectLanguage,
1336
}: {
1337
post: PostDraft
1338
dispatch: (action: PostAction) => void
···
1341
onError: (error: string) => void
1342
onSelectVideo: (postId: string, asset: ImagePickerAsset) => void
1343
onAddPost: () => void
1344
+
currentLanguages: string[]
1345
+
onSelectLanguage?: (language: string) => void
1346
}) {
1347
const t = useTheme()
1348
const {_} = useLingui()
···
1496
<PlusIcon size="lg" />
1497
</Button>
1498
)}
1499
+
<PostLanguageSelect
1500
+
currentLanguages={currentLanguages}
1501
+
onSelectLanguage={onSelectLanguage}
1502
+
/>
1503
<CharProgress
1504
count={post.shortenedGraphemeLength}
1505
style={{width: 65}}
-1
src/view/com/composer/photos/EditImageDialog.web.tsx
-1
src/view/com/composer/photos/EditImageDialog.web.tsx
+35
-9
src/view/com/composer/select-language/PostLanguageSelect.tsx
+35
-9
src/view/com/composer/select-language/PostLanguageSelect.tsx
···
17
import {Text} from '#/components/Typography'
18
import {PostLanguageSelectDialog} from './PostLanguageSelectDialog'
19
20
-
export function PostLanguageSelect() {
21
const {_} = useLingui()
22
const langPrefs = useLanguagePrefs()
23
const setLangPrefs = useLanguagePrefsApi()
···
26
const dedupedHistory = Array.from(
27
new Set([...langPrefs.postLanguageHistory, langPrefs.postLanguage]),
28
)
29
30
if (
31
dedupedHistory.length === 1 &&
···
34
return (
35
<>
36
<LanguageBtn onPress={languageDialogControl.open} />
37
-
<PostLanguageSelectDialog control={languageDialogControl} />
38
</>
39
)
40
}
···
43
<>
44
<Menu.Root>
45
<Menu.Trigger label={_(msg`Select post language`)}>
46
-
{({props}) => <LanguageBtn {...props} />}
47
</Menu.Trigger>
48
<Menu.Outer>
49
<Menu.Group>
···
56
<Menu.Item
57
key={historyItem}
58
label={_(msg`Select ${langName}`)}
59
-
onPress={() => setLangPrefs.setPostLanguage(historyItem)}>
60
<Menu.ItemText>{langName}</Menu.ItemText>
61
<Menu.ItemRadio
62
-
selected={historyItem === langPrefs.postLanguage}
63
/>
64
</Menu.Item>
65
)
···
77
</Menu.Outer>
78
</Menu.Root>
79
80
-
<PostLanguageSelectDialog control={languageDialogControl} />
81
</>
82
)
83
}
84
85
-
function LanguageBtn(props: Omit<ButtonProps, 'label' | 'children'>) {
86
const {_} = useLingui()
87
const langPrefs = useLanguagePrefs()
88
const t = useTheme()
89
90
const postLanguagesPref = toPostLanguages(langPrefs.postLanguage)
91
92
return (
93
<Button
···
106
{({pressed, hovered}) => {
107
const color =
108
pressed || hovered ? t.palette.primary_300 : t.palette.primary_500
109
-
if (postLanguagesPref.length > 0) {
110
return (
111
<Text
112
style={[
···
117
{maxWidth: 100},
118
]}
119
numberOfLines={1}>
120
-
{postLanguagesPref
121
.map(lang => codeToLanguageName(lang, langPrefs.appLanguage))
122
.join(', ')}
123
</Text>
···
17
import {Text} from '#/components/Typography'
18
import {PostLanguageSelectDialog} from './PostLanguageSelectDialog'
19
20
+
export function PostLanguageSelect({
21
+
currentLanguages: currentLanguagesProp,
22
+
onSelectLanguage,
23
+
}: {
24
+
currentLanguages?: string[]
25
+
onSelectLanguage?: (language: string) => void
26
+
}) {
27
const {_} = useLingui()
28
const langPrefs = useLanguagePrefs()
29
const setLangPrefs = useLanguagePrefsApi()
···
32
const dedupedHistory = Array.from(
33
new Set([...langPrefs.postLanguageHistory, langPrefs.postLanguage]),
34
)
35
+
36
+
const currentLanguages =
37
+
currentLanguagesProp ?? toPostLanguages(langPrefs.postLanguage)
38
39
if (
40
dedupedHistory.length === 1 &&
···
43
return (
44
<>
45
<LanguageBtn onPress={languageDialogControl.open} />
46
+
<PostLanguageSelectDialog
47
+
control={languageDialogControl}
48
+
currentLanguages={currentLanguages}
49
+
/>
50
</>
51
)
52
}
···
55
<>
56
<Menu.Root>
57
<Menu.Trigger label={_(msg`Select post language`)}>
58
+
{({props}) => (
59
+
<LanguageBtn currentLanguages={currentLanguages} {...props} />
60
+
)}
61
</Menu.Trigger>
62
<Menu.Outer>
63
<Menu.Group>
···
70
<Menu.Item
71
key={historyItem}
72
label={_(msg`Select ${langName}`)}
73
+
onPress={() => {
74
+
setLangPrefs.setPostLanguage(historyItem)
75
+
onSelectLanguage?.(historyItem)
76
+
}}>
77
<Menu.ItemText>{langName}</Menu.ItemText>
78
<Menu.ItemRadio
79
+
selected={currentLanguages.includes(historyItem)}
80
/>
81
</Menu.Item>
82
)
···
94
</Menu.Outer>
95
</Menu.Root>
96
97
+
<PostLanguageSelectDialog
98
+
control={languageDialogControl}
99
+
currentLanguages={currentLanguages}
100
+
onSelectLanguage={onSelectLanguage}
101
+
/>
102
</>
103
)
104
}
105
106
+
function LanguageBtn(
107
+
props: Omit<ButtonProps, 'label' | 'children'> & {
108
+
currentLanguages?: string[]
109
+
},
110
+
) {
111
const {_} = useLingui()
112
const langPrefs = useLanguagePrefs()
113
const t = useTheme()
114
115
const postLanguagesPref = toPostLanguages(langPrefs.postLanguage)
116
+
const currentLanguages = props.currentLanguages ?? postLanguagesPref
117
118
return (
119
<Button
···
132
{({pressed, hovered}) => {
133
const color =
134
pressed || hovered ? t.palette.primary_300 : t.palette.primary_500
135
+
if (currentLanguages.length > 0) {
136
return (
137
<Text
138
style={[
···
143
{maxWidth: 100},
144
]}
145
numberOfLines={1}>
146
+
{currentLanguages
147
.map(lang => codeToLanguageName(lang, langPrefs.appLanguage))
148
.join(', ')}
149
</Text>
+25
-3
src/view/com/composer/select-language/PostLanguageSelectDialog.tsx
+25
-3
src/view/com/composer/select-language/PostLanguageSelectDialog.tsx
···
8
import {type Language, LANGUAGES, LANGUAGES_MAP_CODE2} from '#/locale/languages'
9
import {isNative, isWeb} from '#/platform/detection'
10
import {
11
useLanguagePrefs,
12
useLanguagePrefsApi,
13
} from '#/state/preferences/languages'
···
23
24
export function PostLanguageSelectDialog({
25
control,
26
}: {
27
control: Dialog.DialogControlProps
28
}) {
29
const {height} = useWindowDimensions()
30
const insets = useSafeAreaInsets()
···
40
nativeOptions={{minHeight: height - insets.top}}>
41
<Dialog.Handle />
42
<ErrorBoundary renderError={renderErrorBoundary}>
43
-
<DialogInner />
44
</ErrorBoundary>
45
</Dialog.Outer>
46
)
47
}
48
49
-
export function DialogInner() {
50
const control = Dialog.useDialogContext()
51
const [headerHeight, setHeaderHeight] = useState(0)
52
···
63
}, [])
64
65
const langPrefs = useLanguagePrefs()
66
const [checkedLanguagesCode2, setCheckedLanguagesCode2] = useState<string[]>(
67
-
langPrefs.postLanguage.split(',') || [langPrefs.primaryLanguage],
68
)
69
const [search, setSearch] = useState('')
70
···
79
langsString = langPrefs.primaryLanguage
80
}
81
setLangPrefs.setPostLanguage(langsString)
82
})
83
}
84
···
8
import {type Language, LANGUAGES, LANGUAGES_MAP_CODE2} from '#/locale/languages'
9
import {isNative, isWeb} from '#/platform/detection'
10
import {
11
+
toPostLanguages,
12
useLanguagePrefs,
13
useLanguagePrefsApi,
14
} from '#/state/preferences/languages'
···
24
25
export function PostLanguageSelectDialog({
26
control,
27
+
/**
28
+
* Optionally can be passed to show different values than what is saved in
29
+
* langPrefs.
30
+
*/
31
+
currentLanguages,
32
+
onSelectLanguage,
33
}: {
34
control: Dialog.DialogControlProps
35
+
currentLanguages?: string[]
36
+
onSelectLanguage?: (language: string) => void
37
}) {
38
const {height} = useWindowDimensions()
39
const insets = useSafeAreaInsets()
···
49
nativeOptions={{minHeight: height - insets.top}}>
50
<Dialog.Handle />
51
<ErrorBoundary renderError={renderErrorBoundary}>
52
+
<DialogInner
53
+
currentLanguages={currentLanguages}
54
+
onSelectLanguage={onSelectLanguage}
55
+
/>
56
</ErrorBoundary>
57
</Dialog.Outer>
58
)
59
}
60
61
+
export function DialogInner({
62
+
currentLanguages,
63
+
onSelectLanguage,
64
+
}: {
65
+
currentLanguages?: string[]
66
+
onSelectLanguage?: (language: string) => void
67
+
}) {
68
const control = Dialog.useDialogContext()
69
const [headerHeight, setHeaderHeight] = useState(0)
70
···
81
}, [])
82
83
const langPrefs = useLanguagePrefs()
84
+
const postLanguagesPref =
85
+
currentLanguages ?? toPostLanguages(langPrefs.postLanguage)
86
+
87
const [checkedLanguagesCode2, setCheckedLanguagesCode2] = useState<string[]>(
88
+
postLanguagesPref || [langPrefs.primaryLanguage],
89
)
90
const [search, setSearch] = useState('')
91
···
100
langsString = langPrefs.primaryLanguage
101
}
102
setLangPrefs.setPostLanguage(langsString)
103
+
onSelectLanguage?.(langsString)
104
})
105
}
106
+121
-44
src/view/com/composer/select-language/SuggestedLanguage.tsx
+121
-44
src/view/com/composer/select-language/SuggestedLanguage.tsx
···
1
import {useEffect, useState} from 'react'
2
-
import {View} from 'react-native'
3
import {parseLanguage} from '@atproto/api'
4
import {msg, Trans} from '@lingui/macro'
5
import {useLingui} from '@lingui/react'
6
import lande from 'lande'
7
8
import {code3ToCode2Strict, codeToLanguageName} from '#/locale/helpers'
9
-
import {
10
-
toPostLanguages,
11
-
useLanguagePrefs,
12
-
useLanguagePrefsApi,
13
-
} from '#/state/preferences/languages'
14
import {atoms as a, useTheme} from '#/alf'
15
import {Button, ButtonText} from '#/components/Button'
16
import {Earth_Stroke2_Corner2_Rounded as EarthIcon} from '#/components/icons/Globe'
···
22
23
export function SuggestedLanguage({
24
text,
25
-
replyToLanguage: replyToLanguageProp,
26
}: {
27
text: string
28
-
replyToLanguage?: string
29
}) {
30
-
const replyToLanguage = cleanUpLanguage(replyToLanguageProp)
31
const [suggestedLanguage, setSuggestedLanguage] = useState<
32
string | undefined
33
-
>(text.length === 0 ? replyToLanguage : undefined)
34
-
const langPrefs = useLanguagePrefs()
35
-
const setLangPrefs = useLanguagePrefsApi()
36
-
const t = useTheme()
37
-
const {_} = useLingui()
38
39
useEffect(() => {
40
-
// For replies, suggest the language of the post being replied to if no text
41
-
// has been typed yet
42
-
if (replyToLanguage && text.length === 0) {
43
-
setSuggestedLanguage(replyToLanguage)
44
-
return
45
}
46
47
const textTrimmed = text.trim()
48
49
// Don't run the language model on small posts, the results are likely
···
58
})
59
60
return () => cancelIdle(idle)
61
-
}, [text, replyToLanguage])
62
63
-
if (
64
-
suggestedLanguage &&
65
-
!toPostLanguages(langPrefs.postLanguage).includes(suggestedLanguage)
66
-
) {
67
const suggestedLanguageName = codeToLanguageName(
68
suggestedLanguage,
69
langPrefs.appLanguage,
70
)
71
72
return (
73
<View
74
style={[
75
-
t.atoms.border_contrast_low,
76
-
a.gap_sm,
77
a.border,
78
a.flex_row,
79
a.align_center,
80
a.rounded_sm,
81
-
a.px_lg,
82
-
a.py_md,
83
-
a.mx_md,
84
-
a.my_sm,
85
t.atoms.bg,
86
]}>
87
<EarthIcon />
88
-
<Text style={[a.flex_1]}>
89
-
<Trans>
90
-
Are you writing in{' '}
91
-
<Text style={[a.font_semi_bold]}>{suggestedLanguageName}</Text>?
92
-
</Trans>
93
-
</Text>
94
95
<Button
96
-
color="secondary"
97
size="small"
98
-
variant="solid"
99
-
onPress={() => setLangPrefs.setPostLanguage(suggestedLanguage)}
100
-
label={_(msg`Change post language to ${suggestedLanguageName}`)}>
101
<ButtonText>
102
<Trans>Yes</Trans>
103
</ButtonText>
104
</Button>
105
</View>
106
-
)
107
-
} else {
108
-
return null
109
-
}
110
}
111
112
/**
···
1
import {useEffect, useState} from 'react'
2
+
import {Text as RNText, View} from 'react-native'
3
import {parseLanguage} from '@atproto/api'
4
import {msg, Trans} from '@lingui/macro'
5
import {useLingui} from '@lingui/react'
6
import lande from 'lande'
7
8
import {code3ToCode2Strict, codeToLanguageName} from '#/locale/helpers'
9
+
import {useLanguagePrefs} from '#/state/preferences/languages'
10
import {atoms as a, useTheme} from '#/alf'
11
import {Button, ButtonText} from '#/components/Button'
12
import {Earth_Stroke2_Corner2_Rounded as EarthIcon} from '#/components/icons/Globe'
···
18
19
export function SuggestedLanguage({
20
text,
21
+
replyToLanguages: replyToLanguagesProp,
22
+
currentLanguages,
23
+
onAcceptSuggestedLanguage,
24
}: {
25
text: string
26
+
/**
27
+
* All languages associated with the post being replied to.
28
+
*/
29
+
replyToLanguages: string[]
30
+
/**
31
+
* All languages currently selected for the post being composed.
32
+
*/
33
+
currentLanguages: string[]
34
+
/**
35
+
* Called when the user accepts a suggested language. We only pass a single
36
+
* language here. If the post being replied to has multiple languages, we
37
+
* only suggest the first one.
38
+
*/
39
+
onAcceptSuggestedLanguage: (language: string | null) => void
40
}) {
41
+
const langPrefs = useLanguagePrefs()
42
+
const replyToLanguages = replyToLanguagesProp
43
+
.map(lang => cleanUpLanguage(lang))
44
+
.filter(Boolean) as string[]
45
+
const [hasInteracted, setHasInteracted] = useState(false)
46
const [suggestedLanguage, setSuggestedLanguage] = useState<
47
string | undefined
48
+
>(undefined)
49
50
useEffect(() => {
51
+
if (text.length > 0 && !hasInteracted) {
52
+
setHasInteracted(true)
53
}
54
+
}, [text, hasInteracted])
55
56
+
useEffect(() => {
57
const textTrimmed = text.trim()
58
59
// Don't run the language model on small posts, the results are likely
···
68
})
69
70
return () => cancelIdle(idle)
71
+
}, [text])
72
+
73
+
/*
74
+
* We've detected a language, and the user hasn't already selected it.
75
+
*/
76
+
const hasLanguageSuggestion =
77
+
suggestedLanguage && !currentLanguages.includes(suggestedLanguage)
78
+
/*
79
+
* We have not detected a different language, and the user is not already
80
+
* using or has not already selected one of the languages of the post they
81
+
* are replying to.
82
+
*/
83
+
const hasSuggestedReplyLanguage =
84
+
!hasInteracted &&
85
+
!suggestedLanguage &&
86
+
replyToLanguages.length &&
87
+
!replyToLanguages.some(l => currentLanguages.includes(l))
88
89
+
if (hasLanguageSuggestion) {
90
const suggestedLanguageName = codeToLanguageName(
91
suggestedLanguage,
92
langPrefs.appLanguage,
93
)
94
95
return (
96
+
<LanguageSuggestionButton
97
+
label={
98
+
<RNText>
99
+
<Trans>
100
+
Are you writing in{' '}
101
+
<Text style={[a.font_bold]}>{suggestedLanguageName}</Text>?
102
+
</Trans>
103
+
</RNText>
104
+
}
105
+
value={suggestedLanguage}
106
+
onAccept={onAcceptSuggestedLanguage}
107
+
/>
108
+
)
109
+
} else if (hasSuggestedReplyLanguage) {
110
+
const suggestedLanguageName = codeToLanguageName(
111
+
replyToLanguages[0],
112
+
langPrefs.appLanguage,
113
+
)
114
+
115
+
return (
116
+
<LanguageSuggestionButton
117
+
label={
118
+
<RNText>
119
+
<Trans>
120
+
The post you're replying to was marked as being written in{' '}
121
+
{suggestedLanguageName} by its author. Would you like to reply in{' '}
122
+
<Text style={[a.font_bold]}>{suggestedLanguageName}</Text>?
123
+
</Trans>
124
+
</RNText>
125
+
}
126
+
value={replyToLanguages[0]}
127
+
onAccept={onAcceptSuggestedLanguage}
128
+
/>
129
+
)
130
+
} else {
131
+
return null
132
+
}
133
+
}
134
+
135
+
function LanguageSuggestionButton({
136
+
label,
137
+
value,
138
+
onAccept,
139
+
}: {
140
+
label: React.ReactNode
141
+
value: string
142
+
onAccept: (language: string | null) => void
143
+
}) {
144
+
const t = useTheme()
145
+
const {_} = useLingui()
146
+
147
+
return (
148
+
<View style={[a.px_lg, a.py_sm]}>
149
<View
150
style={[
151
+
a.gap_md,
152
a.border,
153
a.flex_row,
154
a.align_center,
155
a.rounded_sm,
156
+
a.p_md,
157
+
a.pl_lg,
158
t.atoms.bg,
159
+
t.atoms.border_contrast_low,
160
]}>
161
<EarthIcon />
162
+
<View style={[a.flex_1]}>
163
+
<Text
164
+
style={[
165
+
a.flex_1,
166
+
a.leading_snug,
167
+
{
168
+
maxWidth: 400,
169
+
},
170
+
]}>
171
+
{label}
172
+
</Text>
173
+
</View>
174
175
<Button
176
size="small"
177
+
color="secondary"
178
+
onPress={() => onAccept(value)}
179
+
label={_(msg`Accept this language suggestion`)}>
180
<ButtonText>
181
<Trans>Yes</Trans>
182
</ButtonText>
183
</Button>
184
</View>
185
+
</View>
186
+
)
187
}
188
189
/**
+1
-1
src/view/com/composer/text-input/web/TagDecorator.ts
+1
-1
src/view/com/composer/text-input/web/TagDecorator.ts
+1
-1
src/view/com/posts/PostFeedErrorMessage.tsx
+1
-1
src/view/com/posts/PostFeedErrorMessage.tsx
-2
src/view/screens/ModerationMutedAccounts.tsx
-2
src/view/screens/ModerationMutedAccounts.tsx
···
2
import {type StyleProp, View, type ViewStyle} from 'react-native'
3
import {type AppBskyActorDefs as ActorDefs} from '@atproto/api'
4
import {Trans} from '@lingui/macro'
5
-
import {useLingui} from '@lingui/react'
6
import {useFocusEffect} from '@react-navigation/native'
7
import {type NativeStackScreenProps} from '@react-navigation/native-stack'
8
···
27
export function ModerationMutedAccounts({}: Props) {
28
const t = useTheme()
29
const moderationOpts = useModerationOpts()
30
-
const {_} = useLingui()
31
const setMinimalShellMode = useSetMinimalShellMode()
32
33
const [isPTRing, setIsPTRing] = useState(false)
···
2
import {type StyleProp, View, type ViewStyle} from 'react-native'
3
import {type AppBskyActorDefs as ActorDefs} from '@atproto/api'
4
import {Trans} from '@lingui/macro'
5
import {useFocusEffect} from '@react-navigation/native'
6
import {type NativeStackScreenProps} from '@react-navigation/native-stack'
7
···
26
export function ModerationMutedAccounts({}: Props) {
27
const t = useTheme()
28
const moderationOpts = useModerationOpts()
29
const setMinimalShellMode = useSetMinimalShellMode()
30
31
const [isPTRing, setIsPTRing] = useState(false)
-2
src/view/screens/ProfileFeedLikedBy.tsx
-2
src/view/screens/ProfileFeedLikedBy.tsx
···
1
import {useCallback} from 'react'
2
import {Trans} from '@lingui/macro'
3
-
import {useLingui} from '@lingui/react'
4
import {useFocusEffect} from '@react-navigation/native'
5
6
import {
···
17
const setMinimalShellMode = useSetMinimalShellMode()
18
const {name, rkey} = route.params
19
const uri = makeRecordUri(name, 'app.bsky.feed.generator', rkey)
20
-
const {_} = useLingui()
21
22
useFocusEffect(
23
useCallback(() => {
···
1
import {useCallback} from 'react'
2
import {Trans} from '@lingui/macro'
3
import {useFocusEffect} from '@react-navigation/native'
4
5
import {
···
16
const setMinimalShellMode = useSetMinimalShellMode()
17
const {name, rkey} = route.params
18
const uri = makeRecordUri(name, 'app.bsky.feed.generator', rkey)
19
20
useFocusEffect(
21
useCallback(() => {
+74
-3
src/view/screens/Storybook/Admonitions.tsx
+74
-3
src/view/screens/Storybook/Admonitions.tsx
···
1
-
import {View} from 'react-native'
2
3
-
import {atoms as a} from '#/alf'
4
-
import {Admonition} from '#/components/Admonition'
5
import {InlineLinkText} from '#/components/Link'
6
import {H1} from '#/components/Typography'
7
8
export function Admonitions() {
9
return (
10
<View style={[a.gap_md]}>
11
<H1>Admonitions</H1>
···
30
<Admonition type="error">
31
The quick brown fox jumps over the lazy dog.
32
</Admonition>
33
</View>
34
)
35
}
···
1
+
import {Text as RNText, View} from 'react-native'
2
+
import {msg, Trans} from '@lingui/macro'
3
+
import {useLingui} from '@lingui/react'
4
5
+
import {atoms as a, useTheme} from '#/alf'
6
+
import {
7
+
Admonition,
8
+
Button as AdmonitionButton,
9
+
Content as AdmonitionContent,
10
+
Icon as AdmonitionIcon,
11
+
Outer as AdmonitionOuter,
12
+
Row as AdmonitionRow,
13
+
Text as AdmonitionText,
14
+
} from '#/components/Admonition'
15
+
import {ButtonIcon, ButtonText} from '#/components/Button'
16
+
import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as Retry} from '#/components/icons/ArrowRotateCounterClockwise'
17
+
import {BellRinging_Filled_Corner0_Rounded as BellRingingFilledIcon} from '#/components/icons/BellRinging'
18
import {InlineLinkText} from '#/components/Link'
19
import {H1} from '#/components/Typography'
20
21
export function Admonitions() {
22
+
const {_} = useLingui()
23
+
const t = useTheme()
24
+
25
return (
26
<View style={[a.gap_md]}>
27
<H1>Admonitions</H1>
···
46
<Admonition type="error">
47
The quick brown fox jumps over the lazy dog.
48
</Admonition>
49
+
50
+
<AdmonitionOuter type="error">
51
+
<AdmonitionRow>
52
+
<AdmonitionIcon />
53
+
<AdmonitionContent>
54
+
<AdmonitionText>
55
+
<Trans>Something went wrong, please try again</Trans>
56
+
</AdmonitionText>
57
+
</AdmonitionContent>
58
+
<AdmonitionButton
59
+
color="negative_subtle"
60
+
label={_(msg`Retry loading report options`)}
61
+
onPress={() => {}}>
62
+
<ButtonText>
63
+
<Trans>Retry</Trans>
64
+
</ButtonText>
65
+
<ButtonIcon icon={Retry} />
66
+
</AdmonitionButton>
67
+
</AdmonitionRow>
68
+
</AdmonitionOuter>
69
+
70
+
<AdmonitionOuter type="tip">
71
+
<AdmonitionRow>
72
+
<AdmonitionIcon />
73
+
<AdmonitionContent>
74
+
<AdmonitionText>
75
+
<Trans>
76
+
Enable notifications for an account by visiting their profile
77
+
and pressing the{' '}
78
+
<RNText style={[a.font_bold, t.atoms.text_contrast_high]}>
79
+
bell icon
80
+
</RNText>{' '}
81
+
<BellRingingFilledIcon
82
+
size="xs"
83
+
style={t.atoms.text_contrast_high}
84
+
/>
85
+
.
86
+
</Trans>
87
+
</AdmonitionText>
88
+
<AdmonitionText>
89
+
<Trans>
90
+
If you want to restrict who can receive notifications for your
91
+
account's activity, you can change this in{' '}
92
+
<InlineLinkText
93
+
label={_(msg`Privacy and Security settings`)}
94
+
to={{screen: 'ActivityPrivacySettings'}}
95
+
style={[a.font_bold]}>
96
+
Settings → Privacy and Security
97
+
</InlineLinkText>
98
+
.
99
+
</Trans>
100
+
</AdmonitionText>
101
+
</AdmonitionContent>
102
+
</AdmonitionRow>
103
+
</AdmonitionOuter>
104
</View>
105
)
106
}
+25
src/view/screens/Storybook/Buttons.tsx
+25
src/view/screens/Storybook/Buttons.tsx
···
8
ButtonIcon,
9
type ButtonSize,
10
ButtonText,
11
} from '#/components/Button'
12
import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron'
13
import {Globe_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe'
···
17
return (
18
<View style={[a.gap_md]}>
19
<Text style={[a.font_bold, a.text_5xl]}>Buttons</Text>
20
21
{[
22
'primary',
···
8
ButtonIcon,
9
type ButtonSize,
10
ButtonText,
11
+
StackedButton,
12
} from '#/components/Button'
13
import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron'
14
import {Globe_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe'
···
18
return (
19
<View style={[a.gap_md]}>
20
<Text style={[a.font_bold, a.text_5xl]}>Buttons</Text>
21
+
22
+
<View style={[a.flex_row, a.gap_md, a.align_start, {maxWidth: 350}]}>
23
+
<StackedButton
24
+
label="stacked"
25
+
icon={Globe}
26
+
color="secondary"
27
+
style={[a.flex_1]}>
28
+
Bop it
29
+
</StackedButton>
30
+
<StackedButton
31
+
label="stacked"
32
+
icon={Globe}
33
+
color="negative_subtle"
34
+
style={[a.flex_1]}>
35
+
Twist it
36
+
</StackedButton>
37
+
<StackedButton
38
+
label="stacked"
39
+
icon={Globe}
40
+
color="primary"
41
+
style={[a.flex_1]}>
42
+
Pull it
43
+
</StackedButton>
44
+
</View>
45
46
{[
47
'primary',
+5
-17
yarn.lock
+5
-17
yarn.lock
···
8646
dependencies:
8647
"@babel/helper-define-polyfill-provider" "^0.6.3"
8648
8649
-
babel-plugin-react-compiler@^19.1.0-rc.1:
8650
-
version "19.1.0-rc.1"
8651
-
resolved "https://registry.yarnpkg.com/babel-plugin-react-compiler/-/babel-plugin-react-compiler-19.1.0-rc.1.tgz#99d131be61017e40abbaedd98321069bf8b7e54a"
8652
-
integrity sha512-M4fpG+Hfq5gWzsJeeMErdRokzg0fdJ8IAk+JDhfB/WLT+U3WwJWR8edphypJrk447/JEvYu6DBFwsTn10bMW4Q==
8653
-
dependencies:
8654
-
"@babel/types" "^7.26.0"
8655
-
8656
-
babel-plugin-react-compiler@^19.1.0-rc.2:
8657
version "19.1.0-rc.3"
8658
resolved "https://registry.yarnpkg.com/babel-plugin-react-compiler/-/babel-plugin-react-compiler-19.1.0-rc.3.tgz#45e5a282a2460b3701971e5eb8310a90a7919022"
8659
integrity sha512-mjRn69WuTz4adL0bXGx8Rsyk1086zFJeKmes6aK0xPuK3aaXmDJdLHqwKKMrpm6KAI1MCoUK72d2VeqQbu8YIA==
···
10867
dependencies:
10868
"@typescript-eslint/utils" "^5.61.0"
10869
10870
-
eslint-plugin-react-compiler@^19.1.0-rc.1:
10871
-
version "19.1.0-rc.1"
10872
-
resolved "https://registry.yarnpkg.com/eslint-plugin-react-compiler/-/eslint-plugin-react-compiler-19.1.0-rc.1.tgz#e974ba9541c9a4464d77723e0505b5742bc22e56"
10873
-
integrity sha512-3umw5eqZXapBl7aQGmvcjheKhUbsElb9jTETxRZg371e1LG4EPs/zCHt2JzP+wNcdaZWzjU/R730zPUJblY2zw==
10874
dependencies:
10875
"@babel/core" "^7.24.4"
10876
"@babel/parser" "^7.24.4"
···
17113
convert-source-map "^2.0.0"
17114
invariant "^2.2.4"
17115
react-native-is-edge-to-edge "1.1.7"
17116
-
17117
-
react-native-root-siblings@^5.0.1:
17118
-
version "5.0.1"
17119
-
resolved "https://registry.yarnpkg.com/react-native-root-siblings/-/react-native-root-siblings-5.0.1.tgz#97e050e5155228f65810fb1c466ff8e769c5272c"
17120
-
integrity sha512-Ay3k/fBj6ReUkWX5WNS+oEAcgPLEGOK8n7K/L7D85mf3xvd8rm/b4spsv26E4HlFzluVx5HKbxEt9cl0wQ1u3g==
17121
17122
react-native-safe-area-context@~5.6.0:
17123
version "5.6.1"
···
8646
dependencies:
8647
"@babel/helper-define-polyfill-provider" "^0.6.3"
8648
8649
+
babel-plugin-react-compiler@^19.1.0-rc.2, babel-plugin-react-compiler@^19.1.0-rc.3:
8650
version "19.1.0-rc.3"
8651
resolved "https://registry.yarnpkg.com/babel-plugin-react-compiler/-/babel-plugin-react-compiler-19.1.0-rc.3.tgz#45e5a282a2460b3701971e5eb8310a90a7919022"
8652
integrity sha512-mjRn69WuTz4adL0bXGx8Rsyk1086zFJeKmes6aK0xPuK3aaXmDJdLHqwKKMrpm6KAI1MCoUK72d2VeqQbu8YIA==
···
10860
dependencies:
10861
"@typescript-eslint/utils" "^5.61.0"
10862
10863
+
eslint-plugin-react-compiler@^19.1.0-rc.2:
10864
+
version "19.1.0-rc.2"
10865
+
resolved "https://registry.yarnpkg.com/eslint-plugin-react-compiler/-/eslint-plugin-react-compiler-19.1.0-rc.2.tgz#83343e7422e00fa61e729af8e8468f0ddec37925"
10866
+
integrity sha512-oKalwDGcD+RX9mf3NEO4zOoUMeLvjSvcbbEOpquzmzqEEM2MQdp7/FY/Hx9NzmUwFzH1W9SKTz5fihfMldpEYw==
10867
dependencies:
10868
"@babel/core" "^7.24.4"
10869
"@babel/parser" "^7.24.4"
···
17106
convert-source-map "^2.0.0"
17107
invariant "^2.2.4"
17108
react-native-is-edge-to-edge "1.1.7"
17109
17110
react-native-safe-area-context@~5.6.0:
17111
version "5.6.1"