Add thread sort settings (#1475)

* Add thread sorting preferences

* UI tweaks

* Tweak settings

* Tune the copy
This commit is contained in:
Paul Frazee 2023-09-19 12:24:58 -07:00 committed by GitHub
parent 9c4374f66a
commit da8499c881
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 256 additions and 13 deletions

View file

@ -241,7 +241,7 @@ export class PostThreadModel {
res.data.thread as AppBskyFeedDefs.ThreadViewPost,
thread.uri,
)
sortThread(thread)
sortThread(thread, this.rootStore.preferences)
this.thread = thread
}
}
@ -263,11 +263,16 @@ function pruneReplies(post: MaybePost) {
}
}
interface SortSettings {
threadDefaultSort: string
threadFollowedUsersFirst: boolean
}
type MaybeThreadItem =
| PostThreadItemModel
| AppBskyFeedDefs.NotFoundPost
| AppBskyFeedDefs.BlockedPost
function sortThread(item: MaybeThreadItem) {
function sortThread(item: MaybeThreadItem, opts: SortSettings) {
if ('notFound' in item) {
return
}
@ -296,13 +301,31 @@ function sortThread(item: MaybeThreadItem) {
if (modScore(a.moderation) !== modScore(b.moderation)) {
return modScore(a.moderation) - modScore(b.moderation)
}
if (a.post.likeCount === b.post.likeCount) {
return b.post.indexedAt.localeCompare(a.post.indexedAt) // newest
} else {
return (b.post.likeCount || 0) - (a.post.likeCount || 0) // most likes
if (opts.threadFollowedUsersFirst) {
const af = a.post.author.viewer?.following
const bf = b.post.author.viewer?.following
if (af && !bf) {
return -1
} else if (!af && bf) {
return 1
}
}
if (opts.threadDefaultSort === 'oldest') {
return a.post.indexedAt.localeCompare(b.post.indexedAt)
} else if (opts.threadDefaultSort === 'newest') {
return b.post.indexedAt.localeCompare(a.post.indexedAt)
} else if (opts.threadDefaultSort === 'most-likes') {
if (a.post.likeCount === b.post.likeCount) {
return b.post.indexedAt.localeCompare(a.post.indexedAt) // newest
} else {
return (b.post.likeCount || 0) - (a.post.likeCount || 0) // most likes
}
} else if (opts.threadDefaultSort === 'random') {
return 0.5 - Math.random() // this is vaguely criminal but we can get away with it
}
return b.post.indexedAt.localeCompare(a.post.indexedAt)
})
item.replies.forEach(reply => sortThread(reply))
item.replies.forEach(reply => sortThread(reply, opts))
}
}

View file

@ -25,6 +25,7 @@ const VISIBILITY_VALUES = ['ignore', 'warn', 'hide']
const DEFAULT_LANG_CODES = (deviceLocales || [])
.concat(['en', 'ja', 'pt', 'de'])
.slice(0, 6)
const THREAD_SORT_VALUES = ['oldest', 'newest', 'most-likes', 'random']
export class LabelPreferencesModel {
nsfw: LabelPreference = 'hide'
@ -55,6 +56,8 @@ export class PreferencesModel {
homeFeedRepostsEnabled: boolean = true
homeFeedQuotePostsEnabled: boolean = true
homeFeedMergeFeedEnabled: boolean = false
threadDefaultSort: string = 'oldest'
threadFollowedUsersFirst: boolean = true
requireAltTextEnabled: boolean = false
// used to linearize async modifications to state
@ -86,6 +89,8 @@ export class PreferencesModel {
homeFeedRepostsEnabled: this.homeFeedRepostsEnabled,
homeFeedQuotePostsEnabled: this.homeFeedQuotePostsEnabled,
homeFeedMergeFeedEnabled: this.homeFeedMergeFeedEnabled,
threadDefaultSort: this.threadDefaultSort,
threadFollowedUsersFirst: this.threadFollowedUsersFirst,
requireAltTextEnabled: this.requireAltTextEnabled,
}
}
@ -189,6 +194,21 @@ export class PreferencesModel {
) {
this.homeFeedMergeFeedEnabled = v.homeFeedMergeFeedEnabled
}
// check if thread sort order is set in preferences, then hydrate
if (
hasProp(v, 'threadDefaultSort') &&
typeof v.threadDefaultSort === 'string' &&
THREAD_SORT_VALUES.includes(v.threadDefaultSort)
) {
this.threadDefaultSort = v.threadDefaultSort
}
// check if tread followed-users-first is enabled in preferences, then hydrate
if (
hasProp(v, 'threadFollowedUsersFirst') &&
typeof v.threadFollowedUsersFirst === 'boolean'
) {
this.threadFollowedUsersFirst = v.threadFollowedUsersFirst
}
// check if requiring alt text is enabled in preferences, then hydrate
if (
hasProp(v, 'requireAltTextEnabled') &&
@ -494,6 +514,16 @@ export class PreferencesModel {
this.homeFeedMergeFeedEnabled = !this.homeFeedMergeFeedEnabled
}
setThreadDefaultSort(v: string) {
if (THREAD_SORT_VALUES.includes(v)) {
this.threadDefaultSort = v
}
}
toggleThreadFollowedUsersFirst() {
this.threadFollowedUsersFirst = !this.threadFollowedUsersFirst
}
toggleRequireAltTextEnabled() {
this.requireAltTextEnabled = !this.requireAltTextEnabled
}