Add a hotness thread sort (#6649)
* Add a hotness thread sort * Bump @atproto/api
This commit is contained in:
+1
-1
@@ -54,7 +54,7 @@
|
|||||||
"icons:optimize": "svgo -f ./assets/icons"
|
"icons:optimize": "svgo -f ./assets/icons"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@atproto/api": "^0.13.11",
|
"@atproto/api": "^0.13.18",
|
||||||
"@braintree/sanitize-url": "^6.0.2",
|
"@braintree/sanitize-url": "^6.0.2",
|
||||||
"@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet",
|
"@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet",
|
||||||
"@emoji-mart/react": "^1.1.1",
|
"@emoji-mart/react": "^1.1.1",
|
||||||
|
|||||||
@@ -56,6 +56,12 @@ export function ThreadPreferencesScreen({}: Props) {
|
|||||||
values={sortReplies ? [sortReplies] : []}
|
values={sortReplies ? [sortReplies] : []}
|
||||||
onChange={values => setThreadViewPrefs({sort: values[0]})}>
|
onChange={values => setThreadViewPrefs({sort: values[0]})}>
|
||||||
<View style={[a.gap_sm, a.flex_1]}>
|
<View style={[a.gap_sm, a.flex_1]}>
|
||||||
|
<Toggle.Item name="hotness" label={_(msg`Hot replies first`)}>
|
||||||
|
<Toggle.Radio />
|
||||||
|
<Toggle.LabelText>
|
||||||
|
<Trans>Hot replies first</Trans>
|
||||||
|
</Toggle.LabelText>
|
||||||
|
</Toggle.Item>
|
||||||
<Toggle.Item
|
<Toggle.Item
|
||||||
name="oldest"
|
name="oldest"
|
||||||
label={_(msg`Oldest replies first`)}>
|
label={_(msg`Oldest replies first`)}>
|
||||||
|
|||||||
@@ -237,7 +237,11 @@ export function sortThread(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.sort === 'oldest') {
|
if (opts.sort === 'hotness') {
|
||||||
|
const aHotness = getHotness(a.post)
|
||||||
|
const bHotness = getHotness(b.post)
|
||||||
|
return bHotness - aHotness
|
||||||
|
} else if (opts.sort === 'oldest') {
|
||||||
return a.post.indexedAt.localeCompare(b.post.indexedAt)
|
return a.post.indexedAt.localeCompare(b.post.indexedAt)
|
||||||
} else if (opts.sort === 'newest') {
|
} else if (opts.sort === 'newest') {
|
||||||
return b.post.indexedAt.localeCompare(a.post.indexedAt)
|
return b.post.indexedAt.localeCompare(a.post.indexedAt)
|
||||||
@@ -269,6 +273,21 @@ export function sortThread(
|
|||||||
// internal methods
|
// internal methods
|
||||||
// =
|
// =
|
||||||
|
|
||||||
|
// Inspired by https://join-lemmy.org/docs/contributors/07-ranking-algo.html
|
||||||
|
// We want to give recent comments a real chance (and not bury them deep below the fold)
|
||||||
|
// while also surfacing well-liked comments from the past. In the future, we can explore
|
||||||
|
// something more sophisticated, but we don't have much data on the client right now.
|
||||||
|
function getHotness(post: AppBskyFeedDefs.PostView) {
|
||||||
|
const hoursAgo =
|
||||||
|
(new Date().getTime() - new Date(post.indexedAt).getTime()) /
|
||||||
|
(1000 * 60 * 60)
|
||||||
|
const likeCount = post.likeCount ?? 0
|
||||||
|
const likeOrder = Math.log(3 + likeCount)
|
||||||
|
const timePenaltyExponent = 1.5 + 1.5 / (1 + Math.log(1 + likeCount))
|
||||||
|
const timePenalty = Math.pow(hoursAgo + 2, timePenaltyExponent)
|
||||||
|
return likeOrder / timePenalty
|
||||||
|
}
|
||||||
|
|
||||||
function responseToThreadNodes(
|
function responseToThreadNodes(
|
||||||
node: ThreadViewNode,
|
node: ThreadViewNode,
|
||||||
depth = 0,
|
depth = 0,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const DEFAULT_HOME_FEED_PREFS: UsePreferencesQueryResponse['feedViewPrefs
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_THREAD_VIEW_PREFS: ThreadViewPreferences = {
|
export const DEFAULT_THREAD_VIEW_PREFS: ThreadViewPreferences = {
|
||||||
sort: 'newest',
|
sort: 'hotness',
|
||||||
prioritizeFollowedUsers: true,
|
prioritizeFollowedUsers: true,
|
||||||
lab_treeViewEnabled: false,
|
lab_treeViewEnabled: false,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,6 @@ export type ThreadViewPreferences = Pick<
|
|||||||
BskyThreadViewPreference,
|
BskyThreadViewPreference,
|
||||||
'prioritizeFollowedUsers'
|
'prioritizeFollowedUsers'
|
||||||
> & {
|
> & {
|
||||||
sort: 'oldest' | 'newest' | 'most-likes' | 'random' | string
|
sort: 'hotness' | 'oldest' | 'newest' | 'most-likes' | 'random' | string
|
||||||
lab_treeViewEnabled?: boolean
|
lab_treeViewEnabled?: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,20 @@
|
|||||||
tlds "^1.234.0"
|
tlds "^1.234.0"
|
||||||
zod "^3.23.8"
|
zod "^3.23.8"
|
||||||
|
|
||||||
|
"@atproto/api@^0.13.18":
|
||||||
|
version "0.13.18"
|
||||||
|
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.13.18.tgz#cc537cc3b4c8d03f258a373f4d893fea11a77cdd"
|
||||||
|
integrity sha512-rrl5HhzGYWZ7fiC965TPBUOVItq9M4dxMb6qz8IvAVQliSkrJrKc7UD0QWL89QiiXaOBuX8w+4i5r4wrfBGddg==
|
||||||
|
dependencies:
|
||||||
|
"@atproto/common-web" "^0.3.1"
|
||||||
|
"@atproto/lexicon" "^0.4.3"
|
||||||
|
"@atproto/syntax" "^0.3.1"
|
||||||
|
"@atproto/xrpc" "^0.6.4"
|
||||||
|
await-lock "^2.2.2"
|
||||||
|
multiformats "^9.9.0"
|
||||||
|
tlds "^1.234.0"
|
||||||
|
zod "^3.23.8"
|
||||||
|
|
||||||
"@atproto/aws@^0.2.7":
|
"@atproto/aws@^0.2.7":
|
||||||
version "0.2.7"
|
version "0.2.7"
|
||||||
resolved "https://registry.yarnpkg.com/@atproto/aws/-/aws-0.2.7.tgz#2f3e05c897ef49b4c46452ec8c36870193952998"
|
resolved "https://registry.yarnpkg.com/@atproto/aws/-/aws-0.2.7.tgz#2f3e05c897ef49b4c46452ec8c36870193952998"
|
||||||
@@ -269,6 +283,17 @@
|
|||||||
multiformats "^9.9.0"
|
multiformats "^9.9.0"
|
||||||
zod "^3.23.8"
|
zod "^3.23.8"
|
||||||
|
|
||||||
|
"@atproto/lexicon@^0.4.3":
|
||||||
|
version "0.4.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@atproto/lexicon/-/lexicon-0.4.3.tgz#d69f6bb363a6326df7766c48132bfa30e22622d9"
|
||||||
|
integrity sha512-lFVZXe1S1pJP0dcxvJuHP3r/a+EAIBwwU7jUK+r8iLhIja+ml6NmYv8KeFHmIJATh03spEQ9s02duDmFVdCoXg==
|
||||||
|
dependencies:
|
||||||
|
"@atproto/common-web" "^0.3.1"
|
||||||
|
"@atproto/syntax" "^0.3.1"
|
||||||
|
iso-datestring-validator "^2.2.2"
|
||||||
|
multiformats "^9.9.0"
|
||||||
|
zod "^3.23.8"
|
||||||
|
|
||||||
"@atproto/oauth-provider@^0.2.5":
|
"@atproto/oauth-provider@^0.2.5":
|
||||||
version "0.2.5"
|
version "0.2.5"
|
||||||
resolved "https://registry.yarnpkg.com/@atproto/oauth-provider/-/oauth-provider-0.2.5.tgz#7358398125f840404bcc02c966fd2f333869ad2f"
|
resolved "https://registry.yarnpkg.com/@atproto/oauth-provider/-/oauth-provider-0.2.5.tgz#7358398125f840404bcc02c966fd2f333869ad2f"
|
||||||
@@ -411,6 +436,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@atproto/syntax/-/syntax-0.3.0.tgz#fafa2dbea9add37253005cb663e7373e05e618b3"
|
resolved "https://registry.yarnpkg.com/@atproto/syntax/-/syntax-0.3.0.tgz#fafa2dbea9add37253005cb663e7373e05e618b3"
|
||||||
integrity sha512-Weq0ZBxffGHDXHl9U7BQc2BFJi/e23AL+k+i5+D9hUq/bzT4yjGsrCejkjq0xt82xXDjmhhvQSZ0LqxyZ5woxA==
|
integrity sha512-Weq0ZBxffGHDXHl9U7BQc2BFJi/e23AL+k+i5+D9hUq/bzT4yjGsrCejkjq0xt82xXDjmhhvQSZ0LqxyZ5woxA==
|
||||||
|
|
||||||
|
"@atproto/syntax@^0.3.1":
|
||||||
|
version "0.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@atproto/syntax/-/syntax-0.3.1.tgz#4346418728f9643d783d2ffcf7c77e132e1f53d4"
|
||||||
|
integrity sha512-fzW0Mg1QUOVCWUD3RgEsDt6d1OZ6DdFmbKcDdbzUfh0t4rhtRAC05KbZYmxuMPWDAiJ4BbbQ5dkAc/mNypMXkw==
|
||||||
|
|
||||||
"@atproto/xrpc-server@^0.7.1":
|
"@atproto/xrpc-server@^0.7.1":
|
||||||
version "0.7.1"
|
version "0.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/@atproto/xrpc-server/-/xrpc-server-0.7.1.tgz#e9750aab7bb531c3a82dc6048d47179de18dbdd9"
|
resolved "https://registry.yarnpkg.com/@atproto/xrpc-server/-/xrpc-server-0.7.1.tgz#e9750aab7bb531c3a82dc6048d47179de18dbdd9"
|
||||||
@@ -437,6 +467,14 @@
|
|||||||
"@atproto/lexicon" "^0.4.2"
|
"@atproto/lexicon" "^0.4.2"
|
||||||
zod "^3.23.8"
|
zod "^3.23.8"
|
||||||
|
|
||||||
|
"@atproto/xrpc@^0.6.4":
|
||||||
|
version "0.6.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@atproto/xrpc/-/xrpc-0.6.4.tgz#4cf59774f7c72e5bc821bc5f1d57f0a6ae2014db"
|
||||||
|
integrity sha512-9ZAJ8nsXTqC4XFyS0E1Wlg7bAvonhXQNQ3Ocs1L1LIwFLXvsw/4fNpIHXxvXvqTCVeyHLbImOnE9UiO1c/qIYA==
|
||||||
|
dependencies:
|
||||||
|
"@atproto/lexicon" "^0.4.3"
|
||||||
|
zod "^3.23.8"
|
||||||
|
|
||||||
"@aws-crypto/crc32@3.0.0":
|
"@aws-crypto/crc32@3.0.0":
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@aws-crypto/crc32/-/crc32-3.0.0.tgz#07300eca214409c33e3ff769cd5697b57fdd38fa"
|
resolved "https://registry.yarnpkg.com/@aws-crypto/crc32/-/crc32-3.0.0.tgz#07300eca214409c33e3ff769cd5697b57fdd38fa"
|
||||||
|
|||||||
Reference in New Issue
Block a user