+2
-2
Dockerfile
+2
-2
Dockerfile
+2
-2
Dockerfile.embedr
+2
-2
Dockerfile.embedr
+1
-1
__tests__/lib/link-meta.test.ts
+1
-1
__tests__/lib/link-meta.test.ts
+1
assets/icons/bulletlist_stroke1_corner0_rounded.svg
+1
assets/icons/bulletlist_stroke1_corner0_rounded.svg
···
1
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 46 38"><path stroke="#405168" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M22.333 31.667H45M22.333 6.333H45m-33.333 0A5.333 5.333 0 1 1 1 6.333a5.333 5.333 0 0 1 10.667 0Zm0 25.334a5.333 5.333 0 1 1-10.667 0 5.333 5.333 0 0 1 10.667 0Z"/></svg>
+1
assets/icons/circle_and_square_stroke1_corner0_rounded_filled.svg
+1
assets/icons/circle_and_square_stroke1_corner0_rounded_filled.svg
···
1
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 62 53"><path fill="#405168" d="M28.173.231a5.653 5.653 0 0 1 7.018 3.83l2.66 9.046a20 20 0 0 1 3.986-.397c11.026 0 19.964 8.937 19.964 19.962l-.006.516c-.274 10.787-9.104 19.448-19.958 19.448l-.514-.007c-8.332-.21-15.394-5.528-18.178-12.938l-8.805 2.59a5.654 5.654 0 0 1-7.02-3.83L.232 14.34a5.654 5.654 0 0 1 3.83-7.018L28.172.23ZM41.838 14.71c-1.17 0-2.313.111-3.42.325l3.863 13.137a5.653 5.653 0 0 1-3.83 7.019L25.07 39.126c2.593 6.732 9.122 11.51 16.768 11.51 9.92 0 17.963-8.043 17.963-17.964S51.758 14.71 41.837 14.71ZM33.271 4.624a3.653 3.653 0 0 0-4.535-2.474L4.624 9.24a3.653 3.653 0 0 0-2.475 4.535l7.09 24.113a3.654 3.654 0 0 0 4.536 2.475l8.762-2.577a20 20 0 0 1-.662-5.114c0-8.961 5.905-16.544 14.037-19.069l-2.64-8.98Zm3.204 10.899c-7.302 2.28-12.601 9.096-12.601 17.15 0 1.571.203 3.095.582 4.548l13.431-3.948a3.654 3.654 0 0 0 2.474-4.536l-3.886-13.214Z"/></svg>
+1
assets/icons/editbig_stroke1_corner0_rounded.svg
+1
assets/icons/editbig_stroke1_corner0_rounded.svg
···
1
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 48 48"><path fill="#405168" d="M19.667 4.458a1 1 0 1 0 0-2v2Zm25 23a1 1 0 0 0-2 0h2ZM3.912 45.543l.454-.891-.454.89Zm-2.33-2.33.89-.455h0l-.89.454Zm39.173 2.33-.454-.891h0l.454.89Zm2.33-2.33-.89-.455.89.454ZM1.581 6.37l-.89-.454h0l.89.454Zm2.331-2.331-.454-.891.454.89ZM14.333 32.79h-1a1 1 0 0 0 1 1v-1Zm.781-8.781.707.707-.707-.707ZM36.562 2.562l-.707-.707v0l.707.707Zm7.543 0-.707.707v0l.707-.707Zm.457.458.707-.707v0l-.707.707Zm0 7.542.707.707-.707-.707ZM23.114 32.01l.707.707-.707-.707Zm12.02 14.114v-1h-25.6v2h25.6v-1ZM1 37.591h1v-25.6H0v25.6h1ZM9.533 3.458v1h10.134v-2H9.533v1Zm34.134 24h-1V37.59h2V27.457h-1ZM9.533 46.124v-1c-1.51 0-2.582 0-3.421-.07-.828-.067-1.34-.195-1.746-.402l-.454.89-.454.892c.735.374 1.54.537 2.491.614.94.077 2.107.076 3.584.076v-1ZM1 37.591H0c0 1.477 0 2.645.076 3.584.078.951.24 1.756.614 2.491l.891-.454.891-.454c-.207-.406-.335-.918-.403-1.746C2.001 40.173 2 39.101 2 37.591H1Zm2.912 7.952.454-.891a4.33 4.33 0 0 1-1.894-1.894l-.89.454-.892.454a6.33 6.33 0 0 0 2.768 2.768l.454-.891Zm31.221.581v1c1.477 0 2.645.001 3.585-.076.95-.078 1.756-.24 2.49-.614l-.453-.891-.454-.891c-.406.207-.919.335-1.746.403-.84.068-1.912.07-3.422.07v1Zm8.534-8.533h-1c0 1.51-.001 2.582-.07 3.421-.067.828-.196 1.34-.403 1.746l.891.454.891.454c.375-.735.537-1.54.615-2.49.076-.94.076-2.108.076-3.585h-1Zm-2.912 7.952.454.89a6.33 6.33 0 0 0 2.767-2.767l-.89-.454-.892-.454a4.33 4.33 0 0 1-1.893 1.894l.454.89ZM1 11.99h1c0-1.51 0-2.582.07-3.422.067-.827.195-1.34.402-1.745l-.89-.454-.892-.454c-.374.734-.536 1.54-.614 2.49C-.001 9.345 0 10.513 0 11.99h1Zm8.533-8.533v-1c-1.477 0-2.645-.001-3.584.076-.951.077-1.756.24-2.49.614l.453.89.454.892c.406-.207.918-.336 1.746-.403.839-.069 1.911-.07 3.421-.07v-1ZM1.581 6.37l.891.454A4.33 4.33 0 0 1 4.366 4.93l-.454-.891-.454-.891A6.33 6.33 0 0 0 .69 5.916l.891.454Zm12.752 19.525h-1v6.896h2v-6.896h-1Zm0 6.896v1h6.896v-2h-6.896v1Zm.781-8.781.707.707L37.27 3.269l-.707-.707-.707-.707-21.448 21.448.707.707Zm28.99-21.448-.706.707.457.458.707-.707.707-.707-.457-.458-.707.707Zm.458 8-.707-.707-21.448 21.448.707.707.707.707L45.27 11.269l-.707-.707Zm0-7.542-.707.707a4.333 4.333 0 0 1 0 6.128l.707.707.707.707a6.333 6.333 0 0 0 0-8.956l-.707.707Zm-8-.458.707.707a4.333 4.333 0 0 1 6.129 0l.707-.707.707-.707a6.333 6.333 0 0 0-8.957 0l.707.707ZM21.23 32.791v1c.972 0 1.905-.386 2.593-1.074l-.708-.707-.707-.707a1.67 1.67 0 0 1-1.178.488v1Zm-6.896-6.896h1c0-.442.176-.866.489-1.178l-.708-.707-.707-.707a3.67 3.67 0 0 0-1.074 2.592h1Z"/></svg>
+1
assets/icons/hashtagwide_stroke1_corner0_rounded.svg
+1
assets/icons/hashtagwide_stroke1_corner0_rounded.svg
···
1
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 46 46"><path stroke="#405168" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.333 1 9 45M37 1l-5.333 44M1 11.667h44m0 22.666H1"/></svg>
+1
assets/icons/heart2_stroke1_corner0_rounded.svg
+1
assets/icons/heart2_stroke1_corner0_rounded.svg
···
1
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 50 45"><path stroke="#405168" stroke-linejoin="round" stroke-width="2" d="M49 17c0 15.333-22 26.667-24 26.667S1 32.333 1 17C1 6.333 7.667 1 14.333 1S25 5 25 5s4-4 10.667-4S49 6.333 49 17Z"/></svg>
+1
assets/icons/image_stroke1_corner0_rounded.svg
+1
assets/icons/image_stroke1_corner0_rounded.svg
···
1
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 46 46"><path stroke="#080B12" stroke-linecap="round" stroke-width="1.5" d="m1.417 28.645 7.586-5.676a5.33 5.33 0 0 1 6.867.809c3.98 4.286 8.594 8.182 14.88 8.182 5.794 0 9.633-2.147 13.333-5.847m-38 18.637h33.334a5.333 5.333 0 0 0 5.333-5.333V6.083A5.333 5.333 0 0 0 39.417.75H6.083A5.333 5.333 0 0 0 .75 6.083v33.334a5.333 5.333 0 0 0 5.333 5.333ZM36.75 14.083a5.333 5.333 0 1 1-10.667 0 5.333 5.333 0 0 1 10.667 0Z"/></svg>
+1
assets/icons/message_stroke1_corner0_rounded_filled.svg
+1
assets/icons/message_stroke1_corner0_rounded_filled.svg
···
1
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 50 50"><path stroke="#405168" stroke-linecap="square" stroke-linejoin="round" stroke-width="2" d="M9 1h32a8 8 0 0 1 8 8v21.333a8 8 0 0 1-8 8H27.667L14.333 49V38.333H9a8 8 0 0 1-8-8V9a8 8 0 0 1 8-8Z"/></svg>
+1
assets/icons/peopleremove2_stroke1_corner0_rounded.svg
+1
assets/icons/peopleremove2_stroke1_corner0_rounded.svg
···
1
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 43 48"><path stroke="#405168" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.603 46.333H3.532c-1.572 0-2.816-1.358-2.472-2.891 2.033-9.046 9.421-15.775 19.543-15.775q1.367 0 2.666.16m18.667 7.84L36.603 41m0 0-5.334 5.333M36.603 41l-5.334-5.333M36.603 41l5.333 5.333m-12-36A9.333 9.333 0 1 1 20.603 1a9.333 9.333 0 0 1 9.333 9.333Z"/></svg>
+1
assets/icons/videoclip_stroke1_corner0_rounded.svg
+1
assets/icons/videoclip_stroke1_corner0_rounded.svg
···
1
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 46 46"><path stroke="#405168" stroke-linecap="square" stroke-linejoin="round" stroke-width="2" d="M1 23h10.667M1 23V12m0 11v11m10.667-11h22.666m-22.666 0v11m0-11V12m22.666 11H45m-10.667 0v12.222m0-12.222V12M45 23V12m0 11v12.222M34.333 45h5.334A5.333 5.333 0 0 0 45 39.667v-4.445M34.333 45v-9.778m0 9.778H11.667M34.333 1h5.334A5.333 5.333 0 0 1 45 6.333V12M34.333 1v11m0-11H11.667m22.666 11H45M34.333 35.222H45M11.667 45H6.333A5.333 5.333 0 0 1 1 39.667V34m10.667 11V34m0-33H6.333A5.333 5.333 0 0 0 1 6.333V12M11.667 1v11M1 12h10.667M1 34h10.667"/></svg>
+1
-1
bskyweb/go.mod
+1
-1
bskyweb/go.mod
+2
-1
src/components/Button.tsx
+2
-1
src/components/Button.tsx
+27
src/components/Lists.tsx
+27
src/components/Lists.tsx
···
4
4
import {useLingui} from '@lingui/react'
5
5
6
6
import {cleanError} from '#/lib/strings/errors'
7
+
import {
8
+
EmptyState,
9
+
type EmptyStateButtonProps,
10
+
} from '#/view/com/util/EmptyState'
7
11
import {CenteredView} from '#/view/com/util/Views'
8
12
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
9
13
import {Button, ButtonText} from '#/components/Button'
···
129
133
hideBackButton,
130
134
sideBorders,
131
135
topBorder = false,
136
+
emptyStateIcon,
137
+
emptyStateButton,
138
+
useEmptyState = false,
132
139
}: {
133
140
isLoading: boolean
134
141
noEmpty?: boolean
···
143
150
hideBackButton?: boolean
144
151
sideBorders?: boolean
145
152
topBorder?: boolean
153
+
emptyStateIcon?: React.ComponentType<any> | React.ReactElement
154
+
emptyStateButton?: EmptyStateButtonProps
155
+
useEmptyState?: boolean
146
156
}): React.ReactNode => {
147
157
const t = useTheme()
148
158
const {_} = useLingui()
···
177
187
sideBorders={sideBorders}
178
188
hideBackButton={hideBackButton}
179
189
/>
190
+
)
191
+
}
192
+
193
+
if (useEmptyState) {
194
+
return (
195
+
<View style={[t.atoms.border_contrast_low]}>
196
+
<EmptyState
197
+
icon={emptyStateIcon}
198
+
message={
199
+
emptyMessage ??
200
+
(emptyType === 'results'
201
+
? _(msg`No results found`)
202
+
: _(msg`Page not found`))
203
+
}
204
+
button={emptyStateButton}
205
+
/>
206
+
</View>
180
207
)
181
208
}
182
209
+8
-1
src/components/StarterPack/Main/PostsList.tsx
+8
-1
src/components/StarterPack/Main/PostsList.tsx
···
9
9
import {EmptyState} from '#/view/com/util/EmptyState'
10
10
import {type ListRef} from '#/view/com/util/List'
11
11
import {type SectionRef} from '#/screens/Profile/Sections/types'
12
+
import {HashtagWide_Stroke1_Corner0_Rounded as HashtagWideIcon} from '#/components/icons/Hashtag'
12
13
13
14
interface ProfilesListProps {
14
15
listUri: string
···
33
34
}))
34
35
35
36
const renderPostsEmpty = useCallback(() => {
36
-
return <EmptyState icon="hashtag" message={_(msg`This feed is empty.`)} />
37
+
return (
38
+
<EmptyState
39
+
icon={HashtagWideIcon}
40
+
iconSize="2xl"
41
+
message={_(msg`This feed is empty.`)}
42
+
/>
43
+
)
37
44
}, [_])
38
45
39
46
return (
+33
-1
src/components/StarterPack/ProfileStarterPacks.tsx
+33
-1
src/components/StarterPack/ProfileStarterPacks.tsx
···
21
21
import {logger} from '#/logger'
22
22
import {isIOS} from '#/platform/detection'
23
23
import {useActorStarterPacksQuery} from '#/state/queries/actor-starter-packs'
24
+
import {
25
+
EmptyState,
26
+
type EmptyStateButtonProps,
27
+
} from '#/view/com/util/EmptyState'
24
28
import {List, type ListRef} from '#/view/com/util/List'
25
29
import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
26
30
import {atoms as a, ios, useTheme} from '#/alf'
···
47
51
testID?: string
48
52
setScrollViewTag: (tag: number | null) => void
49
53
isMe: boolean
54
+
emptyStateMessage?: string
55
+
emptyStateButton?: EmptyStateButtonProps
56
+
emptyStateIcon?: React.ComponentType<any> | React.ReactElement
50
57
}
51
58
52
59
function keyExtractor(item: AppBskyGraphDefs.StarterPackView) {
···
63
70
testID,
64
71
setScrollViewTag,
65
72
isMe,
73
+
emptyStateMessage,
74
+
emptyStateButton,
75
+
emptyStateIcon,
66
76
}: ProfileFeedgensProps) {
67
77
const t = useTheme()
68
78
const bottomBarOffset = useBottomBarOffset(100)
···
79
89
const {isTabletOrDesktop} = useWebMediaQueries()
80
90
81
91
const items = data?.pages.flatMap(page => page.starterPacks)
92
+
const {_} = useLingui()
93
+
94
+
const EmptyComponent = useCallback(() => {
95
+
if (emptyStateMessage || emptyStateButton || emptyStateIcon) {
96
+
return (
97
+
<View style={[a.px_lg, a.align_center, a.justify_center]}>
98
+
<EmptyState
99
+
icon={emptyStateIcon}
100
+
iconSize="3xl"
101
+
message={
102
+
emptyStateMessage ??
103
+
_(
104
+
'Starter packs let you share your favorite feeds and people with your friends.',
105
+
)
106
+
}
107
+
button={emptyStateButton}
108
+
/>
109
+
</View>
110
+
)
111
+
}
112
+
return <Empty />
113
+
}, [_, emptyStateMessage, emptyStateButton, emptyStateIcon])
82
114
83
115
useImperativeHandle(ref, () => ({
84
116
scrollToTop: () => {},
···
146
178
onEndReached={onEndReached}
147
179
onRefresh={onRefresh}
148
180
ListEmptyComponent={
149
-
data ? (isMe ? Empty : undefined) : FeedLoadingPlaceholder
181
+
data ? (isMe ? EmptyComponent : undefined) : FeedLoadingPlaceholder
150
182
}
151
183
ListFooterComponent={
152
184
!!data && items?.length !== 0 && isMe ? CreateAnother : undefined
+8
src/components/icons/BulletList.tsx
+8
src/components/icons/BulletList.tsx
···
1
1
import {createSinglePathSVG} from './TEMPLATE'
2
2
3
+
export const BulletList_Stroke1_Corner0_Rounded = createSinglePathSVG({
4
+
viewBox: '0 0 47 38',
5
+
strokeLinecap: 'round',
6
+
strokeLinejoin: 'round',
7
+
strokeWidth: 2,
8
+
path: 'M22.333 31.667H45M22.333 6.333H45m-33.333 0A5.333 5.333 0 1 1 1 6.333a5.333 5.333 0 0 1 10.667 0Zm0 25.334a5.333 5.333 0 1 1-10.667 0 5.333 5.333 0 0 1 10.667 0Z',
9
+
})
10
+
3
11
export const BulletList_Stroke2_Corner0_Rounded = createSinglePathSVG({
4
12
path: 'M6 6a1 1 0 1 0 0 2 1 1 0 0 0 0-2ZM3 7a3 3 0 1 1 6 0 3 3 0 0 1-6 0Zm9 0a1 1 0 0 1 1-1h7a1 1 0 1 1 0 2h-7a1 1 0 0 1-1-1Zm-6 9a1 1 0 1 0 0 2 1 1 0 0 0 0-2Zm-3 1a3 3 0 1 1 6 0 3 3 0 0 1-6 0Zm9 0a1 1 0 0 1 1-1h7a1 1 0 1 1 0 2h-7a1 1 0 0 1-1-1Z',
5
13
})
+7
src/components/icons/CircleAndSquare.tsx
+7
src/components/icons/CircleAndSquare.tsx
···
1
+
import {createSinglePathSVG} from './TEMPLATE'
2
+
3
+
export const Circle_And_Square_Stroke1_Corner0_Rounded_Filled =
4
+
createSinglePathSVG({
5
+
viewBox: '0 0 62 53',
6
+
path: 'M28.173.231a5.653 5.653 0 0 1 7.018 3.83l2.66 9.046a20 20 0 0 1 3.986-.397c11.026 0 19.964 8.937 19.964 19.962l-.006.516c-.274 10.787-9.104 19.448-19.958 19.448l-.514-.007c-8.332-.21-15.394-5.528-18.178-12.938l-8.805 2.59a5.654 5.654 0 0 1-7.02-3.83L.232 14.34a5.654 5.654 0 0 1 3.83-7.018L28.172.23ZM41.838 14.71c-1.17 0-2.313.111-3.42.325l3.863 13.137a5.653 5.653 0 0 1-3.83 7.019L25.07 39.126c2.593 6.732 9.122 11.51 16.768 11.51 9.92 0 17.963-8.043 17.963-17.964S51.758 14.71 41.837 14.71ZM33.271 4.624a3.653 3.653 0 0 0-4.535-2.474L4.624 9.24a3.653 3.653 0 0 0-2.475 4.535l7.09 24.113a3.654 3.654 0 0 0 4.536 2.475l8.762-2.577a20 20 0 0 1-.662-5.114c0-8.961 5.905-16.544 14.037-19.069l-2.64-8.98Zm3.204 10.899c-7.302 2.28-12.601 9.096-12.601 17.15 0 1.571.203 3.095.582 4.548l13.431-3.948a3.654 3.654 0 0 0 2.474-4.536l-3.886-13.214Z',
7
+
})
+5
src/components/icons/EditBig.tsx
+5
src/components/icons/EditBig.tsx
···
1
1
import {createSinglePathSVG} from './TEMPLATE'
2
2
3
+
export const EditBig_Stroke1_Corner0_Rounded = createSinglePathSVG({
4
+
viewBox: '0 0 48 48',
5
+
path: 'M19.667 4.458a1 1 0 1 0 0-2v2Zm25 23a1 1 0 0 0-2 0h2ZM3.912 45.543l.454-.891-.454.89Zm-2.33-2.33.89-.455h0l-.89.454Zm39.173 2.33-.454-.891h0l.454.89Zm2.33-2.33-.89-.455.89.454ZM1.581 6.37l-.89-.454h0l.89.454Zm2.331-2.331-.454-.891.454.89ZM14.333 32.79h-1a1 1 0 0 0 1 1v-1Zm.781-8.781.707.707-.707-.707ZM36.562 2.562l-.707-.707v0l.707.707Zm7.543 0-.707.707v0l.707-.707Zm.457.458.707-.707v0l-.707.707Zm0 7.542.707.707-.707-.707ZM23.114 32.01l.707.707-.707-.707Zm12.02 14.114v-1h-25.6v2h25.6v-1ZM1 37.591h1v-25.6H0v25.6h1ZM9.533 3.458v1h10.134v-2H9.533v1Zm34.134 24h-1V37.59h2V27.457h-1ZM9.533 46.124v-1c-1.51 0-2.582 0-3.421-.07-.828-.067-1.34-.195-1.746-.402l-.454.89-.454.892c.735.374 1.54.537 2.491.614.94.077 2.107.076 3.584.076v-1ZM1 37.591H0c0 1.477 0 2.645.076 3.584.078.951.24 1.756.614 2.491l.891-.454.891-.454c-.207-.406-.335-.918-.403-1.746C2.001 40.173 2 39.101 2 37.591H1Zm2.912 7.952.454-.891a4.33 4.33 0 0 1-1.894-1.894l-.89.454-.892.454a6.33 6.33 0 0 0 2.768 2.768l.454-.891Zm31.221.581v1c1.477 0 2.645.001 3.585-.076.95-.078 1.756-.24 2.49-.614l-.453-.891-.454-.891c-.406.207-.919.335-1.746.403-.84.068-1.912.07-3.422.07v1Zm8.534-8.533h-1c0 1.51-.001 2.582-.07 3.421-.067.828-.196 1.34-.403 1.746l.891.454.891.454c.375-.735.537-1.54.615-2.49.076-.94.076-2.108.076-3.585h-1Zm-2.912 7.952.454.89a6.33 6.33 0 0 0 2.767-2.767l-.89-.454-.892-.454a4.33 4.33 0 0 1-1.893 1.894l.454.89ZM1 11.99h1c0-1.51 0-2.582.07-3.422.067-.827.195-1.34.402-1.745l-.89-.454-.892-.454c-.374.734-.536 1.54-.614 2.49C-.001 9.345 0 10.513 0 11.99h1Zm8.533-8.533v-1c-1.477 0-2.645-.001-3.584.076-.951.077-1.756.24-2.49.614l.453.89.454.892c.406-.207.918-.336 1.746-.403.839-.069 1.911-.07 3.421-.07v-1ZM1.581 6.37l.891.454A4.33 4.33 0 0 1 4.366 4.93l-.454-.891-.454-.891A6.33 6.33 0 0 0 .69 5.916l.891.454Zm12.752 19.525h-1v6.896h2v-6.896h-1Zm0 6.896v1h6.896v-2h-6.896v1Zm.781-8.781.707.707L37.27 3.269l-.707-.707-.707-.707-21.448 21.448.707.707Zm28.99-21.448-.706.707.457.458.707-.707.707-.707-.457-.458-.707.707Zm.458 8-.707-.707-21.448 21.448.707.707.707.707L45.27 11.269l-.707-.707Zm0-7.542-.707.707a4.333 4.333 0 0 1 0 6.128l.707.707.707.707a6.333 6.333 0 0 0 0-8.956l-.707.707Zm-8-.458.707.707a4.333 4.333 0 0 1 6.129 0l.707-.707.707-.707a6.333 6.333 0 0 0-8.957 0l.707.707ZM21.23 32.791v1c.972 0 1.905-.386 2.593-1.074l-.708-.707-.707-.707a1.67 1.67 0 0 1-1.178.488v1Zm-6.896-6.896h1c0-.442.176-.866.489-1.178l-.708-.707-.707-.707a3.67 3.67 0 0 0-1.074 2.592h1Z',
6
+
})
7
+
3
8
export const EditBig_Stroke2_Corner0_Rounded = createSinglePathSVG({
4
9
path: 'M17.293 2.293a1 1 0 0 1 1.414 0l3 3a1 1 0 0 1 0 1.414l-9 9A1 1 0 0 1 12 16H9a1 1 0 0 1-1-1v-3a1 1 0 0 1 .293-.707l9-9ZM10 12.414V14h1.586l8-8L18 4.414l-8 8ZM3 4a1 1 0 0 1 1-1h7a1 1 0 1 1 0 2H5v14h14v-6a1 1 0 1 1 2 0v7a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4Z',
5
10
})
+8
src/components/icons/Hashtag.tsx
+8
src/components/icons/Hashtag.tsx
···
1
1
import {createSinglePathSVG} from './TEMPLATE'
2
2
3
+
export const HashtagWide_Stroke1_Corner0_Rounded = createSinglePathSVG({
4
+
viewBox: '0 0 46 46',
5
+
strokeLinecap: 'round',
6
+
strokeLinejoin: 'round',
7
+
strokeWidth: 2,
8
+
path: 'M14.333 1 9 45M37 1l-5.333 44M1 11.667h44m0 22.666H1',
9
+
})
10
+
3
11
export const Hashtag_Stroke2_Corner0_Rounded = createSinglePathSVG({
4
12
path: 'M9.124 3.008a1 1 0 0 1 .868 1.116L9.632 7h5.985l.39-3.124a1 1 0 0 1 1.985.248L17.632 7H20a1 1 0 1 1 0 2h-2.617l-.75 6H20a1 1 0 1 1 0 2h-3.617l-.39 3.124a1 1 0 1 1-1.985-.248l.36-2.876H8.382l-.39 3.124a1 1 0 1 1-1.985-.248L6.368 17H4a1 1 0 1 1 0-2h2.617l.75-6H4a1 1 0 1 1 0-2h3.617l.39-3.124a1 1 0 0 1 1.117-.868ZM9.383 9l-.75 6h5.984l.75-6H9.383Z',
5
13
})
+7
src/components/icons/Heart2.tsx
+7
src/components/icons/Heart2.tsx
···
1
1
import {createSinglePathSVG} from './TEMPLATE'
2
2
3
+
export const Heart2_Stroke1_Corner0_Rounded = createSinglePathSVG({
4
+
viewBox: '0 0 51 46',
5
+
strokeLinejoin: 'round',
6
+
strokeWidth: 2,
7
+
path: 'M49 17c0 15.333-22 26.667-24 26.667S1 32.333 1 17C1 6.333 7.667 1 14.333 1S25 5 25 5s4-4 10.667-4S49 6.333 49 17Z',
8
+
})
9
+
3
10
export const Heart2_Stroke2_Corner0_Rounded = createSinglePathSVG({
4
11
path: 'M16.734 5.091c-1.238-.276-2.708.047-4.022 1.38a1 1 0 0 1-1.424 0C9.974 5.137 8.504 4.814 7.266 5.09c-1.263.282-2.379 1.206-2.92 2.556C3.33 10.18 4.252 14.84 12 19.348c7.747-4.508 8.67-9.168 7.654-11.7-.541-1.351-1.657-2.275-2.92-2.557Zm4.777 1.812c1.604 4-.494 9.69-9.022 14.47a1 1 0 0 1-.978 0C2.983 16.592.885 10.902 2.49 6.902c.779-1.942 2.414-3.334 4.342-3.764 1.697-.378 3.552.003 5.169 1.286 1.617-1.283 3.472-1.664 5.17-1.286 1.927.43 3.562 1.822 4.34 3.764Z',
5
12
})
+7
src/components/icons/Image.tsx
+7
src/components/icons/Image.tsx
···
1
1
import {createSinglePathSVG} from './TEMPLATE'
2
2
3
+
export const Image_Stroke1_Corner0_Rounded = createSinglePathSVG({
4
+
viewBox: '0 0 46 46',
5
+
strokeLinecap: 'round',
6
+
strokeWidth: 1.5,
7
+
path: 'm1.417 28.645 7.586-5.676a5.33 5.33 0 0 1 6.867.809c3.98 4.286 8.594 8.182 14.88 8.182 5.794 0 9.633-2.147 13.333-5.847m-38 18.637h33.334a5.333 5.333 0 0 0 5.333-5.333V6.083A5.333 5.333 0 0 0 39.417.75H6.083A5.333 5.333 0 0 0 .75 6.083v33.334a5.333 5.333 0 0 0 5.333 5.333ZM36.75 14.083a5.333 5.333 0 1 1-10.667 0 5.333 5.333 0 0 1 10.667 0Z',
8
+
})
9
+
3
10
export const Image_Stroke2_Corner0_Rounded = createSinglePathSVG({
4
11
path: 'M3 4a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4Zm2 1v7.213l1.246-.932.044-.03a3 3 0 0 1 3.863.454c1.468 1.58 2.941 2.749 4.847 2.749 1.703 0 2.855-.555 4-1.618V5H5Zm14 10.357c-1.112.697-2.386 1.097-4 1.097-2.81 0-4.796-1.755-6.313-3.388a1 1 0 0 0-1.269-.164L5 14.712V19h14v-3.643ZM15 8a1 1 0 1 0 0 2 1 1 0 0 0 0-2Zm-3 1a3 3 0 1 1 6 0 3 3 0 0 1-6 0Z',
5
12
})
+8
src/components/icons/Message.tsx
+8
src/components/icons/Message.tsx
···
1
1
import {createSinglePathSVG} from './TEMPLATE'
2
2
3
+
export const Message_Stroke1_Corner0_Rounded_Filled = createSinglePathSVG({
4
+
viewBox: '0 0 51 51',
5
+
strokeWidth: 2,
6
+
strokeLinecap: 'square',
7
+
strokeLinejoin: 'round',
8
+
path: 'M9 1h32a8 8 0 0 1 8 8v21.333a8 8 0 0 1-8 8H27.667L14.333 49V38.333H9a8 8 0 0 1-8-8V9a8 8 0 0 1 8-8Z',
9
+
})
10
+
3
11
export const Message_Stroke2_Corner0_Rounded_Filled = createSinglePathSVG({
4
12
path: 'M2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10a9.968 9.968 0 0 1-4.136-.893l-4.68.876a1 1 0 0 1-1.164-1.184l.931-4.537A9.965 9.965 0 0 1 2 12Zm4.25 0a1.25 1.25 0 1 0 2.5 0 1.25 1.25 0 0 0-2.5 0Zm4.5 0a1.25 1.25 0 1 0 2.5 0 1.25 1.25 0 0 0-2.5 0Zm5.75 1.25a1.25 1.25 0 1 1 0-2.5 1.25 1.25 0 0 1 0 2.5Z',
5
13
})
+8
src/components/icons/PeopleRemove2.tsx
+8
src/components/icons/PeopleRemove2.tsx
···
1
1
import {createSinglePathSVG} from './TEMPLATE'
2
2
3
+
export const PeopleRemove2_Stroke1_Corner0_Rounded = createSinglePathSVG({
4
+
viewBox: '-15 0 65 64',
5
+
strokeWidth: 2,
6
+
strokeLinecap: 'round',
7
+
strokeLinejoin: 'round',
8
+
path: 'M20.603 46.333H3.532c-1.572 0-2.816-1.358-2.472-2.891 2.033-9.046 9.421-15.775 19.543-15.775q1.367 0 2.666.16m18.667 7.84L36.603 41m0 0-5.334 5.333M36.603 41l-5.334-5.333M36.603 41l5.333 5.333m-12-36A9.333 9.333 0 1 1 20.603 1a9.333 9.333 0 0 1 9.333 9.333Z',
9
+
})
10
+
3
11
export const PeopleRemove2_Stroke2_Corner0_Rounded = createSinglePathSVG({
4
12
path: 'M10 4a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5ZM5.5 6.5a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM16 11a1 1 0 0 1 1-1h5a1 1 0 1 1 0 2h-5a1 1 0 0 1-1-1ZM3.678 19h12.644c-.71-2.909-3.092-5-6.322-5s-5.613 2.091-6.322 5Zm-2.174.906C1.917 15.521 5.242 12 10 12c4.758 0 8.083 3.521 8.496 7.906A1 1 0 0 1 17.5 21h-15a1 1 0 0 1-.996-1.094Z',
5
13
})
+44
-1
src/components/icons/TEMPLATE.tsx
+44
-1
src/components/icons/TEMPLATE.tsx
···
28
28
},
29
29
)
30
30
31
-
export function createSinglePathSVG({path}: {path: string}) {
31
+
export function createSinglePathSVG({
32
+
path,
33
+
viewBox,
34
+
strokeWidth = 0,
35
+
strokeLinecap = 'butt',
36
+
strokeLinejoin = 'miter',
37
+
}: {
38
+
path: string
39
+
viewBox?: string
40
+
strokeWidth?: number
41
+
strokeLinecap?: 'butt' | 'round' | 'square'
42
+
strokeLinejoin?: 'miter' | 'round' | 'bevel'
43
+
}) {
44
+
return React.forwardRef<Svg, Props>(function LogoImpl(props, ref) {
45
+
const {fill, size, style, gradient, ...rest} = useCommonSVGProps(props)
46
+
47
+
const hasStroke = strokeWidth > 0
48
+
49
+
return (
50
+
<Svg
51
+
fill="none"
52
+
{...rest}
53
+
ref={ref}
54
+
viewBox={viewBox || '0 0 24 24'}
55
+
width={size}
56
+
height={size}
57
+
style={[style]}>
58
+
{gradient}
59
+
<Path
60
+
fill={hasStroke ? 'none' : fill}
61
+
stroke={hasStroke ? fill : 'none'}
62
+
strokeWidth={strokeWidth}
63
+
strokeLinecap={strokeLinecap}
64
+
strokeLinejoin={strokeLinejoin}
65
+
fillRule="evenodd"
66
+
clipRule="evenodd"
67
+
d={path}
68
+
/>
69
+
</Svg>
70
+
)
71
+
})
72
+
}
73
+
74
+
export function createSinglePathSVG2({path}: {path: string}) {
32
75
return React.forwardRef<Svg, Props>(function LogoImpl(props, ref) {
33
76
const {fill, size, style, gradient, ...rest} = useCommonSVGProps(props)
34
77
+8
src/components/icons/VideoClip.tsx
+8
src/components/icons/VideoClip.tsx
···
1
1
import {createSinglePathSVG} from './TEMPLATE'
2
2
3
+
export const VideoClip_Stroke1_Corner0_Rounded = createSinglePathSVG({
4
+
viewBox: '0 0 46 46',
5
+
strokeLinecap: 'square',
6
+
strokeLinejoin: 'round',
7
+
strokeWidth: 2,
8
+
path: 'M1 23h10.667M1 23V12m0 11v11m10.667-11h22.666m-22.666 0v11m0-11V12m22.666 11H45m-10.667 0v12.222m0-12.222V12M45 23V12m0 11v12.222M34.333 45h5.334A5.333 5.333 0 0 0 45 39.667v-4.445M34.333 45v-9.778m0 9.778H11.667M34.333 1h5.334A5.333 5.333 0 0 1 45 6.333V12M34.333 1v11m0-11H11.667m22.666 11H45M34.333 35.222H45M11.667 45H6.333A5.333 5.333 0 0 1 1 39.667V34m10.667 11V34m0-33H6.333A5.333 5.333 0 0 0 1 6.333V12M11.667 1v11M1 12h10.667M1 34h10.667',
9
+
})
10
+
3
11
export const VideoClip_Stroke2_Corner0_Rounded = createSinglePathSVG({
4
12
path: 'M3 4a1 1 0 011-1h16a1 1 0 011 1v16a1 1 0 01-1 1H4a1 1 0 01-1-1V4Zm2 1v2h2V5H5Zm4 0v6h6V5H9Zm8 0v2h2V5h-2Zm2 4h-2v2h2V9Zm0 4h-2v2h2V13Zm0 4h-2V19h2ZM15 19v-6H9v6h6Zm-8 0v-2H5v2h2Zm-2-4h2v-2H5v2Zm0-4h2V9H5v2Z',
5
13
})
+1
src/components/icons/common.tsx
+1
src/components/icons/common.tsx
+12
src/lib/strings/errors.ts
+12
src/lib/strings/errors.ts
···
52
52
const str = String(e)
53
53
return str.includes('Bad token scope') || str.includes('Bad token method')
54
54
}
55
+
56
+
/**
57
+
* Intended to capture "User cancelled" or "Crop cancelled" errors
58
+
* that we often get from expo modules such expo-image-crop-tool
59
+
*
60
+
* The exact name has changed in the past so let's just see if the string
61
+
* contains "cancel"
62
+
*/
63
+
export function isCancelledError(e: unknown) {
64
+
const str = String(e).toLowerCase()
65
+
return str.includes('cancel')
66
+
}
+11
-1
src/lib/strings/time.ts
+11
-1
src/lib/strings/time.ts
···
1
1
import {type I18n} from '@lingui/core'
2
+
import {msg} from '@lingui/macro'
2
3
3
4
export function niceDate(
4
5
i18n: I18n,
5
6
date: number | string | Date,
6
-
dateStyle: 'short' | 'medium' | 'long' | 'full' = 'long',
7
+
dateStyle: 'short' | 'medium' | 'long' | 'full' | 'dot separated' = 'long',
7
8
) {
8
9
const d = new Date(date)
10
+
11
+
if (dateStyle === 'dot separated') {
12
+
return i18n._(
13
+
msg({
14
+
context: 'date and time formatted like this: [time] · [date]',
15
+
message: `${i18n.date(d, {timeStyle: 'short'})} · ${i18n.date(d, {day: 'numeric', month: 'numeric', year: '2-digit'})}`,
16
+
}),
17
+
)
18
+
}
9
19
10
20
return i18n.date(d, {
11
21
dateStyle,
+199
-126
src/locale/locales/en/messages.po
+199
-126
src/locale/locales/en/messages.po
···
128
128
msgid "{0, plural, other {+# more}}"
129
129
msgstr ""
130
130
131
+
#: src/lib/strings/time.ts:13
132
+
msgctxt "date and time formatted like this: [time] · [date]"
133
+
msgid "{0} · {1}"
134
+
msgstr ""
135
+
131
136
#: src/components/moderation/ContentHider.tsx:89
132
137
msgid "{0} (Account)"
133
138
msgstr ""
···
695
700
msgid "Add a temporary live status to your profile. When someone clicks on your avatar, they’ll see information about your live event."
696
701
msgstr ""
697
702
698
-
#: src/screens/ProfileList/AboutSection.tsx:62
699
-
#: src/screens/ProfileList/AboutSection.tsx:80
703
+
#: src/screens/ProfileList/AboutSection.tsx:63
704
+
#: src/screens/ProfileList/AboutSection.tsx:81
700
705
msgid "Add a user to this list"
701
706
msgstr ""
702
707
···
738
743
msgid "Add app password"
739
744
msgstr ""
740
745
741
-
#: src/screens/Settings/AppPasswords.tsx:73
742
-
#: src/screens/Settings/AppPasswords.tsx:81
746
+
#: src/screens/Settings/AppPasswords.tsx:74
747
+
#: src/screens/Settings/AppPasswords.tsx:82
743
748
#: src/screens/Settings/components/AddAppPasswordDialog.tsx:111
744
749
msgid "Add App Password"
745
750
msgstr ""
···
766
771
msgid "Add muted words and tags"
767
772
msgstr ""
768
773
769
-
#: src/screens/ProfileList/AboutSection.tsx:70
770
-
#: src/screens/ProfileList/AboutSection.tsx:88
774
+
#: src/screens/ProfileList/AboutSection.tsx:71
775
+
#: src/screens/ProfileList/AboutSection.tsx:89
771
776
msgid "Add people"
772
777
msgstr ""
773
778
···
958
963
msgid "Allow your followers to reply"
959
964
msgstr ""
960
965
961
-
#: src/screens/Settings/AppPasswords.tsx:199
966
+
#: src/screens/Settings/AppPasswords.tsx:200
962
967
msgid "Allows access to direct messages"
963
968
msgstr ""
964
969
···
996
1001
msgid "Alt Text"
997
1002
msgstr ""
998
1003
999
-
#: src/view/com/composer/photos/Gallery.tsx:260
1004
+
#: src/view/com/composer/photos/Gallery.tsx:261
1000
1005
msgid "Alt text describes images for blind and low-vision users, and helps give context to everyone."
1001
1006
msgstr ""
1002
1007
···
1031
1036
msgid "An error occurred while fetching the feed."
1032
1037
msgstr ""
1033
1038
1034
-
#: src/components/StarterPack/ProfileStarterPacks.tsx:339
1039
+
#: src/components/StarterPack/ProfileStarterPacks.tsx:371
1035
1040
msgid "An error occurred while generating your starter pack. Want to try again?"
1036
1041
msgstr ""
1037
1042
···
1160
1165
msgid "App Password"
1161
1166
msgstr ""
1162
1167
1163
-
#: src/screens/Settings/AppPasswords.tsx:145
1168
+
#: src/screens/Settings/AppPasswords.tsx:146
1164
1169
msgctxt "toast"
1165
1170
msgid "App password deleted"
1166
1171
msgstr ""
···
1183
1188
msgstr ""
1184
1189
1185
1190
#: src/Navigation.tsx:351
1186
-
#: src/screens/Settings/AppPasswords.tsx:49
1191
+
#: src/screens/Settings/AppPasswords.tsx:50
1187
1192
msgid "App Passwords"
1188
1193
msgstr ""
1189
1194
···
1244
1249
msgid "Archived post"
1245
1250
msgstr ""
1246
1251
1247
-
#: src/screens/Settings/AppPasswords.tsx:208
1252
+
#: src/screens/Settings/AppPasswords.tsx:209
1248
1253
msgid "Are you sure you want to delete the app password \"{0}\"?"
1249
1254
msgstr ""
1250
1255
···
1368
1373
msgstr ""
1369
1374
1370
1375
#: src/components/dialogs/StarterPackDialog.tsx:71
1371
-
#: src/components/StarterPack/ProfileStarterPacks.tsx:231
1372
-
#: src/components/StarterPack/ProfileStarterPacks.tsx:241
1376
+
#: src/components/StarterPack/ProfileStarterPacks.tsx:263
1377
+
#: src/components/StarterPack/ProfileStarterPacks.tsx:273
1378
+
#: src/view/screens/Profile.tsx:351
1373
1379
msgid "Before creating a starter pack, you must first verify your email."
1374
1380
msgstr ""
1375
1381
···
1530
1536
msgid "Bluesky Social Terms of Service"
1531
1537
msgstr ""
1532
1538
1533
-
#: src/components/StarterPack/ProfileStarterPacks.tsx:306
1539
+
#: src/components/StarterPack/ProfileStarterPacks.tsx:338
1534
1540
msgid "Bluesky will choose a set of recommended accounts from people in your network."
1535
1541
msgstr ""
1536
1542
···
1565
1571
1566
1572
#: src/components/moderation/ReportDialog/utils/useReportOptions.ts:215
1567
1573
msgid "Breaking site rules"
1574
+
msgstr ""
1575
+
1576
+
#: src/view/com/feeds/ProfileFeedgens.tsx:158
1577
+
#: src/view/com/feeds/ProfileFeedgens.tsx:159
1578
+
msgid "Browse custom feeds"
1568
1579
msgstr ""
1569
1580
1570
1581
#: src/components/FeedInterstitials.tsx:436
···
1613
1624
msgid "Business"
1614
1625
msgstr ""
1615
1626
1627
+
#: src/screens/Bookmarks/index.tsx:277
1628
+
msgid "Button to go back to the home timeline"
1629
+
msgstr ""
1630
+
1616
1631
#: src/components/LabelingServiceCard/index.tsx:62
1617
1632
#: src/components/moderation/ReportDialog/index.tsx:834
1618
1633
#: src/screens/Search/components/StarterPackCard.tsx:106
···
1880
1895
msgid "Choose Feeds"
1881
1896
msgstr ""
1882
1897
1883
-
#: src/components/StarterPack/ProfileStarterPacks.tsx:314
1898
+
#: src/components/StarterPack/ProfileStarterPacks.tsx:346
1884
1899
msgid "Choose for me"
1885
1900
msgstr ""
1886
1901
···
2447
2462
msgid "Could not leave chat"
2448
2463
msgstr ""
2449
2464
2450
-
#: src/screens/Profile/ProfileFeed/index.tsx:83
2465
+
#: src/screens/Profile/ProfileFeed/index.tsx:84
2451
2466
msgid "Could not load feed"
2452
2467
msgstr ""
2453
2468
···
2477
2492
#. Text on button to create a new starter pack
2478
2493
#: src/components/dialogs/StarterPackDialog.tsx:112
2479
2494
#: src/components/dialogs/StarterPackDialog.tsx:201
2480
-
#: src/components/StarterPack/ProfileStarterPacks.tsx:296
2495
+
#: src/components/StarterPack/ProfileStarterPacks.tsx:328
2481
2496
msgid "Create"
2497
+
msgstr ""
2498
+
2499
+
#: src/view/com/lists/ProfileLists.tsx:159
2500
+
#: src/view/com/lists/ProfileLists.tsx:160
2501
+
msgid "Create a list"
2482
2502
msgstr ""
2483
2503
2484
2504
#: src/components/StarterPack/QrCodeDialog.tsx:163
2485
2505
msgid "Create a QR code for a starter pack"
2486
2506
msgstr ""
2487
2507
2488
-
#: src/components/StarterPack/ProfileStarterPacks.tsx:174
2489
-
#: src/components/StarterPack/ProfileStarterPacks.tsx:283
2508
+
#: src/components/StarterPack/ProfileStarterPacks.tsx:206
2509
+
#: src/components/StarterPack/ProfileStarterPacks.tsx:315
2490
2510
#: src/Navigation.tsx:589
2491
2511
msgid "Create a starter pack"
2492
2512
msgstr ""
2493
2513
2494
-
#: src/components/StarterPack/ProfileStarterPacks.tsx:270
2514
+
#: src/view/screens/Profile.tsx:542
2515
+
#: src/view/screens/Profile.tsx:543
2516
+
msgid "Create a Starter Pack"
2517
+
msgstr ""
2518
+
2519
+
#: src/components/StarterPack/ProfileStarterPacks.tsx:302
2495
2520
msgid "Create a starter pack for me"
2496
2521
msgstr ""
2497
2522
···
2528
2553
msgid "Create an avatar instead"
2529
2554
msgstr ""
2530
2555
2531
-
#: src/components/StarterPack/ProfileStarterPacks.tsx:181
2556
+
#: src/components/StarterPack/ProfileStarterPacks.tsx:213
2532
2557
msgid "Create another"
2533
2558
msgstr ""
2534
2559
···
2556
2581
msgid "Create user list"
2557
2582
msgstr ""
2558
2583
2559
-
#: src/screens/Settings/AppPasswords.tsx:172
2584
+
#: src/screens/Settings/AppPasswords.tsx:173
2560
2585
msgid "Created {0}"
2561
2586
msgstr ""
2562
2587
···
2602
2627
msgid "Dark"
2603
2628
msgstr ""
2604
2629
2605
-
#: src/view/screens/Debug.tsx:68
2630
+
#: src/view/screens/Debug.tsx:69
2606
2631
msgid "Dark mode"
2607
2632
msgstr ""
2608
2633
···
2624
2649
msgid "Debug Moderation"
2625
2650
msgstr ""
2626
2651
2627
-
#: src/view/screens/Debug.tsx:88
2652
+
#: src/view/screens/Debug.tsx:89
2628
2653
msgid "Debug panel"
2629
2654
msgstr ""
2630
2655
···
2644
2669
#: src/components/PostControls/PostMenu/PostMenuItems.tsx:736
2645
2670
#: src/screens/Messages/components/ChatStatusInfo.tsx:55
2646
2671
#: src/screens/ProfileList/components/MoreOptionsMenu.tsx:280
2647
-
#: src/screens/Settings/AppPasswords.tsx:211
2672
+
#: src/screens/Settings/AppPasswords.tsx:212
2648
2673
#: src/screens/StarterPack/StarterPackScreen.tsx:601
2649
2674
#: src/screens/StarterPack/StarterPackScreen.tsx:690
2650
2675
#: src/screens/StarterPack/StarterPackScreen.tsx:762
···
2660
2685
msgid "Delete Account <0>\"</0><1>{0}</1><2>\"</2>"
2661
2686
msgstr ""
2662
2687
2663
-
#: src/screens/Settings/AppPasswords.tsx:185
2688
+
#: src/screens/Settings/AppPasswords.tsx:186
2664
2689
msgid "Delete app password"
2665
2690
msgstr ""
2666
2691
2667
-
#: src/screens/Settings/AppPasswords.tsx:206
2692
+
#: src/screens/Settings/AppPasswords.tsx:207
2668
2693
msgid "Delete app password?"
2669
2694
msgstr ""
2670
2695
···
3260
3285
msgid "Enabled"
3261
3286
msgstr ""
3262
3287
3263
-
#: src/screens/Profile/Sections/Feed.tsx:109
3288
+
#: src/screens/Profile/Sections/Feed.tsx:131
3264
3289
msgid "End of feed"
3265
3290
msgstr ""
3266
3291
···
3746
3771
#: src/screens/Search/SearchResults.tsx:77
3747
3772
#: src/screens/StarterPack/StarterPackScreen.tsx:190
3748
3773
#: src/view/screens/Feeds.tsx:511
3749
-
#: src/view/screens/Profile.tsx:230
3774
+
#: src/view/screens/Profile.tsx:239
3750
3775
#: src/view/shell/desktop/LeftNav.tsx:728
3751
3776
#: src/view/shell/Drawer.tsx:530
3752
3777
msgid "Feeds"
···
4056
4081
msgid "From <0/>"
4057
4082
msgstr ""
4058
4083
4059
-
#: src/components/StarterPack/ProfileStarterPacks.tsx:303
4084
+
#: src/components/StarterPack/ProfileStarterPacks.tsx:335
4060
4085
msgid "Generate a starter pack"
4061
4086
msgstr ""
4062
4087
···
4147
4172
#: src/components/moderation/ScreenHider.tsx:160
4148
4173
#: src/components/moderation/ScreenHider.tsx:169
4149
4174
#: src/screens/Messages/Inbox.tsx:251
4150
-
#: src/screens/Profile/ProfileFeed/index.tsx:92
4175
+
#: src/screens/Profile/ProfileFeed/index.tsx:93
4151
4176
#: src/screens/ProfileList/components/ErrorScreen.tsx:34
4152
4177
#: src/screens/ProfileList/components/ErrorScreen.tsx:40
4153
4178
#: src/screens/VideoFeed/components/Header.tsx:163
4154
4179
#: src/screens/VideoFeed/index.tsx:1162
4155
4180
#: src/screens/VideoFeed/index.tsx:1166
4156
4181
#: src/view/com/auth/LoggedOut.tsx:72
4182
+
#: src/view/com/profile/ProfileFollowers.tsx:144
4183
+
#: src/view/com/profile/ProfileFollowers.tsx:145
4157
4184
#: src/view/screens/NotFound.tsx:57
4158
4185
msgid "Go back"
4159
4186
msgstr ""
···
4162
4189
#: src/screens/List/ListHiddenScreen.tsx:224
4163
4190
#: src/screens/Profile/ErrorState.tsx:62
4164
4191
#: src/screens/Profile/ErrorState.tsx:66
4165
-
#: src/screens/Profile/ProfileFeed/index.tsx:97
4192
+
#: src/screens/Profile/ProfileFeed/index.tsx:98
4166
4193
#: src/screens/StarterPack/StarterPackScreen.tsx:775
4167
4194
#: src/view/screens/NotFound.tsx:56
4168
4195
msgid "Go Back"
···
4180
4207
msgid "Go home"
4181
4208
msgstr ""
4182
4209
4210
+
#: src/screens/Bookmarks/index.tsx:278
4183
4211
#: src/view/screens/NotFound.tsx:57
4184
4212
msgid "Go home"
4185
4213
msgstr ""
···
4433
4461
msgid "Hides the content"
4434
4462
msgstr ""
4435
4463
4436
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:121
4464
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:124
4437
4465
msgid "Hmm, some kind of issue occurred when contacting the feed server. Please let the feed owner know about this issue."
4438
4466
msgstr ""
4439
4467
4440
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:109
4468
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:112
4441
4469
msgid "Hmm, the feed server appears to be misconfigured. Please let the feed owner know about this issue."
4442
4470
msgstr ""
4443
4471
4444
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:115
4472
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:118
4445
4473
msgid "Hmm, the feed server appears to be offline. Please let the feed owner know about this issue."
4446
4474
msgstr ""
4447
4475
4448
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:112
4476
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:115
4449
4477
msgid "Hmm, the feed server gave a bad response. Please let the feed owner know about this issue."
4450
4478
msgstr ""
4451
4479
4452
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:106
4480
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:109
4453
4481
msgid "Hmm, we're having trouble finding this feed. It may have been deleted."
4454
4482
msgstr ""
4455
4483
···
4763
4791
msgstr ""
4764
4792
4765
4793
#: src/view/com/composer/labels/LabelsBtn.tsx:69
4766
-
#: src/view/screens/Profile.tsx:223
4794
+
#: src/view/screens/Profile.tsx:232
4767
4795
msgid "Labels"
4768
4796
msgstr ""
4769
4797
···
4905
4933
msgid "left to go."
4906
4934
msgstr ""
4907
4935
4908
-
#: src/components/StarterPack/ProfileStarterPacks.tsx:319
4936
+
#: src/components/StarterPack/ProfileStarterPacks.tsx:351
4909
4937
msgid "Let me choose"
4910
4938
msgstr ""
4911
4939
···
4986
5014
#: src/lib/hooks/useNotificationHandler.ts:126
4987
5015
#: src/screens/Settings/NotificationSettings/index.tsx:126
4988
5016
#: src/screens/Settings/NotificationSettings/LikeNotificationSettings.tsx:41
4989
-
#: src/view/screens/Profile.tsx:229
5017
+
#: src/view/screens/Profile.tsx:238
4990
5018
msgid "Likes"
4991
5019
msgstr ""
4992
5020
···
5091
5119
5092
5120
#: src/Navigation.tsx:172
5093
5121
#: src/view/screens/Lists.tsx:67
5094
-
#: src/view/screens/Profile.tsx:224
5095
-
#: src/view/screens/Profile.tsx:232
5122
+
#: src/view/screens/Profile.tsx:233
5123
+
#: src/view/screens/Profile.tsx:241
5096
5124
#: src/view/shell/desktop/LeftNav.tsx:746
5097
5125
#: src/view/shell/Drawer.tsx:545
5098
5126
msgid "Lists"
5127
+
msgstr ""
5128
+
5129
+
#: src/view/com/lists/MyLists.tsx:72
5130
+
#: src/view/com/lists/ProfileLists.tsx:155
5131
+
msgid "Lists allow you to see content from your favorite people."
5099
5132
msgstr ""
5100
5133
5101
5134
#: src/components/dms/BlockedByListDialog.tsx:39
···
5131
5164
msgid "Load new notifications"
5132
5165
msgstr ""
5133
5166
5134
-
#: src/screens/Profile/ProfileFeed/index.tsx:224
5135
-
#: src/screens/Profile/Sections/Feed.tsx:94
5136
-
#: src/screens/ProfileList/FeedSection.tsx:105
5167
+
#: src/screens/Profile/ProfileFeed/index.tsx:231
5168
+
#: src/screens/Profile/Sections/Feed.tsx:116
5169
+
#: src/screens/ProfileList/FeedSection.tsx:112
5137
5170
#: src/view/com/feeds/FeedPage.tsx:169
5138
5171
msgid "Load new posts"
5139
5172
msgstr ""
···
5195
5228
msgid "Make adjustments to email settings for your account"
5196
5229
msgstr ""
5197
5230
5198
-
#: src/components/StarterPack/ProfileStarterPacks.tsx:278
5231
+
#: src/components/StarterPack/ProfileStarterPacks.tsx:310
5199
5232
msgid "Make one for me"
5200
5233
msgstr ""
5201
5234
···
5237
5270
msgid "Maybe later"
5238
5271
msgstr ""
5239
5272
5240
-
#: src/view/screens/Profile.tsx:227
5273
+
#: src/view/screens/Profile.tsx:236
5241
5274
msgid "Media"
5242
5275
msgstr ""
5243
5276
···
5281
5314
msgid "Message from @{0}: {1}"
5282
5315
msgstr ""
5283
5316
5284
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:205
5317
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:208
5285
5318
msgid "Message from server: {0}"
5286
5319
msgstr ""
5287
5320
···
5625
5658
msgid "New password"
5626
5659
msgstr ""
5627
5660
5628
-
#: src/screens/Profile/ProfileFeed/index.tsx:241
5661
+
#: src/screens/Profile/ProfileFeed/index.tsx:248
5629
5662
#: src/screens/ProfileList/index.tsx:246
5630
5663
#: src/screens/ProfileList/index.tsx:284
5631
5664
#: src/view/screens/Feeds.tsx:552
5632
5665
#: src/view/screens/Notifications.tsx:167
5633
-
#: src/view/screens/Profile.tsx:510
5666
+
#: src/view/screens/Profile.tsx:571
5634
5667
msgid "New post"
5635
5668
msgstr ""
5636
5669
···
5699
5732
msgid "No ads, no invasive tracking, no engagement traps. Bluesky respects your time and attention."
5700
5733
msgstr ""
5701
5734
5702
-
#: src/screens/Settings/AppPasswords.tsx:106
5735
+
#: src/screens/Settings/AppPasswords.tsx:107
5703
5736
msgid "No app passwords yet"
5704
5737
msgstr ""
5705
5738
···
5720
5753
msgid "No feeds found. Try searching for something else."
5721
5754
msgstr ""
5722
5755
5756
+
#: src/view/com/profile/ProfileFollowers.tsx:135
5757
+
msgid "No followers yet"
5758
+
msgstr ""
5759
+
5723
5760
#: src/components/live/LinkPreview.tsx:63
5724
5761
msgid "No image"
5725
5762
msgstr ""
5726
5763
5727
5764
#: src/components/LikedByList.tsx:84
5728
5765
#: src/view/com/post-thread/PostLikedBy.tsx:84
5766
+
#: src/view/screens/Profile.tsx:511
5729
5767
msgid "No likes yet"
5730
5768
msgstr ""
5731
5769
···
5735
5773
msgid "No longer following {0}"
5736
5774
msgstr ""
5737
5775
5776
+
#: src/view/screens/Profile.tsx:467
5777
+
msgid "No media yet"
5778
+
msgstr ""
5779
+
5738
5780
#: src/screens/Messages/components/ChatListItem.tsx:142
5739
5781
msgid "No messages yet"
5740
5782
msgstr ""
···
5743
5785
msgid "No more doomscrolling junk-filled algorithms. Find feeds that work for you, not against you."
5744
5786
msgstr ""
5745
5787
5746
-
#: src/view/com/notifications/NotificationFeed.tsx:122
5788
+
#: src/view/com/notifications/NotificationFeed.tsx:123
5747
5789
msgid "No notifications yet!"
5748
5790
msgstr ""
5749
5791
···
5759
5801
msgid "No one but the author can quote this post."
5760
5802
msgstr ""
5761
5803
5762
-
#: src/screens/Notifications/ActivityList.tsx:38
5804
+
#: src/screens/Notifications/ActivityList.tsx:42
5763
5805
msgid "No posts here"
5764
5806
msgstr ""
5765
5807
5766
-
#: src/screens/Profile/Sections/Feed.tsx:62
5767
-
msgid "No posts yet."
5808
+
#: src/screens/Profile/Sections/Feed.tsx:80
5809
+
#: src/view/screens/Profile.tsx:431
5810
+
msgid "No posts yet"
5768
5811
msgstr ""
5769
5812
5770
5813
#: src/view/com/post-thread/PostQuotes.tsx:105
5771
5814
msgid "No quotes yet"
5772
5815
msgstr ""
5773
5816
5817
+
#: src/view/screens/Profile.tsx:452
5818
+
msgid "No replies yet"
5819
+
msgstr ""
5820
+
5774
5821
#: src/view/com/post-thread/PostRepostedBy.tsx:90
5775
5822
msgid "No reposts yet"
5776
5823
msgstr ""
···
5789
5836
msgid "No results for \"{0}\"."
5790
5837
msgstr ""
5791
5838
5792
-
#: src/components/Lists.tsx:189
5839
+
#: src/components/Lists.tsx:201
5840
+
#: src/components/Lists.tsx:216
5793
5841
msgid "No results found"
5794
5842
msgstr ""
5795
5843
···
5814
5862
msgid "No thanks"
5815
5863
msgstr ""
5816
5864
5865
+
#: src/view/screens/Profile.tsx:489
5866
+
msgid "No video posts yet"
5867
+
msgstr ""
5868
+
5817
5869
#: src/components/dialogs/PostInteractionSettingsDialog.tsx:465
5818
5870
msgid "Nobody"
5819
5871
msgstr ""
···
5852
5904
msgstr ""
5853
5905
5854
5906
#: src/Navigation.tsx:167
5855
-
#: src/view/screens/Profile.tsx:125
5907
+
#: src/view/screens/Profile.tsx:132
5856
5908
msgid "Not Found"
5857
5909
msgstr ""
5858
5910
···
5877
5929
msgstr ""
5878
5930
5879
5931
#: src/screens/Bookmarks/components/EmptyState.tsx:35
5932
+
#: src/screens/Bookmarks/index.tsx:274
5880
5933
msgid "Nothing saved yet"
5881
5934
msgstr ""
5882
5935
···
5896
5949
5897
5950
#: src/Navigation.tsx:564
5898
5951
#: src/Navigation.tsx:764
5899
-
#: src/screens/Notifications/ActivityList.tsx:29
5952
+
#: src/screens/Notifications/ActivityList.tsx:30
5900
5953
#: src/screens/Settings/NotificationSettings/ActivityNotificationSettings.tsx:90
5901
5954
#: src/screens/Settings/NotificationSettings/index.tsx:92
5902
5955
#: src/screens/Settings/NotificationSettings/LikeNotificationSettings.tsx:30
···
6015
6068
msgid "Only WebVTT (.vtt) files are supported"
6016
6069
msgstr ""
6017
6070
6018
-
#: src/components/Lists.tsx:94
6071
+
#: src/components/Lists.tsx:98
6019
6072
msgid "Oops, something went wrong!"
6020
6073
msgstr ""
6021
6074
6022
-
#: src/components/Lists.tsx:173
6023
-
#: src/components/StarterPack/ProfileStarterPacks.tsx:328
6024
-
#: src/components/StarterPack/ProfileStarterPacks.tsx:337
6025
-
#: src/screens/Settings/AppPasswords.tsx:57
6075
+
#: src/components/Lists.tsx:183
6076
+
#: src/components/StarterPack/ProfileStarterPacks.tsx:360
6077
+
#: src/components/StarterPack/ProfileStarterPacks.tsx:369
6078
+
#: src/screens/Settings/AppPasswords.tsx:58
6026
6079
#: src/screens/Settings/components/ChangeHandleDialog.tsx:106
6027
-
#: src/view/screens/Profile.tsx:125
6080
+
#: src/view/screens/Profile.tsx:132
6028
6081
msgid "Oops!"
6029
6082
msgstr ""
6030
6083
···
6268
6321
msgid "Our moderators have reviewed reports and decided to disable your access to chats on Bluesky."
6269
6322
msgstr ""
6270
6323
6271
-
#: src/components/Lists.tsx:190
6324
+
#: src/components/Lists.tsx:202
6325
+
#: src/components/Lists.tsx:217
6272
6326
#: src/view/screens/NotFound.tsx:47
6273
6327
msgid "Page not found"
6274
6328
msgstr ""
···
6596
6650
msgid "Post"
6597
6651
msgstr ""
6598
6652
6653
+
#: src/view/screens/Profile.tsx:469
6654
+
#: src/view/screens/Profile.tsx:470
6655
+
msgid "Post a photo"
6656
+
msgstr ""
6657
+
6658
+
#: src/view/screens/Profile.tsx:491
6659
+
#: src/view/screens/Profile.tsx:492
6660
+
msgid "Post a video"
6661
+
msgstr ""
6662
+
6599
6663
#: src/view/com/composer/Composer.tsx:1117
6600
6664
msgctxt "action"
6601
6665
msgid "Post All"
···
6676
6740
#: src/screens/ProfileList/index.tsx:166
6677
6741
#: src/screens/Settings/NotificationSettings/ActivityNotificationSettings.tsx:216
6678
6742
#: src/screens/StarterPack/StarterPackScreen.tsx:191
6679
-
#: src/view/screens/Profile.tsx:225
6743
+
#: src/view/screens/Profile.tsx:234
6680
6744
msgid "Posts"
6681
6745
msgstr ""
6682
6746
···
6684
6748
msgid "Posts can be muted based on their text, their tags, or both. We recommend avoiding common words that appear in many posts, since it can result in no posts being shown."
6685
6749
msgstr ""
6686
6750
6687
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:72
6751
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:75
6688
6752
msgid "Posts hidden"
6689
6753
msgstr ""
6690
6754
···
6705
6769
msgstr ""
6706
6770
6707
6771
#: src/components/Error.tsx:60
6708
-
#: src/components/Lists.tsx:99
6772
+
#: src/components/Lists.tsx:103
6709
6773
#: src/screens/Messages/components/MessageListError.tsx:24
6710
6774
#: src/screens/Signup/BackNextButtons.tsx:47
6711
6775
msgid "Press to retry"
···
6770
6834
msgstr ""
6771
6835
6772
6836
#: src/view/screens/DebugMod.tsx:936
6773
-
#: src/view/screens/Profile.tsx:364
6837
+
#: src/view/screens/Profile.tsx:384
6774
6838
msgid "profile"
6775
6839
msgstr ""
6776
6840
···
6802
6866
msgid "Public, sharable lists of users to mute or block in bulk."
6803
6867
msgstr ""
6804
6868
6805
-
#: src/view/com/lists/MyLists.tsx:72
6806
-
msgid "Public, sharable lists which can be used to drive feeds."
6807
-
msgstr ""
6808
-
6809
6869
#. Accessibility label for button to publish a single post
6810
6870
#: src/view/com/composer/Composer.tsx:1099
6811
6871
msgid "Publish post"
···
7011
7071
#: src/components/FeedCard.tsx:343
7012
7072
#: src/components/StarterPack/Wizard/WizardListCard.tsx:105
7013
7073
#: src/components/StarterPack/Wizard/WizardListCard.tsx:112
7014
-
#: src/screens/Bookmarks/index.tsx:255
7074
+
#: src/screens/Bookmarks/index.tsx:259
7015
7075
#: src/screens/Settings/Settings.tsx:664
7016
7076
#: src/view/com/modals/UserAddRemoveLists.tsx:235
7017
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:217
7077
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:220
7018
7078
msgid "Remove"
7019
7079
msgstr ""
7020
7080
···
7051
7111
7052
7112
#: src/view/com/posts/FeedShutdownMsg.tsx:116
7053
7113
#: src/view/com/posts/FeedShutdownMsg.tsx:120
7054
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:173
7114
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:176
7055
7115
msgid "Remove feed"
7056
7116
msgstr ""
7057
7117
7058
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:214
7118
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:217
7059
7119
msgid "Remove feed?"
7060
7120
msgstr ""
7061
7121
···
7077
7137
msgstr ""
7078
7138
7079
7139
#: src/components/PostControls/BookmarkButton.tsx:128
7080
-
#: src/screens/Bookmarks/index.tsx:249
7140
+
#: src/screens/Bookmarks/index.tsx:253
7081
7141
msgid "Remove from saved posts"
7082
7142
msgstr ""
7083
7143
···
7111
7171
msgid "Remove subtitle file"
7112
7172
msgstr ""
7113
7173
7114
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:215
7174
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:218
7115
7175
msgid "Remove this feed from your saved feeds"
7116
7176
msgstr ""
7117
7177
···
7148
7208
msgstr ""
7149
7209
7150
7210
#: src/components/PostControls/BookmarkButton.tsx:94
7151
-
#: src/screens/Bookmarks/index.tsx:207
7211
+
#: src/screens/Bookmarks/index.tsx:211
7152
7212
msgid "Removed from saved posts"
7153
7213
msgstr ""
7154
7214
···
7197
7257
#: src/screens/Settings/NotificationSettings/ActivityNotificationSettings.tsx:218
7198
7258
#: src/screens/Settings/NotificationSettings/index.tsx:148
7199
7259
#: src/screens/Settings/NotificationSettings/ReplyNotificationSettings.tsx:41
7200
-
#: src/view/screens/Profile.tsx:226
7260
+
#: src/view/screens/Profile.tsx:235
7201
7261
msgid "Replies"
7202
7262
msgstr ""
7203
7263
···
7460
7520
7461
7521
#: src/components/dms/MessageItem.tsx:322
7462
7522
#: src/components/Error.tsx:65
7463
-
#: src/components/Lists.tsx:110
7523
+
#: src/components/Lists.tsx:114
7464
7524
#: src/components/moderation/ReportDialog/index.tsx:274
7465
7525
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx:55
7466
7526
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx:58
7467
-
#: src/components/StarterPack/ProfileStarterPacks.tsx:342
7527
+
#: src/components/StarterPack/ProfileStarterPacks.tsx:374
7468
7528
#: src/screens/Login/LoginForm.tsx:326
7469
7529
#: src/screens/Login/LoginForm.tsx:333
7470
7530
#: src/screens/Messages/ChatList.tsx:292
···
7498
7558
msgid "Returns to home page"
7499
7559
msgstr ""
7500
7560
7501
-
#: src/screens/Profile/ProfileFeed/index.tsx:93
7561
+
#: src/screens/Profile/ProfileFeed/index.tsx:94
7502
7562
#: src/screens/ProfileList/components/ErrorScreen.tsx:35
7503
7563
#: src/screens/Settings/components/ChangeHandleDialog.tsx:575
7504
7564
#: src/screens/VideoFeed/index.tsx:1163
···
7582
7642
7583
7643
#: src/components/dialogs/nuxs/BookmarksAnnouncement.tsx:143
7584
7644
#: src/Navigation.tsx:608
7585
-
#: src/screens/Bookmarks/index.tsx:55
7645
+
#: src/screens/Bookmarks/index.tsx:59
7586
7646
msgid "Saved Posts"
7587
7647
msgstr ""
7588
7648
···
7615
7675
msgid "Scroll right"
7616
7676
msgstr ""
7617
7677
7618
-
#: src/screens/ProfileList/AboutSection.tsx:130
7678
+
#: src/screens/ProfileList/AboutSection.tsx:131
7619
7679
msgid "Scroll to top"
7620
7680
msgstr ""
7621
7681
···
7747
7807
7748
7808
#: src/components/FeedInterstitials.tsx:392
7749
7809
msgid "See more suggested profiles on the Explore page"
7810
+
msgstr ""
7811
+
7812
+
#: src/view/com/profile/ProfileFollows.tsx:155
7813
+
#: src/view/com/profile/ProfileFollows.tsx:156
7814
+
msgid "See suggested accounts"
7750
7815
msgstr ""
7751
7816
7752
7817
#: src/screens/SavedFeeds.tsx:220
···
8416
8481
msgid "Something went wrong, please try again."
8417
8482
msgstr ""
8418
8483
8419
-
#: src/components/Lists.tsx:174
8484
+
#: src/components/Lists.tsx:184
8420
8485
msgid "Something went wrong!"
8421
8486
msgstr ""
8422
8487
···
8480
8545
msgid "Start a new chat"
8481
8546
msgstr ""
8482
8547
8483
-
#: src/screens/ProfileList/AboutSection.tsx:102
8484
-
#: src/screens/ProfileList/FeedSection.tsx:74
8548
+
#: src/screens/ProfileList/AboutSection.tsx:103
8549
+
#: src/screens/ProfileList/FeedSection.tsx:81
8485
8550
msgid "Start adding people"
8486
8551
msgstr ""
8487
8552
8488
-
#: src/screens/ProfileList/AboutSection.tsx:108
8489
-
#: src/screens/ProfileList/FeedSection.tsx:80
8553
+
#: src/screens/ProfileList/AboutSection.tsx:109
8554
+
#: src/screens/ProfileList/FeedSection.tsx:87
8490
8555
msgid "Start adding people!"
8491
8556
msgstr ""
8492
8557
···
8518
8583
msgstr ""
8519
8584
8520
8585
#: src/screens/Search/Explore.tsx:625
8521
-
#: src/view/screens/Profile.tsx:231
8586
+
#: src/view/screens/Profile.tsx:240
8522
8587
msgid "Starter Packs"
8523
8588
msgstr ""
8524
8589
8525
-
#: src/components/StarterPack/ProfileStarterPacks.tsx:262
8590
+
#: src/components/StarterPack/ProfileStarterPacks.tsx:294
8526
8591
msgid "Starter packs let you easily share your favorite feeds and people with your friends."
8592
+
msgstr ""
8593
+
8594
+
#: src/view/screens/Profile.tsx:539
8595
+
msgid "Starter packs let you share your favorite feeds and people with your friends."
8527
8596
msgstr ""
8528
8597
8529
8598
#: src/screens/Settings/AboutSettings.tsx:100
···
8928
8997
msgid "There was an issue contacting the server, please check your internet connection and try again."
8929
8998
msgstr ""
8930
8999
8931
-
#: src/view/com/notifications/NotificationFeed.tsx:130
9000
+
#: src/view/com/notifications/NotificationFeed.tsx:131
8932
9001
msgid "There was an issue fetching notifications. Tap here to try again."
8933
9002
msgstr ""
8934
9003
···
8941
9010
msgid "There was an issue fetching the list. Tap here to try again."
8942
9011
msgstr ""
8943
9012
8944
-
#: src/screens/Settings/AppPasswords.tsx:58
9013
+
#: src/screens/Settings/AppPasswords.tsx:59
8945
9014
msgid "There was an issue fetching your app passwords"
8946
9015
msgstr ""
8947
9016
8948
-
#: src/view/com/feeds/ProfileFeedgens.tsx:163
8949
-
#: src/view/com/lists/ProfileLists.tsx:161
9017
+
#: src/view/com/feeds/ProfileFeedgens.tsx:174
9018
+
#: src/view/com/lists/ProfileLists.tsx:175
8950
9019
msgid "There was an issue fetching your lists. Tap here to try again."
8951
9020
msgstr ""
8952
9021
···
8954
9023
msgid "There was an issue fetching your service info"
8955
9024
msgstr ""
8956
9025
8957
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:149
9026
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:152
8958
9027
msgid "There was an issue removing this feed. Please check your internet connection and try again."
8959
9028
msgstr ""
8960
9029
···
9070
9139
msgid "This content is not available because one of the users involved has blocked the other."
9071
9140
msgstr ""
9072
9141
9073
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:118
9142
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:121
9074
9143
msgid "This content is not viewable without a Bluesky account."
9075
9144
msgstr ""
9076
9145
···
9098
9167
msgid "This feature is not available while using an App Password. Please sign in with your main password."
9099
9168
msgstr ""
9100
9169
9101
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:124
9170
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:127
9102
9171
msgid "This feed is currently receiving high traffic and is temporarily unavailable. Please try again later."
9103
9172
msgstr ""
9104
9173
···
9106
9175
msgid "This feed is empty! You may need to follow more users or tune your language settings."
9107
9176
msgstr ""
9108
9177
9109
-
#: src/components/StarterPack/Main/PostsList.tsx:36
9110
-
#: src/screens/Profile/ProfileFeed/index.tsx:192
9111
-
#: src/screens/ProfileList/FeedSection.tsx:71
9178
+
#: src/components/StarterPack/Main/PostsList.tsx:41
9179
+
#: src/screens/Profile/ProfileFeed/index.tsx:197
9180
+
#: src/screens/ProfileList/FeedSection.tsx:77
9112
9181
msgid "This feed is empty."
9113
9182
msgstr ""
9114
9183
···
9124
9193
msgid "This information is private and not shared with other users."
9125
9194
msgstr ""
9126
9195
9196
+
#: src/view/screens/Debug.tsx:343
9197
+
msgid "This is an empty state"
9198
+
msgstr ""
9199
+
9127
9200
#: src/components/live/EditLiveDialog.tsx:189
9128
9201
#: src/components/live/GoLiveDialog.tsx:157
9129
9202
msgid "This is not a valid link"
···
9153
9226
msgid "This list – created by you – contains possible violations of Bluesky's community guidelines in its name or description."
9154
9227
msgstr ""
9155
9228
9156
-
#: src/screens/ProfileList/AboutSection.tsx:98
9229
+
#: src/screens/ProfileList/AboutSection.tsx:99
9157
9230
msgid "This list is empty."
9158
9231
msgstr ""
9159
9232
···
9173
9246
msgid "This post is only visible to logged-in users."
9174
9247
msgstr ""
9175
9248
9176
-
#: src/screens/Bookmarks/index.tsx:245
9249
+
#: src/screens/Bookmarks/index.tsx:249
9177
9250
msgid "This post was deleted by its author"
9178
9251
msgstr ""
9179
9252
···
9209
9282
msgid "This user does not have a display name, and therefore cannot be verified."
9210
9283
msgstr ""
9211
9284
9212
-
#: src/view/com/profile/ProfileFollowers.tsx:133
9285
+
#: src/view/com/profile/ProfileFollowers.tsx:136
9213
9286
msgid "This user doesn't have any followers."
9214
9287
msgstr ""
9215
9288
···
9238
9311
msgid "This user is new here. Press for more info about when they joined."
9239
9312
msgstr ""
9240
9313
9241
-
#: src/view/com/profile/ProfileFollows.tsx:133
9314
+
#: src/view/com/profile/ProfileFollows.tsx:147
9242
9315
msgid "This user isn't following anyone."
9243
9316
msgstr ""
9244
9317
···
9706
9779
msgid "Uploading video..."
9707
9780
msgstr ""
9708
9781
9709
-
#: src/screens/Settings/AppPasswords.tsx:65
9782
+
#: src/screens/Settings/AppPasswords.tsx:66
9710
9783
msgid "Use app passwords to sign in to other Bluesky clients without giving full access to your account or password."
9711
9784
msgstr ""
9712
9785
···
9960
10033
msgid "Video: {0}"
9961
10034
msgstr ""
9962
10035
9963
-
#: src/view/screens/Profile.tsx:228
10036
+
#: src/view/screens/Profile.tsx:237
9964
10037
msgid "Videos"
9965
10038
msgstr ""
9966
10039
···
10032
10105
#: src/components/ProfileHoverCard/index.web.tsx:466
10033
10106
#: src/components/ProfileHoverCard/index.web.tsx:486
10034
10107
#: src/components/ProfileHoverCard/index.web.tsx:513
10035
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:179
10108
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:182
10036
10109
#: src/view/com/util/PostMeta.tsx:90
10037
10110
#: src/view/com/util/PostMeta.tsx:127
10038
10111
msgid "View profile"
···
10262
10335
msgid "We're sorry! The post you are replying to has been deleted."
10263
10336
msgstr ""
10264
10337
10265
-
#: src/components/Lists.tsx:194
10338
+
#: src/components/Lists.tsx:221
10266
10339
#: src/view/screens/NotFound.tsx:50
10267
10340
msgid "We're sorry! We can't find the page you were looking for."
10268
10341
msgstr ""
···
10386
10459
msgid "Write a message"
10387
10460
msgstr ""
10388
10461
10462
+
#: src/view/screens/Profile.tsx:433
10463
+
#: src/view/screens/Profile.tsx:434
10464
+
msgid "Write a post"
10465
+
msgstr ""
10466
+
10389
10467
#: src/view/com/composer/Composer.tsx:955
10390
10468
msgid "Write post"
10391
10469
msgstr ""
···
10471
10549
msgid "You are not allowed to upload videos."
10472
10550
msgstr ""
10473
10551
10474
-
#: src/view/com/profile/ProfileFollows.tsx:132
10475
-
msgid "You are not following anyone."
10552
+
#: src/view/com/profile/ProfileFollows.tsx:146
10553
+
msgid "You are not following anyone yet"
10476
10554
msgstr ""
10477
10555
10478
10556
#: src/components/live/queries.ts:156
···
10549
10627
msgid "You can update this later from your settings."
10550
10628
msgstr ""
10551
10629
10552
-
#: src/view/com/profile/ProfileFollowers.tsx:132
10553
-
msgid "You do not have any followers."
10554
-
msgstr ""
10555
-
10556
10630
#: src/screens/Profile/KnownFollowers.tsx:112
10557
10631
msgid "You don't follow any users who follow @{name}."
10558
10632
msgstr ""
···
10614
10688
msgid "You have no conversations yet. Start one!"
10615
10689
msgstr ""
10616
10690
10617
-
#: src/view/com/feeds/ProfileFeedgens.tsx:151
10618
-
msgid "You have no feeds."
10619
-
msgstr ""
10620
-
10621
10691
#: src/view/com/lists/MyLists.tsx:81
10622
-
#: src/view/com/lists/ProfileLists.tsx:149
10623
10692
msgid "You have no lists."
10624
10693
msgstr ""
10625
10694
···
10635
10704
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."
10636
10705
msgstr ""
10637
10706
10638
-
#: src/components/Lists.tsx:57
10707
+
#: src/components/Lists.tsx:61
10639
10708
msgid "You have reached the end"
10640
10709
msgstr ""
10641
10710
···
10647
10716
msgid "You have temporarily reached the limit for video uploads. Please try again later."
10648
10717
msgstr ""
10649
10718
10650
-
#: src/components/StarterPack/ProfileStarterPacks.tsx:259
10719
+
#: src/components/StarterPack/ProfileStarterPacks.tsx:291
10651
10720
msgid "You haven't created a starter pack yet!"
10721
+
msgstr ""
10722
+
10723
+
#: src/view/com/feeds/ProfileFeedgens.tsx:155
10724
+
msgid "You haven't made any custom feeds yet."
10652
10725
msgstr ""
10653
10726
10654
10727
#: src/components/dialogs/MutedWords.tsx:403
···
10696
10769
msgid "You must be at least 13 years old to use Bluesky. Read our <0>Terms of Service</0> for more information."
10697
10770
msgstr ""
10698
10771
10699
-
#: src/components/StarterPack/ProfileStarterPacks.tsx:330
10772
+
#: src/components/StarterPack/ProfileStarterPacks.tsx:362
10700
10773
msgid "You must be following at least seven other people to generate a starter pack."
10701
10774
msgstr ""
10702
10775
+30
-4
src/screens/Bookmarks/index.tsx
+30
-4
src/screens/Bookmarks/index.tsx
···
7
7
} from '@atproto/api'
8
8
import {msg, Trans} from '@lingui/macro'
9
9
import {useLingui} from '@lingui/react'
10
-
import {useFocusEffect} from '@react-navigation/native'
10
+
import {
11
+
type NavigationProp,
12
+
useFocusEffect,
13
+
useNavigation,
14
+
} from '@react-navigation/native'
11
15
12
16
import {useCleanError} from '#/lib/hooks/useCleanError'
13
17
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
···
21
25
import {useBookmarksQuery} from '#/state/queries/bookmarks/useBookmarksQuery'
22
26
import {useSetMinimalShellMode} from '#/state/shell'
23
27
import {Post} from '#/view/com/post/Post'
28
+
import {EmptyState} from '#/view/com/util/EmptyState'
24
29
import {List} from '#/view/com/util/List'
25
30
import {PostFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
26
-
import {EmptyState} from '#/screens/Bookmarks/components/EmptyState'
27
31
import {atoms as a, useTheme} from '#/alf'
28
32
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
29
-
import {BookmarkFilled} from '#/components/icons/Bookmark'
33
+
import {BookmarkDeleteLarge, BookmarkFilled} from '#/components/icons/Bookmark'
30
34
import {CircleQuestion_Stroke2_Corner2_Rounded as QuestionIcon} from '#/components/icons/CircleQuestion'
31
35
import * as Layout from '#/components/Layout'
32
36
import {ListFooter} from '#/components/Lists'
···
259
263
)
260
264
}
261
265
266
+
function BookmarksEmpty() {
267
+
const t = useTheme()
268
+
const {_} = useLingui()
269
+
const navigation = useNavigation<NavigationProp<CommonNavigatorParams>>()
270
+
271
+
return (
272
+
<EmptyState
273
+
icon={BookmarkDeleteLarge}
274
+
message={_(msg`Nothing saved yet`)}
275
+
textStyle={[t.atoms.text_contrast_medium, a.font_medium]}
276
+
button={{
277
+
label: _(msg`Button to go back to the home timeline`),
278
+
text: _(msg`Go home`),
279
+
onPress: () => navigation.navigate('Home' as never),
280
+
size: 'small',
281
+
color: 'secondary',
282
+
}}
283
+
style={[a.pt_3xl]}
284
+
/>
285
+
)
286
+
}
287
+
262
288
function renderItem({item, index}: {item: ListItem; index: number}) {
263
289
switch (item.type) {
264
290
case 'loading': {
265
291
return <PostFeedLoadingPlaceholder />
266
292
}
267
293
case 'empty': {
268
-
return <EmptyState />
294
+
return <BookmarksEmpty />
269
295
}
270
296
case 'bookmark': {
271
297
return (
+6
-1
src/screens/Notifications/ActivityList.tsx
+6
-1
src/screens/Notifications/ActivityList.tsx
···
5
5
import {type AllNavigatorParams} from '#/lib/routes/types'
6
6
import {PostFeed} from '#/view/com/posts/PostFeed'
7
7
import {EmptyState} from '#/view/com/util/EmptyState'
8
+
import {EditBig_Stroke1_Corner0_Rounded as EditIcon} from '#/components/icons/EditBig'
8
9
import * as Layout from '#/components/Layout'
9
10
import {ListFooter} from '#/components/Lists'
10
11
···
35
36
feed={`posts|${uris}`}
36
37
disablePoll
37
38
renderEmptyState={() => (
38
-
<EmptyState icon="growth" message={_(msg`No skeets here`)} />
39
+
<EmptyState
40
+
icon={EditIcon}
41
+
iconSize="2xl"
42
+
message={_(msg`No posts here`)}
43
+
/>
39
44
)}
40
45
renderEndOfFeed={() => <ListFooter />}
41
46
/>
+13
-5
src/screens/Onboarding/StepProfile/index.tsx
+13
-5
src/screens/Onboarding/StepProfile/index.tsx
···
15
15
import {getDataUriSize} from '#/lib/media/util'
16
16
import {useRequestNotificationsPermission} from '#/lib/notifications/notifications'
17
17
import {logEvent, useGate} from '#/lib/statsig/statsig'
18
+
import {isCancelledError} from '#/lib/strings/errors'
19
+
import {logger} from '#/logger'
18
20
import {isNative, isWeb} from '#/platform/detection'
19
21
import {
20
22
DescriptionText,
···
184
186
if (!image) return
185
187
186
188
if (!isWeb) {
187
-
image = await openCropper({
188
-
imageUri: image.path,
189
-
shape: 'circle',
190
-
aspectRatio: 1 / 1,
191
-
})
189
+
try {
190
+
image = await openCropper({
191
+
imageUri: image.path,
192
+
shape: 'circle',
193
+
aspectRatio: 1 / 1,
194
+
})
195
+
} catch (e) {
196
+
if (!isCancelledError(e)) {
197
+
logger.error('Failed to crop avatar in onboarding', {error: e})
198
+
}
199
+
}
192
200
}
193
201
image = await compressIfNeeded(image, 1000000)
194
202
+2
-2
src/screens/PostThread/components/ThreadItemAnchor.tsx
+2
-2
src/screens/PostThread/components/ThreadItemAnchor.tsx
···
588
588
<BackdatedPostIndicator post={post} />
589
589
<View style={[a.flex_row, a.align_center, a.flex_wrap, a.gap_sm]}>
590
590
<Text style={[a.text_sm, t.atoms.text_contrast_medium]}>
591
-
{niceDate(i18n, post.indexedAt, 'medium')}
591
+
{niceDate(i18n, post.indexedAt, 'dot separated')}
592
592
</Text>
593
593
{isRootPost && (
594
594
<WhoCanReply post={post} isThreadAuthor={isThreadAuthor} />
···
674
674
a.leading_tight,
675
675
t.atoms.text_contrast_medium,
676
676
]}>
677
-
<Trans>Archived from {niceDate(i18n, createdAt)}</Trans>
677
+
<Trans>Archived from {niceDate(i18n, createdAt, 'medium')}</Trans>
678
678
</Text>
679
679
</View>
680
680
)}
+8
-1
src/screens/Profile/ProfileFeed/index.tsx
+8
-1
src/screens/Profile/ProfileFeed/index.tsx
···
45
45
ProfileFeedHeader,
46
46
ProfileFeedHeaderSkeleton,
47
47
} from '#/screens/Profile/components/ProfileFeedHeader'
48
+
import {HashtagWide_Stroke1_Corner0_Rounded as HashtagWideIcon} from '#/components/icons/Hashtag'
48
49
import * as Layout from '#/components/Layout'
49
50
50
51
type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileFeed'>
···
189
190
}, [onScrollToTop, isScreenFocused])
190
191
191
192
const renderPostsEmpty = useCallback(() => {
192
-
return <EmptyState icon="hashtag" message={_(msg`This feed is empty.`)} />
193
+
return (
194
+
<EmptyState
195
+
icon={HashtagWideIcon}
196
+
iconSize="2xl"
197
+
message={_(msg`This feed is empty.`)}
198
+
/>
199
+
)
193
200
}, [_])
194
201
195
202
const isVideoFeed = React.useMemo(() => {
+28
-6
src/screens/Profile/Sections/Feed.tsx
+28
-6
src/screens/Profile/Sections/Feed.tsx
···
6
6
7
7
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
8
8
import {isIOS, isNative} from '#/platform/detection'
9
-
import {type FeedDescriptor} from '#/state/queries/post-feed'
10
-
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
9
+
import {
10
+
type FeedDescriptor,
11
+
RQKEY as FEED_RQKEY,
12
+
} from '#/state/queries/post-feed'
11
13
import {truncateAndInvalidate} from '#/state/queries/util'
12
14
import {PostFeed} from '#/view/com/posts/PostFeed'
13
-
import {EmptyState} from '#/view/com/util/EmptyState'
15
+
import {
16
+
EmptyState,
17
+
type EmptyStateButtonProps,
18
+
} from '#/view/com/util/EmptyState'
14
19
import {type ListRef} from '#/view/com/util/List'
15
20
import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn'
16
21
import {atoms as a, ios, useTheme} from '#/alf'
22
+
import {EditBig_Stroke1_Corner0_Rounded as EditIcon} from '#/components/icons/EditBig'
17
23
import {Text} from '#/components/Typography'
18
24
import {type SectionRef} from './types'
19
25
···
25
31
scrollElRef: ListRef
26
32
ignoreFilterFor?: string
27
33
setScrollViewTag: (tag: number | null) => void
34
+
emptyStateMessage?: string
35
+
emptyStateButton?: EmptyStateButtonProps
36
+
emptyStateIcon?: React.ComponentType<any> | React.ReactElement
28
37
}
38
+
29
39
export function ProfileFeedSection({
30
40
ref,
31
41
feed,
···
34
44
scrollElRef,
35
45
ignoreFilterFor,
36
46
setScrollViewTag,
47
+
emptyStateMessage,
48
+
emptyStateButton,
49
+
emptyStateIcon,
37
50
}: FeedSectionProps) {
38
51
const {_} = useLingui()
39
52
const queryClient = useQueryClient()
···
44
57
const adjustedInitialNumToRender = useInitialNumToRender({
45
58
screenHeightOffset: headerHeight,
46
59
})
47
-
48
60
const onScrollToTop = useCallback(() => {
49
61
scrollElRef.current?.scrollToOffset({
50
62
animated: isNative,
···
59
71
}))
60
72
61
73
const renderPostsEmpty = useCallback(() => {
62
-
return <EmptyState icon="growth" message={_(msg`No skeets yet.`)} />
63
-
}, [_])
74
+
return (
75
+
<View style={[a.flex_1, a.justify_center, a.align_center]}>
76
+
<EmptyState
77
+
style={{width: '100%'}}
78
+
icon={emptyStateIcon || EditIcon}
79
+
iconSize="3xl"
80
+
message={emptyStateMessage || _(msg`No posts yet`)}
81
+
button={emptyStateButton}
82
+
/>
83
+
</View>
84
+
)
85
+
}, [_, emptyStateButton, emptyStateIcon, emptyStateMessage])
64
86
65
87
useEffect(() => {
66
88
if (isIOS && isFocused && scrollElRef.current) {
+3
-2
src/screens/ProfileList/AboutSection.tsx
+3
-2
src/screens/ProfileList/AboutSection.tsx
···
12
12
import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn'
13
13
import {atoms as a, useBreakpoints} from '#/alf'
14
14
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
15
+
import {BulletList_Stroke1_Corner0_Rounded as ListIcon} from '#/components/icons/BulletList'
15
16
import {PersonPlus_Stroke2_Corner0_Rounded as PersonPlusIcon} from '#/components/icons/Person'
16
17
17
18
interface SectionRef {
···
95
96
const renderEmptyState = useCallback(() => {
96
97
return (
97
98
<View style={[a.gap_xl, a.align_center]}>
98
-
<EmptyState icon="users-slash" message={_(msg`This list is empty.`)} />
99
+
<EmptyState icon={ListIcon} message={_(msg`This list is empty.`)} />
99
100
{isOwner && (
100
101
<Button
101
102
testID="emptyStateAddUserBtn"
···
111
112
)}
112
113
</View>
113
114
)
114
-
}, [_, onPressAddUser, isOwner])
115
+
}, [_, isOwner, onPressAddUser])
115
116
116
117
return (
117
118
<View>
+10
-3
src/screens/ProfileList/FeedSection.tsx
+10
-3
src/screens/ProfileList/FeedSection.tsx
···
7
7
8
8
import {isNative} from '#/platform/detection'
9
9
import {listenSoftReset} from '#/state/events'
10
-
import {type FeedDescriptor} from '#/state/queries/post-feed'
11
-
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
10
+
import {
11
+
type FeedDescriptor,
12
+
RQKEY as FEED_RQKEY,
13
+
} from '#/state/queries/post-feed'
12
14
import {PostFeed} from '#/view/com/posts/PostFeed'
13
15
import {EmptyState} from '#/view/com/util/EmptyState'
14
16
import {type ListRef} from '#/view/com/util/List'
15
17
import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn'
16
18
import {atoms as a} from '#/alf'
17
19
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
20
+
import {HashtagWide_Stroke1_Corner0_Rounded as HashtagWideIcon} from '#/components/icons/Hashtag'
18
21
import {PersonPlus_Stroke2_Corner0_Rounded as PersonPlusIcon} from '#/components/icons/Person'
19
22
20
23
interface SectionRef {
···
68
71
const renderPostsEmpty = useCallback(() => {
69
72
return (
70
73
<View style={[a.gap_xl, a.align_center]}>
71
-
<EmptyState icon="hashtag" message={_(msg`This feed is empty.`)} />
74
+
<EmptyState
75
+
icon={HashtagWideIcon}
76
+
iconSize="2xl"
77
+
message={_(msg`This feed is empty.`)}
78
+
/>
72
79
{isOwner && (
73
80
<Button
74
81
label={_(msg`Start adding people`)}
+2
-1
src/screens/Settings/AppPasswords.tsx
+2
-1
src/screens/Settings/AppPasswords.tsx
···
24
24
import {Admonition, colors} from '#/components/Admonition'
25
25
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
26
26
import {useDialogControl} from '#/components/Dialog'
27
+
import {Growth_Stroke2_Corner0_Rounded as Growth} from '#/components/icons/Growth'
27
28
import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus'
28
29
import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash'
29
30
import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning'
···
102
103
</View>
103
104
) : (
104
105
<EmptyState
105
-
icon="growth"
106
+
icon={Growth}
106
107
message={_(msg`No app passwords yet`)}
107
108
/>
108
109
)
+1
src/screens/Settings/components/SettingsList.tsx
+1
src/screens/Settings/components/SettingsList.tsx
+2
-1
src/state/gallery.ts
+2
-1
src/state/gallery.ts
···
18
18
import {openCropper} from '#/lib/media/picker'
19
19
import {type PickerImage} from '#/lib/media/picker.shared'
20
20
import {getDataUriSize} from '#/lib/media/util'
21
+
import {isCancelledError} from '#/lib/strings/errors'
21
22
import {isNative} from '#/platform/detection'
22
23
23
24
export type ImageTransformation = {
···
147
148
},
148
149
}
149
150
} catch (e) {
150
-
if (e instanceof Error && e.message.includes('User cancelled')) {
151
+
if (!isCancelledError(e)) {
151
152
return img
152
153
}
153
154
+1
-1
src/view/com/composer/photos/Gallery.tsx
+1
-1
src/view/com/composer/photos/Gallery.tsx
···
241
241
accessibilityIgnoresInvertColors
242
242
cachePolicy="none"
243
243
autoplay={false}
244
+
contentFit="cover"
244
245
/>
245
246
246
247
<MediaInsetBorder />
···
285
286
marginTop: 16,
286
287
},
287
288
image: {
288
-
resizeMode: 'cover',
289
289
borderRadius: tokens.borderRadius.md,
290
290
},
291
291
imageControl: {
+15
-4
src/view/com/feeds/ProfileFeedgens.tsx
+15
-4
src/view/com/feeds/ProfileFeedgens.tsx
···
15
15
} from 'react-native'
16
16
import {msg} from '@lingui/macro'
17
17
import {useLingui} from '@lingui/react'
18
+
import {useNavigation} from '@react-navigation/native'
18
19
import {useQueryClient} from '@tanstack/react-query'
19
20
20
21
import {cleanError} from '#/lib/strings/errors'
···
29
30
import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn'
30
31
import {atoms as a, ios, useTheme} from '#/alf'
31
32
import * as FeedCard from '#/components/FeedCard'
33
+
import {HashtagWide_Stroke1_Corner0_Rounded as HashtagWideIcon} from '#/components/icons/Hashtag'
32
34
import {ListFooter} from '#/components/Lists'
33
35
34
36
const LOADING = {_reactKey: '__loading__'}
···
78
80
} = useProfileFeedgensQuery(did, opts)
79
81
const isEmpty = !isPending && !data?.pages[0]?.feeds.length
80
82
const {data: preferences} = usePreferencesQuery()
83
+
const navigation = useNavigation()
81
84
82
85
const items = useMemo(() => {
83
86
let items: any[] = []
···
147
150
if (item === EMPTY) {
148
151
return (
149
152
<EmptyState
150
-
icon="hashtag"
151
-
message={_(msg`You have no feeds.`)}
152
-
testID="listsEmpty"
153
+
style={{width: '100%'}}
154
+
icon={HashtagWideIcon}
155
+
message={_(msg`You haven't made any custom feeds yet.`)}
156
+
textStyle={[t.atoms.text_contrast_medium, a.font_medium]}
157
+
button={{
158
+
label: _(msg`Browse custom feeds`),
159
+
text: _(msg`Browse custom feeds`),
160
+
onPress: () => navigation.navigate('Feeds' as never),
161
+
size: 'small',
162
+
color: 'secondary',
163
+
}}
153
164
/>
154
165
)
155
166
} else if (item === ERROR_ITEM) {
···
183
194
}
184
195
return null
185
196
},
186
-
[_, t, error, refetch, onPressRetryLoadMore, preferences],
197
+
[_, t, error, refetch, onPressRetryLoadMore, preferences, navigation],
187
198
)
188
199
189
200
useEffect(() => {
+6
-7
src/view/com/lists/MyLists.tsx
+6
-7
src/view/com/lists/MyLists.tsx
···
19
19
import {useModerationOpts} from '#/state/preferences/moderation-opts'
20
20
import {type MyListsFilter, useMyListsQuery} from '#/state/queries/my-lists'
21
21
import {atoms as a, useTheme} from '#/alf'
22
-
import {BulletList_Stroke2_Corner0_Rounded as ListIcon} from '#/components/icons/BulletList'
22
+
import {BulletList_Stroke1_Corner0_Rounded as ListIcon} from '#/components/icons/BulletList'
23
23
import * as ListCard from '#/components/ListCard'
24
24
import {Text} from '#/components/Typography'
25
25
import {ErrorMessage} from '../util/error/ErrorMessage'
···
71
71
switch (filter) {
72
72
case 'curate':
73
73
emptyText = _(
74
-
msg`Public, sharable lists which can be used to drive feeds.`,
74
+
msg`Lists allow you to see content from your favorite people.`,
75
75
)
76
76
break
77
77
case 'mod':
···
104
104
({item, index}: {item: any; index: number}) => {
105
105
if (item === EMPTY) {
106
106
return (
107
-
<View style={[a.flex_1, a.align_center, a.gap_sm, a.px_xl, a.pt_xl]}>
107
+
<View style={[a.flex_1, a.align_center, a.gap_sm, a.px_xl, a.pt_3xl]}>
108
108
<View
109
109
style={[
110
110
a.align_center,
111
111
a.justify_center,
112
112
enableSquareButtons ? a.rounded_sm : a.rounded_full,
113
-
t.atoms.bg_contrast_25,
114
113
{
115
-
width: 32,
116
-
height: 32,
114
+
width: 64,
115
+
height: 64,
117
116
},
118
117
]}>
119
-
<ListIcon size="md" fill={t.atoms.text_contrast_low.color} />
118
+
<ListIcon size="2xl" fill={t.atoms.text_contrast_medium.color} />
120
119
</View>
121
120
<Text
122
121
style={[
+50
-33
src/view/com/lists/ProfileLists.tsx
+50
-33
src/view/com/lists/ProfileLists.tsx
···
15
15
} from 'react-native'
16
16
import {msg} from '@lingui/macro'
17
17
import {useLingui} from '@lingui/react'
18
+
import {useNavigation} from '@react-navigation/native'
18
19
import {useQueryClient} from '@tanstack/react-query'
19
20
20
21
import {cleanError} from '#/lib/strings/errors'
21
22
import {logger} from '#/logger'
22
23
import {isIOS, isNative, isWeb} from '#/platform/detection'
23
-
import {RQKEY, useProfileListsQuery} from '#/state/queries/profile-lists'
24
+
import {usePreferencesQuery} from '#/state/queries/preferences'
25
+
import {RQKEY, useProfileFeedgensQuery} from '#/state/queries/profile-feedgens'
24
26
import {EmptyState} from '#/view/com/util/EmptyState'
25
27
import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
26
28
import {List, type ListRef} from '#/view/com/util/List'
27
29
import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
28
30
import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn'
29
31
import {atoms as a, ios, useTheme} from '#/alf'
30
-
import * as ListCard from '#/components/ListCard'
32
+
import * as FeedCard from '#/components/FeedCard'
33
+
import {BulletList_Stroke1_Corner0_Rounded as ListIcon} from '#/components/icons/BulletList'
31
34
import {ListFooter} from '#/components/Lists'
32
35
33
36
const LOADING = {_reactKey: '__loading__'}
···
39
42
scrollToTop: () => void
40
43
}
41
44
42
-
interface ProfileListsProps {
45
+
interface ProfileFeedgensProps {
43
46
ref?: React.Ref<SectionRef>
44
47
did: string
45
48
scrollElRef: ListRef
···
59
62
style,
60
63
testID,
61
64
setScrollViewTag,
62
-
}: ProfileListsProps) {
65
+
}: ProfileFeedgensProps) {
66
+
const {_} = useLingui()
63
67
const t = useTheme()
64
-
const {_} = useLingui()
68
+
const [isPTRing, setIsPTRing] = useState(false)
65
69
const {height} = useWindowDimensions()
66
-
const [isPTRing, setIsPTRing] = useState(false)
67
70
const opts = useMemo(() => ({enabled}), [enabled])
68
71
const {
69
72
data,
70
73
isPending,
74
+
isFetchingNextPage,
71
75
hasNextPage,
72
76
fetchNextPage,
73
-
isFetchingNextPage,
74
77
isError,
75
78
error,
76
79
refetch,
77
-
} = useProfileListsQuery(did, opts)
78
-
const isEmpty = !isPending && !data?.pages[0]?.lists.length
80
+
} = useProfileFeedgensQuery(did, opts)
81
+
const isEmpty = !isPending && !data?.pages[0]?.feeds.length
82
+
const {data: preferences} = usePreferencesQuery()
83
+
const navigation = useNavigation()
79
84
80
85
const items = useMemo(() => {
81
86
let items: any[] = []
···
88
93
items = items.concat([EMPTY])
89
94
} else if (data?.pages) {
90
95
for (const page of data?.pages) {
91
-
items = items.concat(page.lists)
96
+
items = items.concat(page.feeds)
92
97
}
93
-
}
94
-
if (isError && !isEmpty) {
98
+
} else if (isError && !isEmpty) {
95
99
items = items.concat([LOAD_MORE_ERROR_ITEM])
96
100
}
97
101
return items
···
119
123
try {
120
124
await refetch()
121
125
} catch (err) {
122
-
logger.error('Failed to refresh lists', {message: err})
126
+
logger.error('Failed to refresh feeds', {message: err})
123
127
}
124
128
setIsPTRing(false)
125
129
}, [refetch, setIsPTRing])
126
130
127
131
const onEndReached = useCallback(async () => {
128
132
if (isFetchingNextPage || !hasNextPage || isError) return
133
+
129
134
try {
130
135
await fetchNextPage()
131
136
} catch (err) {
132
-
logger.error('Failed to load more lists', {message: err})
137
+
logger.error('Failed to load more feeds', {message: err})
133
138
}
134
139
}, [isFetchingNextPage, hasNextPage, isError, fetchNextPage])
135
140
···
140
145
// rendering
141
146
// =
142
147
143
-
const renderItemInner = useCallback(
148
+
const renderItem = useCallback(
144
149
({item, index}: ListRenderItemInfo<any>) => {
145
150
if (item === EMPTY) {
146
151
return (
147
152
<EmptyState
148
-
icon="list-ul"
149
-
message={_(msg`You have no lists.`)}
150
-
testID="listsEmpty"
153
+
icon={ListIcon}
154
+
message={_(
155
+
msg`Lists allow you to see content from your favorite people.`,
156
+
)}
157
+
textStyle={[t.atoms.text_contrast_medium, a.font_medium]}
158
+
button={{
159
+
label: _(msg`Create a list`),
160
+
text: _(msg`Create a list`),
161
+
onPress: () => navigation.navigate('Lists' as never),
162
+
size: 'small',
163
+
color: 'primary',
164
+
}}
151
165
/>
152
166
)
153
167
} else if (item === ERROR_ITEM) {
···
166
180
} else if (item === LOADING) {
167
181
return <FeedLoadingPlaceholder />
168
182
}
169
-
return (
170
-
<View
171
-
style={[
172
-
(index !== 0 || isWeb) && a.border_t,
173
-
t.atoms.border_contrast_low,
174
-
a.px_lg,
175
-
a.py_lg,
176
-
]}>
177
-
<ListCard.Default view={item} />
178
-
</View>
179
-
)
183
+
if (preferences) {
184
+
return (
185
+
<View
186
+
style={[
187
+
(index !== 0 || isWeb) && a.border_t,
188
+
t.atoms.border_contrast_low,
189
+
a.px_lg,
190
+
a.py_lg,
191
+
]}>
192
+
<FeedCard.Default view={item} />
193
+
</View>
194
+
)
195
+
}
196
+
return null
180
197
},
181
-
[error, refetch, onPressRetryLoadMore, _, t.atoms.border_contrast_low],
198
+
[_, t, error, refetch, onPressRetryLoadMore, preferences, navigation],
182
199
)
183
200
184
201
useEffect(() => {
···
188
205
}
189
206
}, [enabled, scrollElRef, setScrollViewTag])
190
207
191
-
const ProfileListsFooter = useCallback(() => {
208
+
const ProfileFeedgensFooter = useCallback(() => {
192
209
if (isEmpty) return null
193
210
return (
194
211
<ListFooter
···
215
232
ref={scrollElRef}
216
233
data={items}
217
234
keyExtractor={keyExtractor}
218
-
renderItem={renderItemInner}
219
-
ListFooterComponent={ProfileListsFooter}
235
+
renderItem={renderItem}
236
+
ListFooterComponent={ProfileFeedgensFooter}
220
237
refreshing={isPTRing}
221
238
onRefresh={onRefresh}
222
239
headerOffset={headerOffset}
+2
-1
src/view/com/notifications/NotificationFeed.tsx
+2
-1
src/view/com/notifications/NotificationFeed.tsx
···
20
20
import {List, type ListProps, type ListRef} from '#/view/com/util/List'
21
21
import {NotificationFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
22
22
import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn'
23
+
import {Bell_Stroke2_Corner0_Rounded as BellIcon} from '#/components/icons/Bell'
23
24
import {NotificationFeedItem} from './NotificationFeedItem'
24
25
25
26
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
···
120
121
if (item === EMPTY_FEED_ITEM) {
121
122
return (
122
123
<EmptyState
123
-
icon="bell"
124
+
icon={BellIcon}
124
125
message={_(msg`No notifications yet!`)}
125
126
style={styles.emptyState}
126
127
/>
+5
-2
src/view/com/posts/PostFeedErrorMessage.tsx
+5
-2
src/view/com/posts/PostFeedErrorMessage.tsx
···
15
15
import {logger} from '#/logger'
16
16
import {type FeedDescriptor} from '#/state/queries/post-feed'
17
17
import {useRemoveFeedMutation} from '#/state/queries/preferences'
18
+
import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning'
18
19
import * as Prompt from '#/components/Prompt'
19
20
import {EmptyState} from '../util/EmptyState'
20
21
import {ErrorMessage} from '../util/error/ErrorMessage'
···
50
51
() => detectKnownError(feedDesc, error),
51
52
[feedDesc, error],
52
53
)
54
+
53
55
if (
54
56
typeof knownError !== 'undefined' &&
55
57
knownError !== KnownError.Unknown &&
···
68
70
if (knownError === KnownError.Block) {
69
71
return (
70
72
<EmptyState
71
-
icon="ban"
72
-
message={_l(msgLingui`Skeets hidden`)}
73
+
icon={WarningIcon}
74
+
iconSize="2xl"
75
+
message={_l(msgLingui`Posts hidden`)}
73
76
style={{paddingVertical: 40}}
74
77
/>
75
78
)
+13
-1
src/view/com/profile/ProfileFollowers.tsx
+13
-1
src/view/com/profile/ProfileFollowers.tsx
···
2
2
import {type AppBskyActorDefs as ActorDefs} from '@atproto/api'
3
3
import {msg} from '@lingui/macro'
4
4
import {useLingui} from '@lingui/react'
5
+
import {useNavigation} from '@react-navigation/native'
5
6
6
7
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
7
8
import {cleanError} from '#/lib/strings/errors'
···
9
10
import {useProfileFollowersQuery} from '#/state/queries/profile-followers'
10
11
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
11
12
import {useSession} from '#/state/session'
13
+
import {PeopleRemove2_Stroke1_Corner0_Rounded as PeopleRemoveIcon} from '#/components/icons/PeopleRemove2'
12
14
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
13
15
import {List} from '../util/List'
14
16
import {ProfileCardWithFollowBtn} from './ProfileCard'
···
39
41
40
42
export function ProfileFollowers({name}: {name: string}) {
41
43
const {_} = useLingui()
44
+
const navigation = useNavigation()
42
45
const initialNumToRender = useInitialNumToRender()
43
46
const {currentAccount} = useSession()
44
47
···
129
132
emptyType="results"
130
133
emptyMessage={
131
134
isMe
132
-
? _(msg`You do not have any followers.`)
135
+
? _(msg`No followers yet`)
133
136
: _(msg`This user doesn't have any followers.`)
134
137
}
135
138
errorMessage={cleanError(resolveError || error)}
136
139
onRetry={isError ? refetch : undefined}
137
140
sideBorders={false}
141
+
useEmptyState={true}
142
+
emptyStateIcon={PeopleRemoveIcon}
143
+
emptyStateButton={{
144
+
label: _(msg`Go back`),
145
+
text: _(msg`Go back`),
146
+
color: 'secondary',
147
+
size: 'small',
148
+
onPress: () => navigation.goBack(),
149
+
}}
138
150
/>
139
151
)
140
152
}
+24
-1
src/view/com/profile/ProfileFollows.tsx
+24
-1
src/view/com/profile/ProfileFollows.tsx
···
2
2
import {type AppBskyActorDefs as ActorDefs} from '@atproto/api'
3
3
import {msg} from '@lingui/macro'
4
4
import {useLingui} from '@lingui/react'
5
+
import {useNavigation} from '@react-navigation/native'
5
6
6
7
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
8
+
import {type NavigationProp} from '#/lib/routes/types'
7
9
import {cleanError} from '#/lib/strings/errors'
8
10
import {logger} from '#/logger'
11
+
import {isWeb} from '#/platform/detection'
9
12
import {useProfileFollowsQuery} from '#/state/queries/profile-follows'
10
13
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
11
14
import {useSession} from '#/state/session'
15
+
import {PeopleRemove2_Stroke1_Corner0_Rounded as PeopleRemoveIcon} from '#/components/icons/PeopleRemove2'
12
16
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
13
17
import {List} from '../util/List'
14
18
import {ProfileCardWithFollowBtn} from './ProfileCard'
···
41
45
const {_} = useLingui()
42
46
const initialNumToRender = useInitialNumToRender()
43
47
const {currentAccount} = useSession()
48
+
const navigation = useNavigation<NavigationProp>()
49
+
50
+
const onPressFindAccounts = React.useCallback(() => {
51
+
if (isWeb) {
52
+
navigation.navigate('Search', {})
53
+
} else {
54
+
navigation.navigate('SearchTab')
55
+
navigation.popToTop()
56
+
}
57
+
}, [navigation])
44
58
45
59
const [isPTRing, setIsPTRing] = React.useState(false)
46
60
const {
···
129
143
emptyType="results"
130
144
emptyMessage={
131
145
isMe
132
-
? _(msg`You are not following anyone.`)
146
+
? _(msg`You are not following anyone yet`)
133
147
: _(msg`This user isn't following anyone.`)
134
148
}
135
149
errorMessage={cleanError(resolveError || error)}
136
150
onRetry={isError ? refetch : undefined}
137
151
sideBorders={false}
152
+
useEmptyState={true}
153
+
emptyStateIcon={PeopleRemoveIcon}
154
+
emptyStateButton={{
155
+
label: _(msg`See suggested accounts`),
156
+
text: _(msg`See suggested accounts`),
157
+
onPress: onPressFindAccounts,
158
+
size: 'tiny',
159
+
color: 'primary',
160
+
}}
138
161
/>
139
162
)
140
163
}
+84
-49
src/view/com/util/EmptyState.tsx
+84
-49
src/view/com/util/EmptyState.tsx
···
1
-
import {type StyleProp, StyleSheet, View, type ViewStyle} from 'react-native'
2
-
import {type IconProp} from '@fortawesome/fontawesome-svg-core'
3
-
import {
4
-
FontAwesomeIcon,
5
-
type FontAwesomeIconStyle,
6
-
} from '@fortawesome/react-native-fontawesome'
1
+
import React from 'react'
2
+
import {type StyleProp, type TextStyle, type ViewStyle} from 'react-native'
3
+
import {View} from 'react-native'
7
4
8
5
import {usePalette} from '#/lib/hooks/usePalette'
9
6
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
10
-
import {UserGroupIcon} from '#/lib/icons'
11
-
import {Growth_Stroke2_Corner0_Rounded as Growth} from '#/components/icons/Growth'
12
-
import {Text} from './text/Text'
7
+
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
8
+
import {Button, type ButtonProps, ButtonText} from '#/components/Button'
9
+
import {EditBig_Stroke1_Corner0_Rounded as EditIcon} from '#/components/icons/EditBig'
10
+
import {Text} from '#/components/Typography'
11
+
12
+
export type EmptyStateButtonProps = Omit<ButtonProps, 'children' | 'label'> & {
13
+
label: string
14
+
text: string
15
+
}
13
16
14
17
export function EmptyState({
15
18
testID,
16
19
icon,
20
+
iconSize = '3xl',
17
21
message,
18
22
style,
23
+
textStyle,
24
+
button,
19
25
}: {
20
26
testID?: string
21
-
icon: IconProp | 'user-group' | 'growth'
27
+
icon?: React.ComponentType<any> | React.ReactElement
28
+
iconSize?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl'
22
29
message: string
23
30
style?: StyleProp<ViewStyle>
31
+
textStyle?: StyleProp<TextStyle>
32
+
button?: EmptyStateButtonProps
24
33
}) {
25
34
const pal = usePalette('default')
26
35
const {isTabletOrDesktop} = useWebMediaQueries()
27
-
const iconSize = isTabletOrDesktop ? 64 : 48
36
+
const t = useTheme()
37
+
const {gtMobile} = useBreakpoints()
38
+
39
+
const placeholderIcon = (
40
+
<EditIcon size="2xl" fill={t.atoms.text_contrast_medium.color} />
41
+
)
42
+
43
+
const renderIcon = () => {
44
+
if (!icon) {
45
+
return placeholderIcon
46
+
}
47
+
48
+
if (React.isValidElement(icon)) {
49
+
return icon
50
+
}
51
+
52
+
if (
53
+
typeof icon === 'function' ||
54
+
(typeof icon === 'object' && icon && 'render' in icon)
55
+
) {
56
+
const IconComponent = icon
57
+
return (
58
+
<IconComponent
59
+
size={iconSize}
60
+
fill={t.atoms.text_contrast_medium.color}
61
+
style={{color: t.atoms.text_contrast_low.color}}
62
+
/>
63
+
)
64
+
}
65
+
66
+
return placeholderIcon
67
+
}
68
+
28
69
return (
29
70
<View testID={testID} style={style}>
30
71
<View
31
72
style={[
32
-
styles.iconContainer,
33
-
isTabletOrDesktop && styles.iconContainerBig,
34
-
pal.viewLight,
73
+
a.flex_row,
74
+
a.align_center,
75
+
a.justify_center,
76
+
a.self_center,
77
+
a.rounded_full,
78
+
a.mt_5xl,
79
+
{height: 64, width: 64},
80
+
React.isValidElement(icon)
81
+
? a.bg_transparent
82
+
: [isTabletOrDesktop && {marginTop: 50}],
35
83
]}>
36
-
{icon === 'user-group' ? (
37
-
<UserGroupIcon size={iconSize} />
38
-
) : icon === 'growth' ? (
39
-
<Growth width={iconSize} fill={pal.colors.emptyStateIcon} />
40
-
) : (
41
-
<FontAwesomeIcon
42
-
icon={icon}
43
-
size={iconSize}
44
-
style={[{color: pal.colors.emptyStateIcon} as FontAwesomeIconStyle]}
45
-
/>
46
-
)}
84
+
{renderIcon()}
47
85
</View>
48
-
<Text type="xl" style={[{color: pal.colors.textLight}, styles.text]}>
86
+
<Text
87
+
style={[
88
+
{
89
+
color: pal.colors.textLight,
90
+
maxWidth: gtMobile ? '40%' : '60%',
91
+
},
92
+
a.pt_xs,
93
+
a.font_medium,
94
+
a.text_md,
95
+
a.leading_snug,
96
+
a.text_center,
97
+
a.self_center,
98
+
textStyle,
99
+
]}>
49
100
{message}
50
101
</Text>
102
+
{button && (
103
+
<View style={[a.flex_shrink, a.mt_xl, a.self_center]}>
104
+
<Button {...button}>
105
+
<ButtonText>{button.text}</ButtonText>
106
+
</Button>
107
+
</View>
108
+
)}
51
109
</View>
52
110
)
53
111
}
54
-
55
-
const styles = StyleSheet.create({
56
-
iconContainer: {
57
-
flexDirection: 'row',
58
-
alignItems: 'center',
59
-
justifyContent: 'center',
60
-
height: 80,
61
-
width: 80,
62
-
marginLeft: 'auto',
63
-
marginRight: 'auto',
64
-
borderRadius: 80,
65
-
marginTop: 30,
66
-
},
67
-
iconContainerBig: {
68
-
width: 100,
69
-
height: 100,
70
-
marginTop: 50,
71
-
},
72
-
text: {
73
-
textAlign: 'center',
74
-
paddingTop: 20,
75
-
},
76
-
})
+4
-3
src/view/com/util/UserAvatar.tsx
+4
-3
src/view/com/util/UserAvatar.tsx
···
26
26
import {type PickerImage} from '#/lib/media/picker.shared'
27
27
import {makeProfileLink} from '#/lib/routes/links'
28
28
import {sanitizeDisplayName} from '#/lib/strings/display-names'
29
+
import {isCancelledError} from '#/lib/strings/errors'
29
30
import {sanitizeHandle} from '#/lib/strings/handles'
30
31
import {logger} from '#/logger'
31
32
import {isAndroid, isNative, isWeb} from '#/platform/detection'
···
441
442
setRawImage(await createComposerImage(item))
442
443
editImageDialogControl.open()
443
444
}
444
-
} catch (e: any) {
445
+
} catch (e) {
445
446
// Don't log errors for cancelling selection to sentry on ios or android
446
-
if (!String(e).toLowerCase().includes('cancel')) {
447
-
logger.error('Failed to crop banner', {error: e})
447
+
if (!isCancelledError(e)) {
448
+
logger.error('Failed to crop avatar', {error: e})
448
449
}
449
450
}
450
451
}, [
+4
-2
src/view/com/util/UserBanner.tsx
+4
-2
src/view/com/util/UserBanner.tsx
···
12
12
import {compressIfNeeded} from '#/lib/media/manip'
13
13
import {openCamera, openCropper, openPicker} from '#/lib/media/picker'
14
14
import {type PickerImage} from '#/lib/media/picker.shared'
15
+
import {isCancelledError} from '#/lib/strings/errors'
15
16
import {logger} from '#/logger'
16
17
import {isAndroid, isNative} from '#/platform/detection'
17
18
import {
···
92
93
setRawImage(await createComposerImage(items[0]))
93
94
editImageDialogControl.open()
94
95
}
95
-
} catch (e: any) {
96
-
if (!String(e).includes('Canceled')) {
96
+
} catch (e) {
97
+
// Don't log errors for cancelling selection to sentry on ios or android
98
+
if (!isCancelledError(e)) {
97
99
logger.error('Failed to crop banner', {error: e})
98
100
}
99
101
}
+10
-1
src/view/screens/Debug.tsx
+10
-1
src/view/screens/Debug.tsx
···
20
20
import * as Toast from '#/view/com/util/Toast'
21
21
import {ViewHeader} from '#/view/com/util/ViewHeader'
22
22
import {ViewSelector} from '#/view/com/util/ViewSelector'
23
+
import {HashtagWide_Stroke1_Corner0_Rounded as HashtagWideIcon} from '#/components/icons/Hashtag'
23
24
import * as Layout from '#/components/Layout'
24
25
25
26
const MAIN_VIEWS = ['Base', 'Controls', 'Error', 'Notifs']
···
333
334
}
334
335
335
336
function EmptyStateView() {
336
-
return <EmptyState icon="bars" message="This is an empty state" />
337
+
const {_} = useLingui()
338
+
339
+
return (
340
+
<EmptyState
341
+
icon={HashtagWideIcon}
342
+
iconSize="2xl"
343
+
message={_(msg`This is an empty state`)}
344
+
/>
345
+
)
337
346
}
338
347
339
348
function LoadingPlaceholderView() {
+63
-2
src/view/screens/Profile.tsx
+63
-2
src/view/screens/Profile.tsx
···
7
7
type ModerationOpts,
8
8
RichText as RichTextAPI,
9
9
} from '@atproto/api'
10
-
import {msg} from '@lingui/macro'
10
+
import {msg, Trans} from '@lingui/macro'
11
11
import {useLingui} from '@lingui/react'
12
-
import {useFocusEffect} from '@react-navigation/native'
12
+
import {useFocusEffect, useNavigation} from '@react-navigation/native'
13
13
import {useQueryClient} from '@tanstack/react-query'
14
14
15
15
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
16
+
import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification'
16
17
import {useSetTitle} from '#/lib/hooks/useSetTitle'
17
18
import {ComposeIcon2} from '#/lib/icons'
18
19
import {
19
20
type CommonNavigatorParams,
20
21
type NativeStackScreenProps,
22
+
type NavigationProp,
21
23
} from '#/lib/routes/types'
22
24
import {combinedDisplayName} from '#/lib/strings/display-names'
23
25
import {cleanError} from '#/lib/strings/errors'
···
42
44
import {ProfileFeedSection} from '#/screens/Profile/Sections/Feed'
43
45
import {ProfileLabelsSection} from '#/screens/Profile/Sections/Labels'
44
46
import {atoms as a} from '#/alf'
47
+
import {Circle_And_Square_Stroke1_Corner0_Rounded_Filled as CircleAndSquareIcon} from '#/components/icons/CircleAndSquare'
48
+
import {Heart2_Stroke1_Corner0_Rounded as HeartIcon} from '#/components/icons/Heart2'
49
+
import {Image_Stroke1_Corner0_Rounded as ImageIcon} from '#/components/icons/Image'
50
+
import {Message_Stroke1_Corner0_Rounded_Filled as MessageIcon} from '#/components/icons/Message'
51
+
import {VideoClip_Stroke1_Corner0_Rounded as VideoIcon} from '#/components/icons/VideoClip'
45
52
import * as Layout from '#/components/Layout'
46
53
import {ScreenHider} from '#/components/moderation/ScreenHider'
47
54
import {ProfileStarterPacks} from '#/components/StarterPack/ProfileStarterPacks'
···
169
176
const {hasSession, currentAccount} = useSession()
170
177
const setMinimalShellMode = useSetMinimalShellMode()
171
178
const {openComposer} = useOpenComposer()
179
+
const navigation = useNavigation<NavigationProp>()
180
+
const requireEmailVerification = useRequireEmailVerification()
172
181
const {
173
182
data: labelerInfo,
174
183
error: labelerError,
···
334
343
scrollSectionToTop(index)
335
344
}
336
345
346
+
const navToWizard = useCallback(() => {
347
+
navigation.navigate('StarterPackWizard', {})
348
+
}, [navigation])
349
+
const wrappedNavToWizard = requireEmailVerification(navToWizard, {
350
+
instructions: [
351
+
<Trans key="nav">
352
+
Before creating a starter pack, you must first verify your email.
353
+
</Trans>,
354
+
],
355
+
})
356
+
337
357
// rendering
338
358
// =
339
359
···
408
428
scrollElRef={scrollElRef as ListRef}
409
429
ignoreFilterFor={profile.did}
410
430
setScrollViewTag={setScrollViewTag}
431
+
emptyStateMessage={_(msg`No posts yet`)}
432
+
emptyStateButton={{
433
+
label: _(msg`Write a post`),
434
+
text: _(msg`Write a post`),
435
+
onPress: () => openComposer({}),
436
+
size: 'small',
437
+
color: 'primary',
438
+
}}
411
439
/>
412
440
)
413
441
: null}
···
421
449
scrollElRef={scrollElRef as ListRef}
422
450
ignoreFilterFor={profile.did}
423
451
setScrollViewTag={setScrollViewTag}
452
+
emptyStateMessage={_(msg`No replies yet`)}
453
+
emptyStateIcon={MessageIcon}
424
454
/>
425
455
)
426
456
: null}
···
434
464
scrollElRef={scrollElRef as ListRef}
435
465
ignoreFilterFor={profile.did}
436
466
setScrollViewTag={setScrollViewTag}
467
+
emptyStateMessage={_(msg`No media yet`)}
468
+
emptyStateButton={{
469
+
label: _(msg`Post a photo`),
470
+
text: _(msg`Post a photo`),
471
+
onPress: () => openComposer({}),
472
+
size: 'small',
473
+
color: 'primary',
474
+
}}
475
+
emptyStateIcon={ImageIcon}
437
476
/>
438
477
)
439
478
: null}
···
447
486
scrollElRef={scrollElRef as ListRef}
448
487
ignoreFilterFor={profile.did}
449
488
setScrollViewTag={setScrollViewTag}
489
+
emptyStateMessage={_(msg`No video posts yet`)}
490
+
emptyStateButton={{
491
+
label: _(msg`Post a video`),
492
+
text: _(msg`Post a video`),
493
+
onPress: () => openComposer({}),
494
+
size: 'small',
495
+
color: 'primary',
496
+
}}
497
+
emptyStateIcon={VideoIcon}
450
498
/>
451
499
)
452
500
: null}
···
460
508
scrollElRef={scrollElRef as ListRef}
461
509
ignoreFilterFor={profile.did}
462
510
setScrollViewTag={setScrollViewTag}
511
+
emptyStateMessage={_(msg`No likes yet`)}
512
+
emptyStateIcon={HeartIcon}
463
513
/>
464
514
)
465
515
: null}
···
485
535
headerOffset={headerHeight}
486
536
enabled={isFocused}
487
537
setScrollViewTag={setScrollViewTag}
538
+
emptyStateMessage={_(
539
+
msg`Starter packs let you share your favorite feeds and people with your friends.`,
540
+
)}
541
+
emptyStateButton={{
542
+
label: _(msg`Create a Starter Pack`),
543
+
text: _(msg`Create a Starter Pack`),
544
+
onPress: wrappedNavToWizard,
545
+
color: 'primary',
546
+
size: 'small',
547
+
}}
548
+
emptyStateIcon={CircleAndSquareIcon}
488
549
/>
489
550
)
490
551
: null}