Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Move home feed and thread preferences to server (#1507)

* Move home feed and thread preferences to server

* Fix thread usage of prefs

* Remove log

* Bump @atproto/api@0.6.16

* Improve type usage

authored by

Paul Frazee and committed by
GitHub
8584009b 28b692a1

+205 -162
+1 -1
package.json
··· 25 25 "build:apk": "eas build -p android --profile dev-android-apk" 26 26 }, 27 27 "dependencies": { 28 - "@atproto/api": "^0.6.14", 28 + "@atproto/api": "^0.6.16", 29 29 "@bam.tech/react-native-image-resizer": "^3.0.4", 30 30 "@braintree/sanitize-url": "^6.0.2", 31 31 "@emoji-mart/react": "^1.1.1",
+1 -1
src/lib/api/feed/merge.ts
··· 109 109 } 110 110 111 111 _captureFeedsIfNeeded() { 112 - if (!this.rootStore.preferences.homeFeedMergeFeedEnabled) { 112 + if (!this.rootStore.preferences.homeFeed.lab_mergeFeedEnabled) { 113 113 return 114 114 } 115 115 if (this.customFeeds.length === 0) {
+8 -12
src/state/models/content/post-thread.ts
··· 8 8 import {RootStoreModel} from '../root-store' 9 9 import * as apilib from 'lib/api/index' 10 10 import {cleanError} from 'lib/strings/errors' 11 + import {ThreadViewPreference} from '../ui/preferences' 11 12 import {PostThreadItemModel} from './post-thread-item' 12 13 13 14 export class PostThreadModel { ··· 241 242 res.data.thread as AppBskyFeedDefs.ThreadViewPost, 242 243 thread.uri, 243 244 ) 244 - sortThread(thread, this.rootStore.preferences) 245 + sortThread(thread, this.rootStore.preferences.thread) 245 246 this.thread = thread 246 247 } 247 248 } ··· 263 264 } 264 265 } 265 266 266 - interface SortSettings { 267 - threadDefaultSort: string 268 - threadFollowedUsersFirst: boolean 269 - } 270 - 271 267 type MaybeThreadItem = 272 268 | PostThreadItemModel 273 269 | AppBskyFeedDefs.NotFoundPost 274 270 | AppBskyFeedDefs.BlockedPost 275 - function sortThread(item: MaybeThreadItem, opts: SortSettings) { 271 + function sortThread(item: MaybeThreadItem, opts: ThreadViewPreference) { 276 272 if ('notFound' in item) { 277 273 return 278 274 } ··· 301 297 if (modScore(a.moderation) !== modScore(b.moderation)) { 302 298 return modScore(a.moderation) - modScore(b.moderation) 303 299 } 304 - if (opts.threadFollowedUsersFirst) { 300 + if (opts.prioritizeFollowedUsers) { 305 301 const af = a.post.author.viewer?.following 306 302 const bf = b.post.author.viewer?.following 307 303 if (af && !bf) { ··· 310 306 return 1 311 307 } 312 308 } 313 - if (opts.threadDefaultSort === 'oldest') { 309 + if (opts.sort === 'oldest') { 314 310 return a.post.indexedAt.localeCompare(b.post.indexedAt) 315 - } else if (opts.threadDefaultSort === 'newest') { 311 + } else if (opts.sort === 'newest') { 316 312 return b.post.indexedAt.localeCompare(a.post.indexedAt) 317 - } else if (opts.threadDefaultSort === 'most-likes') { 313 + } else if (opts.sort === 'most-likes') { 318 314 if (a.post.likeCount === b.post.likeCount) { 319 315 return b.post.indexedAt.localeCompare(a.post.indexedAt) // newest 320 316 } else { 321 317 return (b.post.likeCount || 0) - (a.post.likeCount || 0) // most likes 322 318 } 323 - } else if (opts.threadDefaultSort === 'random') { 319 + } else if (opts.sort === 'random') { 324 320 return 0.5 - Math.random() // this is vaguely criminal but we can get away with it 325 321 } 326 322 return b.post.indexedAt.localeCompare(a.post.indexedAt)
+138 -120
src/state/models/ui/preferences.ts
··· 1 1 import {makeAutoObservable, runInAction} from 'mobx' 2 - import {LabelPreference as APILabelPreference} from '@atproto/api' 2 + import { 3 + LabelPreference as APILabelPreference, 4 + BskyFeedViewPreference, 5 + BskyThreadViewPreference, 6 + } from '@atproto/api' 3 7 import AwaitLock from 'await-lock' 4 8 import isEqual from 'lodash.isequal' 5 9 import {isObj, hasProp} from 'lib/type-guards' ··· 13 17 14 18 // TEMP we need to permanently convert 'show' to 'ignore', for now we manually convert -prf 15 19 export type LabelPreference = APILabelPreference | 'show' 20 + export type FeedViewPreference = BskyFeedViewPreference & { 21 + lab_mergeFeedEnabled?: boolean | undefined 22 + } 23 + export type ThreadViewPreference = BskyThreadViewPreference & { 24 + lab_treeViewEnabled?: boolean | undefined 25 + } 16 26 const LABEL_GROUPS = [ 17 27 'nsfw', 18 28 'nudity', ··· 28 38 .slice(0, 6) 29 39 const THREAD_SORT_VALUES = ['oldest', 'newest', 'most-likes', 'random'] 30 40 41 + interface LegacyPreferences { 42 + hideReplies?: boolean 43 + hideRepliesByLikeCount?: number 44 + hideReposts?: boolean 45 + hideQuotePosts?: boolean 46 + } 47 + 31 48 export class LabelPreferencesModel { 32 49 nsfw: LabelPreference = 'hide' 33 50 nudity: LabelPreference = 'warn' ··· 52 69 savedFeeds: string[] = [] 53 70 pinnedFeeds: string[] = [] 54 71 birthDate: Date | undefined = undefined 55 - homeFeedRepliesEnabled: boolean = true 56 - homeFeedRepliesByFollowedOnlyEnabled: boolean = true 57 - homeFeedRepliesThreshold: number = 0 58 - homeFeedRepostsEnabled: boolean = true 59 - homeFeedQuotePostsEnabled: boolean = true 60 - homeFeedMergeFeedEnabled: boolean = false 61 - threadDefaultSort: string = 'oldest' 62 - threadFollowedUsersFirst: boolean = true 63 - threadTreeViewEnabled: boolean = false 72 + homeFeed: FeedViewPreference = { 73 + hideReplies: false, 74 + hideRepliesByUnfollowed: false, 75 + hideRepliesByLikeCount: 0, 76 + hideReposts: false, 77 + hideQuotePosts: false, 78 + lab_mergeFeedEnabled: false, // experimental 79 + } 80 + thread: ThreadViewPreference = { 81 + sort: 'oldest', 82 + prioritizeFollowedUsers: true, 83 + lab_treeViewEnabled: false, // experimental 84 + } 64 85 requireAltTextEnabled: boolean = false 86 + 87 + // used to help with transitions from device-stored to server-stored preferences 88 + legacyPreferences: LegacyPreferences | undefined 65 89 66 90 // used to linearize async modifications to state 67 91 lock = new AwaitLock() ··· 86 110 contentLabels: this.contentLabels, 87 111 savedFeeds: this.savedFeeds, 88 112 pinnedFeeds: this.pinnedFeeds, 89 - homeFeedRepliesEnabled: this.homeFeedRepliesEnabled, 90 - homeFeedRepliesByFollowedOnlyEnabled: 91 - this.homeFeedRepliesByFollowedOnlyEnabled, 92 - homeFeedRepliesThreshold: this.homeFeedRepliesThreshold, 93 - homeFeedRepostsEnabled: this.homeFeedRepostsEnabled, 94 - homeFeedQuotePostsEnabled: this.homeFeedQuotePostsEnabled, 95 - homeFeedMergeFeedEnabled: this.homeFeedMergeFeedEnabled, 96 - threadDefaultSort: this.threadDefaultSort, 97 - threadFollowedUsersFirst: this.threadFollowedUsersFirst, 98 - threadTreeViewEnabled: this.threadTreeViewEnabled, 99 113 requireAltTextEnabled: this.requireAltTextEnabled, 100 114 } 101 115 } ··· 165 179 ) { 166 180 this.pinnedFeeds = v.pinnedFeeds 167 181 } 168 - // check if home feed replies are enabled in preferences, then hydrate 169 - if ( 170 - hasProp(v, 'homeFeedRepliesEnabled') && 171 - typeof v.homeFeedRepliesEnabled === 'boolean' 172 - ) { 173 - this.homeFeedRepliesEnabled = v.homeFeedRepliesEnabled 174 - } 175 - // check if home feed replies "followed only" are enabled in preferences, then hydrate 176 - if ( 177 - hasProp(v, 'homeFeedRepliesByFollowedOnlyEnabled') && 178 - typeof v.homeFeedRepliesByFollowedOnlyEnabled === 'boolean' 179 - ) { 180 - this.homeFeedRepliesByFollowedOnlyEnabled = 181 - v.homeFeedRepliesByFollowedOnlyEnabled 182 - } 183 - // check if home feed replies threshold is enabled in preferences, then hydrate 184 - if ( 185 - hasProp(v, 'homeFeedRepliesThreshold') && 186 - typeof v.homeFeedRepliesThreshold === 'number' 187 - ) { 188 - this.homeFeedRepliesThreshold = v.homeFeedRepliesThreshold 189 - } 190 - // check if home feed reposts are enabled in preferences, then hydrate 191 - if ( 192 - hasProp(v, 'homeFeedRepostsEnabled') && 193 - typeof v.homeFeedRepostsEnabled === 'boolean' 194 - ) { 195 - this.homeFeedRepostsEnabled = v.homeFeedRepostsEnabled 196 - } 197 - // check if home feed quote posts are enabled in preferences, then hydrate 198 - if ( 199 - hasProp(v, 'homeFeedQuotePostsEnabled') && 200 - typeof v.homeFeedQuotePostsEnabled === 'boolean' 201 - ) { 202 - this.homeFeedQuotePostsEnabled = v.homeFeedQuotePostsEnabled 203 - } 204 - // check if home feed mergefeed is enabled in preferences, then hydrate 205 - if ( 206 - hasProp(v, 'homeFeedMergeFeedEnabled') && 207 - typeof v.homeFeedMergeFeedEnabled === 'boolean' 208 - ) { 209 - this.homeFeedMergeFeedEnabled = v.homeFeedMergeFeedEnabled 210 - } 211 - // check if thread sort order is set in preferences, then hydrate 212 - if ( 213 - hasProp(v, 'threadDefaultSort') && 214 - typeof v.threadDefaultSort === 'string' && 215 - THREAD_SORT_VALUES.includes(v.threadDefaultSort) 216 - ) { 217 - this.threadDefaultSort = v.threadDefaultSort 218 - } 219 - // check if thread followed-users-first is enabled in preferences, then hydrate 220 - if ( 221 - hasProp(v, 'threadFollowedUsersFirst') && 222 - typeof v.threadFollowedUsersFirst === 'boolean' 223 - ) { 224 - this.threadFollowedUsersFirst = v.threadFollowedUsersFirst 225 - } 226 - // check if thread treeview is enabled in preferences, then hydrate 227 - if ( 228 - hasProp(v, 'threadTreeViewEnabled') && 229 - typeof v.threadTreeViewEnabled === 'boolean' 230 - ) { 231 - this.threadTreeViewEnabled = v.threadTreeViewEnabled 232 - } 233 182 // check if requiring alt text is enabled in preferences, then hydrate 234 183 if ( 235 184 hasProp(v, 'requireAltTextEnabled') && ··· 237 186 ) { 238 187 this.requireAltTextEnabled = v.requireAltTextEnabled 239 188 } 189 + // grab legacy values 190 + this.legacyPreferences = getLegacyPreferences(v) 240 191 } 241 192 } 242 193 ··· 250 201 const prefs = await this.rootStore.agent.getPreferences() 251 202 252 203 runInAction(() => { 204 + if (prefs.feedViewPrefs.home) { 205 + this.homeFeed = prefs.feedViewPrefs.home 206 + } 207 + this.thread = prefs.threadViewPrefs 253 208 this.adultContentEnabled = prefs.adultContentEnabled 254 209 for (const label in prefs.contentLabels) { 255 210 if ( ··· 272 227 this.birthDate = prefs.birthDate 273 228 }) 274 229 230 + // sync legacy values if needed 231 + await this.syncLegacyPreferences() 232 + 275 233 // set defaults on missing items 276 234 if (typeof prefs.feeds.saved === 'undefined') { 277 235 try { ··· 298 256 await this.rootStore.me.savedFeeds.updateCache(clearCache) 299 257 } 300 258 259 + async syncLegacyPreferences() { 260 + if (this.legacyPreferences) { 261 + this.homeFeed = {...this.homeFeed, ...this.legacyPreferences} 262 + this.legacyPreferences = undefined 263 + await this.rootStore.agent.setFeedViewPrefs('home', this.homeFeed) 264 + } 265 + } 266 + 301 267 /** 302 268 * This function resets the preferences to an empty array of no preferences. 303 269 */ ··· 510 476 await this.rootStore.agent.setPersonalDetails({birthDate}) 511 477 } 512 478 513 - toggleHomeFeedRepliesEnabled() { 514 - this.homeFeedRepliesEnabled = !this.homeFeedRepliesEnabled 479 + async toggleHomeFeedHideReplies() { 480 + this.homeFeed.hideReplies = !this.homeFeed.hideReplies 481 + await this.rootStore.agent.setFeedViewPrefs('home', { 482 + hideReplies: this.homeFeed.hideReplies, 483 + }) 515 484 } 516 485 517 - toggleHomeFeedRepliesByFollowedOnlyEnabled() { 518 - this.homeFeedRepliesByFollowedOnlyEnabled = 519 - !this.homeFeedRepliesByFollowedOnlyEnabled 486 + async toggleHomeFeedHideRepliesByUnfollowed() { 487 + this.homeFeed.hideRepliesByUnfollowed = 488 + !this.homeFeed.hideRepliesByUnfollowed 489 + await this.rootStore.agent.setFeedViewPrefs('home', { 490 + hideRepliesByUnfollowed: this.homeFeed.hideRepliesByUnfollowed, 491 + }) 520 492 } 521 493 522 - setHomeFeedRepliesThreshold(threshold: number) { 523 - this.homeFeedRepliesThreshold = threshold 494 + async setHomeFeedHideRepliesByLikeCount(threshold: number) { 495 + this.homeFeed.hideRepliesByLikeCount = threshold 496 + await this.rootStore.agent.setFeedViewPrefs('home', { 497 + hideRepliesByLikeCount: this.homeFeed.hideRepliesByLikeCount, 498 + }) 524 499 } 525 500 526 - toggleHomeFeedRepostsEnabled() { 527 - this.homeFeedRepostsEnabled = !this.homeFeedRepostsEnabled 501 + async toggleHomeFeedHideReposts() { 502 + this.homeFeed.hideReposts = !this.homeFeed.hideReposts 503 + await this.rootStore.agent.setFeedViewPrefs('home', { 504 + hideReposts: this.homeFeed.hideReposts, 505 + }) 528 506 } 529 507 530 - toggleHomeFeedQuotePostsEnabled() { 531 - this.homeFeedQuotePostsEnabled = !this.homeFeedQuotePostsEnabled 508 + async toggleHomeFeedHideQuotePosts() { 509 + this.homeFeed.hideQuotePosts = !this.homeFeed.hideQuotePosts 510 + await this.rootStore.agent.setFeedViewPrefs('home', { 511 + hideQuotePosts: this.homeFeed.hideQuotePosts, 512 + }) 532 513 } 533 514 534 - toggleHomeFeedMergeFeedEnabled() { 535 - this.homeFeedMergeFeedEnabled = !this.homeFeedMergeFeedEnabled 515 + async toggleHomeFeedMergeFeedEnabled() { 516 + this.homeFeed.lab_mergeFeedEnabled = !this.homeFeed.lab_mergeFeedEnabled 517 + await this.rootStore.agent.setFeedViewPrefs('home', { 518 + lab_mergeFeedEnabled: this.homeFeed.lab_mergeFeedEnabled, 519 + }) 536 520 } 537 521 538 - setThreadDefaultSort(v: string) { 522 + async setThreadSort(v: string) { 539 523 if (THREAD_SORT_VALUES.includes(v)) { 540 - this.threadDefaultSort = v 524 + this.thread.sort = v 525 + await this.rootStore.agent.setThreadViewPrefs({sort: v}) 541 526 } 542 527 } 543 528 544 - toggleThreadFollowedUsersFirst() { 545 - this.threadFollowedUsersFirst = !this.threadFollowedUsersFirst 529 + async togglePrioritizedFollowedUsers() { 530 + this.thread.prioritizeFollowedUsers = !this.thread.prioritizeFollowedUsers 531 + await this.rootStore.agent.setThreadViewPrefs({ 532 + prioritizeFollowedUsers: this.thread.prioritizeFollowedUsers, 533 + }) 546 534 } 547 535 548 - toggleThreadTreeViewEnabled() { 549 - this.threadTreeViewEnabled = !this.threadTreeViewEnabled 536 + async toggleThreadTreeViewEnabled() { 537 + this.thread.lab_treeViewEnabled = !this.thread.lab_treeViewEnabled 538 + await this.rootStore.agent.setThreadViewPrefs({ 539 + lab_treeViewEnabled: this.thread.lab_treeViewEnabled, 540 + }) 550 541 } 551 542 552 543 toggleRequireAltTextEnabled() { ··· 560 551 getFeedTuners( 561 552 feedType: 'home' | 'following' | 'author' | 'custom' | 'likes', 562 553 ) { 563 - const areRepliesEnabled = this.homeFeedRepliesEnabled 564 - const areRepliesByFollowedOnlyEnabled = 565 - this.homeFeedRepliesByFollowedOnlyEnabled 566 - const repliesThreshold = this.homeFeedRepliesThreshold 567 - const areRepostsEnabled = this.homeFeedRepostsEnabled 568 - const areQuotePostsEnabled = this.homeFeedQuotePostsEnabled 569 - 570 554 if (feedType === 'custom') { 571 555 return [ 572 556 FeedTuner.dedupReposts, ··· 576 560 if (feedType === 'home' || feedType === 'following') { 577 561 const feedTuners = [] 578 562 579 - if (areRepostsEnabled) { 563 + if (this.homeFeed.hideReposts) { 564 + feedTuners.push(FeedTuner.removeReposts) 565 + } else { 580 566 feedTuners.push(FeedTuner.dedupReposts) 581 - } else { 582 - feedTuners.push(FeedTuner.removeReposts) 583 567 } 584 568 585 - if (areRepliesEnabled) { 569 + if (this.homeFeed.hideReplies) { 570 + feedTuners.push(FeedTuner.removeReplies) 571 + } else { 586 572 feedTuners.push( 587 573 FeedTuner.thresholdRepliesOnly({ 588 574 userDid: this.rootStore.session.data?.did || '', 589 - minLikes: repliesThreshold, 590 - followedOnly: areRepliesByFollowedOnlyEnabled, 575 + minLikes: this.homeFeed.hideRepliesByLikeCount, 576 + followedOnly: !!this.homeFeed.hideRepliesByUnfollowed, 591 577 }), 592 578 ) 593 - } else { 594 - feedTuners.push(FeedTuner.removeReplies) 595 579 } 596 580 597 - if (!areQuotePostsEnabled) { 581 + if (this.homeFeed.hideQuotePosts) { 598 582 feedTuners.push(FeedTuner.removeQuotePosts) 599 583 } 600 584 ··· 611 595 } 612 596 return pref 613 597 } 598 + 599 + function getLegacyPreferences( 600 + v: Record<string, unknown>, 601 + ): LegacyPreferences | undefined { 602 + const legacyPreferences: LegacyPreferences = {} 603 + if ( 604 + hasProp(v, 'homeFeedRepliesEnabled') && 605 + typeof v.homeFeedRepliesEnabled === 'boolean' 606 + ) { 607 + legacyPreferences.hideReplies = !v.homeFeedRepliesEnabled 608 + } 609 + if ( 610 + hasProp(v, 'homeFeedRepliesThreshold') && 611 + typeof v.homeFeedRepliesThreshold === 'number' 612 + ) { 613 + legacyPreferences.hideRepliesByLikeCount = v.homeFeedRepliesThreshold 614 + } 615 + if ( 616 + hasProp(v, 'homeFeedRepostsEnabled') && 617 + typeof v.homeFeedRepostsEnabled === 'boolean' 618 + ) { 619 + legacyPreferences.hideReposts = !v.homeFeedRepostsEnabled 620 + } 621 + if ( 622 + hasProp(v, 'homeFeedQuotePostsEnabled') && 623 + typeof v.homeFeedQuotePostsEnabled === 'boolean' 624 + ) { 625 + legacyPreferences.hideQuotePosts = !v.homeFeedQuotePostsEnabled 626 + } 627 + if (Object.keys(legacyPreferences).length) { 628 + return legacyPreferences 629 + } 630 + return undefined 631 + }
+1 -1
src/view/screens/PostThread.tsx
··· 76 76 uri={uri} 77 77 view={view} 78 78 onPressReply={onPressReply} 79 - treeView={store.preferences.threadTreeViewEnabled} 79 + treeView={!!store.preferences.thread.lab_treeViewEnabled} 80 80 /> 81 81 </View> 82 82 {isMobile && !store.shell.minimalShellMode && (
+32 -20
src/view/screens/PreferencesHomeFeed.tsx
··· 13 13 import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' 14 14 import {ViewHeader} from 'view/com/util/ViewHeader' 15 15 import {CenteredView} from 'view/com/util/Views' 16 + import debounce from 'lodash.debounce' 16 17 17 18 function RepliesThresholdInput({enabled}: {enabled: boolean}) { 18 19 const store = useStores() 19 20 const pal = usePalette('default') 20 - const [value, setValue] = useState(store.preferences.homeFeedRepliesThreshold) 21 + const [value, setValue] = useState( 22 + store.preferences.homeFeed.hideRepliesByLikeCount, 23 + ) 24 + const save = React.useMemo( 25 + () => 26 + debounce( 27 + threshold => 28 + store.preferences.setHomeFeedHideRepliesByLikeCount(threshold), 29 + 500, 30 + ), // debouce for 500ms 31 + [store], 32 + ) 21 33 22 34 return ( 23 35 <View style={[!enabled && styles.dimmed]}> ··· 26 38 onValueChange={(v: number | number[]) => { 27 39 const threshold = Math.floor(Array.isArray(v) ? v[0] : v) 28 40 setValue(threshold) 29 - store.preferences.setHomeFeedRepliesThreshold(threshold) 41 + save(threshold) 30 42 }} 31 43 minimumValue={0} 32 44 maximumValue={25} ··· 88 100 <ToggleButton 89 101 testID="toggleRepliesBtn" 90 102 type="default-light" 91 - label={store.preferences.homeFeedRepliesEnabled ? 'Yes' : 'No'} 92 - isSelected={store.preferences.homeFeedRepliesEnabled} 93 - onPress={store.preferences.toggleHomeFeedRepliesEnabled} 103 + label={store.preferences.homeFeed.hideReplies ? 'No' : 'Yes'} 104 + isSelected={!store.preferences.homeFeed.hideReplies} 105 + onPress={store.preferences.toggleHomeFeedHideReplies} 94 106 /> 95 107 </View> 96 108 <View 97 109 style={[ 98 110 pal.viewLight, 99 111 styles.card, 100 - !store.preferences.homeFeedRepliesEnabled && styles.dimmed, 112 + store.preferences.homeFeed.hideReplies && styles.dimmed, 101 113 ]}> 102 114 <Text type="title-sm" style={[pal.text, s.pb5]}> 103 115 Reply Filters ··· 108 120 <ToggleButton 109 121 type="default-light" 110 122 label="Followed users only" 111 - isSelected={ 112 - store.preferences.homeFeedRepliesByFollowedOnlyEnabled 113 - } 123 + isSelected={store.preferences.homeFeed.hideRepliesByUnfollowed} 114 124 onPress={ 115 - store.preferences.homeFeedRepliesEnabled 116 - ? store.preferences.toggleHomeFeedRepliesByFollowedOnlyEnabled 125 + !store.preferences.homeFeed.hideReplies 126 + ? store.preferences.toggleHomeFeedHideRepliesByUnfollowed 117 127 : undefined 118 128 } 119 129 style={[s.mb10]} ··· 123 133 feed. 124 134 </Text> 125 135 <RepliesThresholdInput 126 - enabled={store.preferences.homeFeedRepliesEnabled} 136 + enabled={!store.preferences.homeFeed.hideReplies} 127 137 /> 128 138 </View> 129 139 ··· 136 146 </Text> 137 147 <ToggleButton 138 148 type="default-light" 139 - label={store.preferences.homeFeedRepostsEnabled ? 'Yes' : 'No'} 140 - isSelected={store.preferences.homeFeedRepostsEnabled} 141 - onPress={store.preferences.toggleHomeFeedRepostsEnabled} 149 + label={store.preferences.homeFeed.hideReposts ? 'No' : 'Yes'} 150 + isSelected={!store.preferences.homeFeed.hideReposts} 151 + onPress={store.preferences.toggleHomeFeedHideReposts} 142 152 /> 143 153 </View> 144 154 ··· 152 162 </Text> 153 163 <ToggleButton 154 164 type="default-light" 155 - label={store.preferences.homeFeedQuotePostsEnabled ? 'Yes' : 'No'} 156 - isSelected={store.preferences.homeFeedQuotePostsEnabled} 157 - onPress={store.preferences.toggleHomeFeedQuotePostsEnabled} 165 + label={store.preferences.homeFeed.hideQuotePosts ? 'No' : 'Yes'} 166 + isSelected={!store.preferences.homeFeed.hideQuotePosts} 167 + onPress={store.preferences.toggleHomeFeedHideQuotePosts} 158 168 /> 159 169 </View> 160 170 ··· 169 179 </Text> 170 180 <ToggleButton 171 181 type="default-light" 172 - label={store.preferences.homeFeedMergeFeedEnabled ? 'Yes' : 'No'} 173 - isSelected={store.preferences.homeFeedMergeFeedEnabled} 182 + label={ 183 + store.preferences.homeFeed.lab_mergeFeedEnabled ? 'Yes' : 'No' 184 + } 185 + isSelected={!!store.preferences.homeFeed.lab_mergeFeedEnabled} 174 186 onPress={store.preferences.toggleHomeFeedMergeFeedEnabled} 175 187 /> 176 188 </View>
+11 -7
src/view/screens/PreferencesThreads.tsx
··· 59 59 {key: 'most-likes', label: 'Most-liked replies first'}, 60 60 {key: 'random', label: 'Random (aka "Poster\'s Roulette")'}, 61 61 ]} 62 - onSelect={store.preferences.setThreadDefaultSort} 63 - initialSelection={store.preferences.threadDefaultSort} 62 + onSelect={store.preferences.setThreadSort} 63 + initialSelection={store.preferences.thread.sort} 64 64 /> 65 65 </View> 66 66 </View> ··· 74 74 </Text> 75 75 <ToggleButton 76 76 type="default-light" 77 - label={store.preferences.threadFollowedUsersFirst ? 'Yes' : 'No'} 78 - isSelected={store.preferences.threadFollowedUsersFirst} 79 - onPress={store.preferences.toggleThreadFollowedUsersFirst} 77 + label={ 78 + store.preferences.thread.prioritizeFollowedUsers ? 'Yes' : 'No' 79 + } 80 + isSelected={store.preferences.thread.prioritizeFollowedUsers} 81 + onPress={store.preferences.togglePrioritizedFollowedUsers} 80 82 /> 81 83 </View> 82 84 ··· 91 93 </Text> 92 94 <ToggleButton 93 95 type="default-light" 94 - label={store.preferences.threadTreeViewEnabled ? 'Yes' : 'No'} 95 - isSelected={store.preferences.threadTreeViewEnabled} 96 + label={ 97 + store.preferences.thread.lab_treeViewEnabled ? 'Yes' : 'No' 98 + } 99 + isSelected={!!store.preferences.thread.lab_treeViewEnabled} 96 100 onPress={store.preferences.toggleThreadTreeViewEnabled} 97 101 /> 98 102 </View>
+13
yarn.lock
··· 47 47 tlds "^1.234.0" 48 48 typed-emitter "^2.1.0" 49 49 50 + "@atproto/api@^0.6.16": 51 + version "0.6.16" 52 + resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.6.16.tgz#0e5f259a8eb8af239b4e77bf70d7e770b33f4eeb" 53 + integrity sha512-DpG994bdwk7NWJSb36Af+0+FRWMFZgzTcrK0rN2tvlsMh6wBF/RdErjHKuoL8wcogGzbI2yp8eOqsA00lyoisw== 54 + dependencies: 55 + "@atproto/common-web" "^0.2.0" 56 + "@atproto/lexicon" "^0.2.1" 57 + "@atproto/syntax" "^0.1.1" 58 + "@atproto/xrpc" "^0.3.1" 59 + multiformats "^9.9.0" 60 + tlds "^1.234.0" 61 + typed-emitter "^2.1.0" 62 + 50 63 "@atproto/bsky@^0.0.5": 51 64 version "0.0.5" 52 65 resolved "https://registry.yarnpkg.com/@atproto/bsky/-/bsky-0.0.5.tgz#4667977158a112f27aeab14fedb3ca1e3ebbd873"