Add "Trending posts" (statuses) feed (#4007)

Add "Trending posts" (statuses) feed.

This feed is a good source of interesting accounts to follow and,
personally, a sort of "Front page of the Fediverse".

Since #3908 and #3910 (which would provide a more thorough, albeit
complex, access to trending things) won't get merged, I'd like to
address this missing feed by simply adding another tab/feed.

~~If desired, I can move the second commit (fixing lint) to another
PR.~~

## Screenshots
### Tab
<img
src="https://github.com/tuskyapp/Tusky/assets/1063155/6a71a97e-673e-44c7-b67d-9b1df0bed4f5"
width=320 /> <img
src="https://github.com/tuskyapp/Tusky/assets/1063155/9bf60b23-d2f3-4dd8-8af6-e96647b02121"
width=320 />

### Activity
<img
src="https://github.com/tuskyapp/Tusky/assets/1063155/4e07dea3-d97f-42c6-8551-492a3116fcfa"
width=320 /> <img
src="https://github.com/tuskyapp/Tusky/assets/1063155/ad00a134-d622-43f4-8305-84cfa7fed706"
width=320 />
This commit is contained in:
Angelo Suzuki 2023-09-14 22:37:41 +02:00 committed by GitHub
commit fa80a0123a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 148 additions and 11 deletions

View file

@ -540,7 +540,8 @@ class TimelineFragment :
when (kind) {
TimelineViewModel.Kind.HOME,
TimelineViewModel.Kind.PUBLIC_FEDERATED,
TimelineViewModel.Kind.PUBLIC_LOCAL -> adapter.refresh()
TimelineViewModel.Kind.PUBLIC_LOCAL,
TimelineViewModel.Kind.PUBLIC_TRENDING_STATUSES -> adapter.refresh()
TimelineViewModel.Kind.USER,
TimelineViewModel.Kind.USER_WITH_REPLIES -> if (status.account.id == viewModel.id) {
adapter.refresh()

View file

@ -33,6 +33,14 @@ class NetworkTimelineRemoteMediator(
private val viewModel: NetworkTimelineViewModel
) : RemoteMediator<String, StatusViewData>() {
private val statusIds = mutableSetOf<String>()
init {
if (viewModel.kind == TimelineViewModel.Kind.PUBLIC_TRENDING_STATUSES) {
statusIds.addAll(viewModel.statusData.map { it.id })
}
}
override suspend fun load(
loadType: LoadType,
state: PagingState<String, StatusViewData>
@ -88,6 +96,10 @@ class NetworkTimelineRemoteMediator(
false
}
if (viewModel.kind == TimelineViewModel.Kind.PUBLIC_TRENDING_STATUSES) {
statusIds.addAll(data.map { it.id })
}
viewModel.statusData.addAll(0, data)
if (insertPlaceholder) {
@ -96,11 +108,22 @@ class NetworkTimelineRemoteMediator(
} else {
val linkHeader = statusResponse.headers()["Link"]
val links = HttpHeaderLink.parse(linkHeader)
val nextId = HttpHeaderLink.findByRelationType(links, "next")?.uri?.getQueryParameter("max_id")
val next = HttpHeaderLink.findByRelationType(links, "next")
viewModel.nextKey = nextId
var filteredData = data
if (viewModel.kind == TimelineViewModel.Kind.PUBLIC_TRENDING_STATUSES) {
// Trending statuses use offset for paging, not IDs. If a new status has been added to the remote
// feed after we performed the initial fetch, then the feed will have moved, but our offset won't.
// As a result, we'd get repeat statuses. This addresses that.
filteredData = data.filter { !statusIds.contains(it.id) }
statusIds.addAll(filteredData.map { it.id })
viewModel.statusData.addAll(data)
viewModel.nextKey = next?.uri?.getQueryParameter("offset")
} else {
viewModel.nextKey = next?.uri?.getQueryParameter("max_id")
}
viewModel.statusData.addAll(filteredData)
}
viewModel.currentSource?.invalidate()

View file

@ -308,6 +308,7 @@ class NetworkTimelineViewModel @Inject constructor(
Kind.FAVOURITES -> api.favourites(fromId, uptoId, limit)
Kind.BOOKMARKS -> api.bookmarks(fromId, uptoId, limit)
Kind.LIST -> api.listTimeline(id!!, fromId, uptoId, limit)
Kind.PUBLIC_TRENDING_STATUSES -> api.trendingStatuses(limit = limit, offset = fromId)
}
}

View file

@ -321,12 +321,12 @@ abstract class TimelineViewModel(
}
enum class Kind {
HOME, PUBLIC_LOCAL, PUBLIC_FEDERATED, TAG, USER, USER_PINNED, USER_WITH_REPLIES, FAVOURITES, LIST, BOOKMARKS;
HOME, PUBLIC_LOCAL, PUBLIC_FEDERATED, TAG, USER, USER_PINNED, USER_WITH_REPLIES, FAVOURITES, LIST, BOOKMARKS, PUBLIC_TRENDING_STATUSES;
fun toFilterKind(): Filter.Kind {
return when (valueOf(name)) {
HOME, LIST -> Filter.Kind.HOME
PUBLIC_FEDERATED, PUBLIC_LOCAL, TAG, FAVOURITES -> Filter.Kind.PUBLIC
PUBLIC_FEDERATED, PUBLIC_LOCAL, TAG, FAVOURITES, PUBLIC_TRENDING_STATUSES -> Filter.Kind.PUBLIC
USER, USER_WITH_REPLIES, USER_PINNED -> Filter.Kind.ACCOUNT
else -> Filter.Kind.PUBLIC
}