···44import {msg, Trans} from '@lingui/macro'
55import {useLingui} from '@lingui/react'
6677+import {popularInterests, useInterestsDisplayNames} from '#/lib/interests'
78import {logEvent} from '#/lib/statsig/statsig'
89import {isWeb} from '#/platform/detection'
910import {useModerationOpts} from '#/state/preferences/moderation-opts'
···1314import {useSession} from '#/state/session'
1415import {type Follow10ProgressGuide} from '#/state/shell/progress-guide'
1516import {type ListMethods} from '#/view/com/util/List'
1616-import {
1717- popularInterests,
1818- useInterestsDisplayNames,
1919-} from '#/screens/Onboarding/state'
2017import {
2118 atoms as a,
2219 native,
···11import React from 'react'
22import {type TextStyle, View, type ViewStyle} from 'react-native'
3344+import {type Interest, useInterestsDisplayNames} from '#/lib/interests'
45import {capitalize} from '#/lib/strings/capitalize'
55-import {useInterestsDisplayNames} from '#/screens/Onboarding/state'
66import {atoms as a, native, useTheme} from '#/alf'
77import * as Toggle from '#/components/forms/Toggle'
88import {Text} from '#/components/Typography'
991010-export function InterestButton({interest}: {interest: string}) {
1010+export function InterestButton({interest}: {interest: Interest}) {
1111 const t = useTheme()
1212 const interestsDisplayNames = useInterestsDisplayNames()
1313 const ctx = Toggle.useItemContext()
···1010import {useQueryClient} from '@tanstack/react-query'
1111import * as bcp47Match from 'bcp-47-match'
12121313+import {popularInterests, useInterestsDisplayNames} from '#/lib/interests'
1314import {cleanError} from '#/lib/strings/errors'
1415import {sanitizeHandle} from '#/lib/strings/handles'
1516import {logger} from '#/logger'
···4142import {List} from '#/view/com/util/List'
4243import {FeedFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
4344import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn'
4444-import {
4545- popularInterests,
4646- useInterestsDisplayNames,
4747-} from '#/screens/Onboarding/state'
4845import {
4946 StarterPackCard,
5047 StarterPackCardSkeleton,
···932929 <View style={[a.absolute, a.inset_0, t.atoms.bg, {top: -2}]} />
933930 <ModuleHeader.FeedLink feed={item.feed}>
934931 <ModuleHeader.FeedAvatar feed={item.feed} />
935935- <View style={[a.flex_1, a.gap_xs]}>
932932+ <View style={[a.flex_1, a.gap_2xs]}>
936933 <ModuleHeader.TitleText style={[a.text_lg]}>
937934 {item.feed.displayName}
938935 </ModuleHeader.TitleText>
···10821079 windowSize={platform({android: 11})}
10831080 /**
10841081 * Default: 10
10821082+ *
10831083+ * NOTE: This was 1 on Android. Unfortunately this leads to the list totally freaking out
10841084+ * when the sticky headers changed. I made a minimal reproduction and yeah, it's this prop.
10851085+ * Totally fine when the sticky headers are static, but when they're dynamic, it's a mess.
10861086+ *
10871087+ * Repro: https://github.com/mozzius/stickyindices-repro
10881088+ *
10891089+ * I then found doubling this prop on iOS also reduced it freaking out there as well.
10901090+ *
10911091+ * Trades off seeing more blank space due to it having to render more items before it can show anything.
10921092+ * -sfn
10851093 */
10861086- maxToRenderPerBatch={platform({android: 1})}
10941094+ maxToRenderPerBatch={platform({android: 10, ios: 20})}
10871095 /**
10881096 * Default: 50
10971097+ *
10981098+ * NOTE: This was 25 on Android. However, due to maxToRenderPerBatch being set to 10,
10991099+ * the lower batching period is no longer necessary (?)
10891100 */
10901090- updateCellsBatchingPeriod={platform({android: 25})}
11011101+ updateCellsBatchingPeriod={50}
10911102 refreshing={isPTR}
10921103 onRefresh={onPTR}
10931104 />
···33import {msg, Trans} from '@lingui/macro'
44import {useLingui} from '@lingui/react'
5566+import {useInterestsDisplayNames} from '#/lib/interests'
67import {Nux, useSaveNux} from '#/state/queries/nuxs'
78import {usePreferencesQuery} from '#/state/queries/preferences'
88-import {useInterestsDisplayNames} from '#/screens/Onboarding/state'
99import {atoms as a, useTheme} from '#/alf'
1010import {Button, ButtonIcon, ButtonText} from '#/components/Button'
1111import {Shapes_Stroke2_Corner0_Rounded as Shapes} from '#/components/icons/Shapes'
···55import {useLingui} from '@lingui/react'
66import {type InfiniteData} from '@tanstack/react-query'
7788+import {popularInterests, useInterestsDisplayNames} from '#/lib/interests'
89import {logger} from '#/logger'
910import {usePreferencesQuery} from '#/state/queries/preferences'
1011import {BlockDrawerGesture} from '#/view/shell/BlockDrawerGesture'
1111-import {
1212- popularInterests,
1313- useInterestsDisplayNames,
1414-} from '#/screens/Onboarding/state'
1512import {useTheme} from '#/alf'
1613import {atoms as a} from '#/alf'
1714import {boostInterests, InterestTabs} from '#/components/InterestTabs'
+1-1
src/screens/Search/util/useSuggestedUsers.ts
···11import {useMemo} from 'react'
2233+import {useInterestsDisplayNames} from '#/lib/interests'
34import {useActorSearchPaginated} from '#/state/queries/actor-search'
45import {useGetSuggestedUsersQuery} from '#/state/queries/trending/useGetSuggestedUsersQuery'
55-import {useInterestsDisplayNames} from '#/screens/Onboarding/state'
6677/**
88 * Conditional hook, used in case a user is a non-english speaker, in which
+7-29
src/screens/Settings/InterestsSettings.tsx
···66import {useQueryClient} from '@tanstack/react-query'
77import debounce from 'lodash.debounce'
8899+import {
1010+ type Interest,
1111+ interests as allInterests,
1212+ useInterestsDisplayNames,
1313+} from '#/lib/interests'
914import {type CommonNavigatorParams} from '#/lib/routes/types'
1015import {
1116 preferencesQueryKey,
···1722import {createSuggestedStarterPacksQueryKey} from '#/state/queries/useSuggestedStarterPacksQuery'
1823import {useAgent} from '#/state/session'
1924import * as Toast from '#/view/com/util/Toast'
2020-import {useInterestsDisplayNames} from '#/screens/Onboarding/state'
2125import {atoms as a, useGutters, useTheme} from '#/alf'
2226import {Admonition} from '#/components/Admonition'
2327import {Divider} from '#/components/Divider'
···160164 onChange={onChangeInterests}
161165 label={_(msg`Select your interests from the options below`)}>
162166 <View style={[a.flex_row, a.flex_wrap, a.gap_sm]}>
163163- {INTERESTS.map(interest => {
167167+ {allInterests.map(interest => {
164168 const name = interestsDisplayNames[interest]
165169 if (!name) return null
166170 return (
···178182 )
179183}
180184181181-export function InterestButton({interest}: {interest: string}) {
185185+export function InterestButton({interest}: {interest: Interest}) {
182186 const t = useTheme()
183187 const interestsDisplayNames = useInterestsDisplayNames()
184188 const ctx = Toggle.useItemContext()
···230234 </View>
231235 )
232236}
233233-234234-const INTERESTS = [
235235- 'animals',
236236- 'art',
237237- 'books',
238238- 'comedy',
239239- 'comics',
240240- 'culture',
241241- 'dev',
242242- 'education',
243243- 'food',
244244- 'gaming',
245245- 'journalism',
246246- 'movies',
247247- 'music',
248248- 'nature',
249249- 'news',
250250- 'pets',
251251- 'photography',
252252- 'politics',
253253- 'science',
254254- 'sports',
255255- 'tech',
256256- 'tv',
257257- 'writers',
258258-]
-34
src/screens/Signup/StepInfo/Policies.tsx
···44import {msg, Trans} from '@lingui/macro'
55import {useLingui} from '@lingui/react'
6677-import {webLinks} from '#/lib/constants'
88-import {useGate} from '#/lib/statsig/statsig'
97import {atoms as a, useTheme} from '#/alf'
108import {Admonition} from '#/components/Admonition'
119import {InlineLinkText} from '#/components/Link'
1210import {Text} from '#/components/Typography'
13111414-function CommunityGuidelinesNotice({}: {}) {
1515- const {_} = useLingui()
1616- const gate = useGate()
1717-1818- if (gate('disable_onboarding_policy_update_notice')) return null
1919-2020- return (
2121- <View style={[a.pt_xs]}>
2222- <Admonition type="tip">
2323- <Trans>
2424- You also agree to{' '}
2525- <InlineLinkText
2626- label={_(msg`Bluesky's Community Guidelines`)}
2727- to={webLinks.communityDeprecated}>
2828- Bluesky’s Community Guidelines
2929- </InlineLinkText>
3030- . An{' '}
3131- <InlineLinkText
3232- label={_(msg`Bluesky's Updated Community Guidelines`)}
3333- to={webLinks.community}>
3434- updated version of our Community Guidelines
3535- </InlineLinkText>{' '}
3636- will take effect on October 15th.
3737- </Trans>
3838- </Admonition>
3939- </View>
4040- )
4141-}
4242-4312export const Policies = ({
4413 serviceDescription,
4514 needsGuardian,
···6736 This service has not provided terms of service or a privacy policy.
6837 </Trans>
6938 </Admonition>
7070- <CommunityGuidelinesNotice />
7139 </View>
7240 )
7341 }
···145113 </Trans>
146114 </Admonition>
147115 ) : undefined}
148148-149149- <CommunityGuidelinesNotice />
150116 </View>
151117 )
152118}
···135135 } else if (AppBskyUnspeccedDefs.isThreadItemPost(item.value)) {
136136 if (parentMetadata) {
137137 /*
138138- * Set this value before incrementing the parent's repliesSeenCounter
138138+ * Set this value before incrementing the `repliesSeenCounter` later
139139+ * on, since `repliesSeenCounter` is 1-indexed and `replyIndex` is
140140+ * 0-indexed.
139141 */
140140- metadata!.replyIndex = parentMetadata.repliesIndexCounter
141141- // Increment the parent's repliesIndexCounter
142142- parentMetadata.repliesIndexCounter += 1
142142+ metadata!.replyIndex = parentMetadata.repliesSeenCounter
143143 }
144144145145 const post = views.threadPost({
···193193 storeTraversalMetadata(metadatas, childMetadata)
194194 if (childParentMetadata) {
195195 /*
196196- * Set this value before incrementing the parent's repliesIndexCounter
196196+ * Set this value before incrementing the
197197+ * `repliesSeenCounter` later on, since `repliesSeenCounter`
198198+ * is 1-indexed and `replyIndex` is 0-indexed.
197199 */
198200 childMetadata!.replyIndex =
199199- childParentMetadata.repliesIndexCounter
200200- childParentMetadata.repliesIndexCounter += 1
201201+ childParentMetadata.repliesSeenCounter
201202 }
202203203204 const childPost = views.threadPost({
···264265 if (nextItem?.type === 'threadPost')
265266 metadata.nextItemDepth = nextItem?.depth
266267267267- /*
268268- * Item is the last "sibling" if we know for sure we're out of
269269- * replies on the parent (even though this item itself may have its
270270- * own reply branches).
268268+ /**
269269+ * Item is also the last "sibling" if its index matches the total
270270+ * number of replies we're actually able to render to the page.
271271 */
272272- const isLastSiblingByCounts =
272272+ const isLastSiblingDueToMissingReplies =
273273 metadata.replyIndex ===
274274- metadata.parentMetadata.repliesIndexCounter - 1
274274+ metadata.parentMetadata.repliesSeenCounter - 1
275275276276 /*
277277 * Item can also be the last "sibling" if we know we don't have a
···287287 * Ok now we can set the last sibling state.
288288 */
289289 metadata.isLastSibling =
290290- isLastSiblingByCounts || isImplicitlyLastSibling
290290+ isImplicitlyLastSibling || isLastSiblingDueToMissingReplies
291291292292 /*
293293 * Item is the last "child" in a branch if there is no next item,
+14-13
src/state/queries/usePostThread/types.ts
···193193 */
194194 repliesUnhydrated: number
195195 /**
196196- * The number of replies that have been seen so far in the traversal.
197197- * Excludes replies that are moderated in some way, since those are not
198198- * "seen" on first load. Use `repliesIndexCounter` for the total number of
199199- * replies that were hydrated in the response.
196196+ * The number of replies that have been "seen" (actually able to be rendered)
197197+ * so far in the traversal. Excludes replies that are moderated in some way,
198198+ * since those are not "seen" on first load.
200199 *
201201- * After traversal, we can use this to calculate if we actually got all the
202202- * replies we expected, or if some were blocked, etc.
200200+ * We use this to compute the `replyIndex` values of the children of this
201201+ * parent. E.g. if a reply is not hydrated on the response, or is moderated
202202+ * in some way (including by the user), this value is not incremented. So
203203+ * this represents the _actual_ index of the reply in the rendered view.
204204+ *
205205+ * Note: this is a "counter", not an "index". Because this value is
206206+ * incremented starting from 0, it is 1-indexed. So to when comparing to the
207207+ * `replyIndex`, you'll need to subtract 1 from this value.
203208 */
204209 repliesSeenCounter: number
205210 /**
206206- * The total number of replies to this post hydrated in this response. Used
207207- * for populating the `replyIndex` of the post by referencing this value on
208208- * the parent.
209209- */
210210- repliesIndexCounter: number
211211- /**
212212- * The index-0-based index of this reply in the parent post's replies.
211211+ * The index-0-based index of this reply in the parent post's replies. This
212212+ * is computed from the `repliesSeenCounter` of the parent post, prior to it
213213+ * being incremented for this reply.
213214 */
214215 replyIndex: number
215216 /**