Skip to content

Commit f5757b4

Browse files
authored
Merge pull request #1095 from DimensionDev/feature/misskey_social_timeline
add social timeline support for misskey
2 parents 82b9094 + 99500df commit f5757b4

File tree

8 files changed

+130
-2
lines changed

8 files changed

+130
-2
lines changed

app/src/main/java/dev/dimension/flare/data/model/TabSettings.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ internal val TitleType.Localized.resId: Int
4646
TitleType.Localized.LocalizedKey.Rss -> R.string.rss_title
4747
TitleType.Localized.LocalizedKey.Antenna -> R.string.home_tab_antennas_title
4848
TitleType.Localized.LocalizedKey.MixedTimeline -> R.string.home_tab_mixed_timeline_title
49+
TitleType.Localized.LocalizedKey.Social -> R.string.home_tab_social_timeline_title
4950
}
5051

5152
internal fun IconType.Material.MaterialIcon.toIcon(): ImageVector =

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@
323323
<string name="home_tab_mixed_timeline_title">Mixed</string>
324324
<string name="tab_settings_mixed_timeline">Add mixed timeline tab</string>
325325
<string name="tab_settings_mixed_timeline_desc">Mixed timeline will mix all tabs timeline result in one tab</string>
326+
<string name="home_tab_social_timeline_title">Social</string>
326327

327328
<string name="changelog_current">
328329
<![CDATA[

desktopApp/src/main/composeResources/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,6 @@
114114
<string name="feeds_discover_feeds_created_by">Created by %1$s</string>
115115
<string name="feeds_unsubscribe">Unsubscribe</string>
116116
<string name="feeds_subscribe">Subscribe</string>
117+
118+
<string name="social_title">Social</string>
117119
</resources>

desktopApp/src/main/kotlin/dev/dimension/flare/data/model/TabSettings.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import dev.dimension.flare.mastodon_tab_local_title
3535
import dev.dimension.flare.mastodon_tab_public_title
3636
import dev.dimension.flare.rss_title
3737
import dev.dimension.flare.settings_title
38+
import dev.dimension.flare.social_title
3839
import dev.dimension.flare.ui.icons.Misskey
3940
import org.jetbrains.compose.resources.StringResource
4041

@@ -55,6 +56,7 @@ internal val TitleType.Localized.res: StringResource
5556
TitleType.Localized.LocalizedKey.Feeds -> Res.string.home_tab_feeds_title
5657
TitleType.Localized.LocalizedKey.DirectMessage -> Res.string.dm_list_title
5758
TitleType.Localized.LocalizedKey.Rss -> Res.string.rss_title
59+
TitleType.Localized.LocalizedKey.Social -> Res.string.social_title
5860
}
5961

6062
internal fun IconType.Material.MaterialIcon.toIcon(): ImageVector =

shared/src/androidJvmMain/kotlin/dev/dimension/flare/data/model/TabSettings.kt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ public sealed interface TitleType {
7575
Rss,
7676
Antenna,
7777
MixedTimeline,
78+
Social,
7879
}
7980
}
8081
}
@@ -399,6 +400,14 @@ public sealed interface TimelineTabItem : TabItem {
399400
icon = IconType.Mixed(IconType.Material.MaterialIcon.List, accountKey),
400401
),
401402
),
403+
Misskey.HybridTimelineTabItem(
404+
account = AccountType.Specific(accountKey),
405+
metaData =
406+
TabMetaData(
407+
title = TitleType.Localized(TitleType.Localized.LocalizedKey.Social),
408+
icon = IconType.Mixed(IconType.Material.MaterialIcon.Featured, accountKey),
409+
),
410+
),
402411
Misskey.LocalTimelineTabItem(
403412
account = AccountType.Specific(accountKey),
404413
metaData =
@@ -753,6 +762,20 @@ public object Misskey {
753762
override fun update(metaData: TabMetaData): TabItem = copy(metaData = metaData)
754763
}
755764

765+
@Serializable
766+
public data class HybridTimelineTabItem(
767+
override val account: AccountType,
768+
override val metaData: TabMetaData,
769+
) : TimelineTabItem {
770+
override val key: String = "hybrid_$account"
771+
772+
override fun createPresenter(): TimelinePresenter =
773+
dev.dimension.flare.ui.presenter.home.misskey
774+
.MisskeyHybridTimelinePresenter(account)
775+
776+
override fun update(metaData: TabMetaData): TabItem = copy(metaData = metaData)
777+
}
778+
756779
@Serializable
757780
public data class FavouriteTimelineTabItem(
758781
override val account: AccountType,
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package dev.dimension.flare.data.datasource.misskey
2+
3+
import androidx.paging.LoadType
4+
import androidx.paging.PagingState
5+
import dev.dimension.flare.common.BaseTimelineRemoteMediator
6+
import dev.dimension.flare.data.database.cache.CacheDatabase
7+
import dev.dimension.flare.data.database.cache.mapper.toDbPagingTimeline
8+
import dev.dimension.flare.data.database.cache.model.DbPagingTimelineWithStatus
9+
import dev.dimension.flare.data.network.misskey.MisskeyService
10+
import dev.dimension.flare.data.network.misskey.api.model.NotesHybridTimelineRequest
11+
import dev.dimension.flare.model.MicroBlogKey
12+
13+
internal class HybridTimelineRemoteMediator(
14+
private val accountKey: MicroBlogKey,
15+
private val service: MisskeyService,
16+
private val database: CacheDatabase,
17+
) : BaseTimelineRemoteMediator(
18+
database = database,
19+
) {
20+
override val pagingKey = "hybrid_timeline_$accountKey"
21+
22+
override suspend fun timeline(
23+
loadType: LoadType,
24+
state: PagingState<Int, DbPagingTimelineWithStatus>,
25+
): Result {
26+
val response =
27+
when (loadType) {
28+
LoadType.PREPEND -> return Result(
29+
endOfPaginationReached = true,
30+
)
31+
LoadType.REFRESH -> {
32+
service.notesHybridTimeline(
33+
NotesHybridTimelineRequest(
34+
limit = state.config.pageSize,
35+
),
36+
)
37+
}
38+
39+
LoadType.APPEND -> {
40+
val lastItem =
41+
database.pagingTimelineDao().getLastPagingTimeline(pagingKey)
42+
?: return Result(
43+
endOfPaginationReached = true,
44+
)
45+
service.notesHybridTimeline(
46+
NotesHybridTimelineRequest(
47+
limit = state.config.pageSize,
48+
untilId = lastItem.timeline.statusKey.id,
49+
),
50+
)
51+
}
52+
} ?: return Result(
53+
endOfPaginationReached = true,
54+
)
55+
56+
return Result(
57+
endOfPaginationReached = response.isEmpty(),
58+
data =
59+
response.toDbPagingTimeline(
60+
accountKey = accountKey,
61+
pagingKey = pagingKey,
62+
),
63+
)
64+
}
65+
}

shared/src/commonMain/kotlin/dev/dimension/flare/data/datasource/misskey/MisskeyDataSource.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ internal class MisskeyDataSource(
111111

112112
fun localTimeline(
113113
pageSize: Int = 20,
114-
pagingKey: String = "local_$accountKey",
115114
scope: CoroutineScope,
116115
): Flow<PagingData<UiTimeline>> =
117116
timelinePager(
@@ -130,9 +129,15 @@ internal class MisskeyDataSource(
130129
database,
131130
)
132131

132+
fun hybridTimelineLoader() =
133+
HybridTimelineRemoteMediator(
134+
accountKey,
135+
service,
136+
database,
137+
)
138+
133139
fun publicTimeline(
134140
pageSize: Int = 20,
135-
pagingKey: String = "public_$accountKey",
136141
scope: CoroutineScope,
137142
): Flow<PagingData<UiTimeline>> =
138143
timelinePager(
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package dev.dimension.flare.ui.presenter.home.misskey
2+
3+
import dev.dimension.flare.common.BaseTimelineLoader
4+
import dev.dimension.flare.data.datasource.misskey.MisskeyDataSource
5+
import dev.dimension.flare.data.repository.AccountRepository
6+
import dev.dimension.flare.data.repository.accountServiceFlow
7+
import dev.dimension.flare.model.AccountType
8+
import dev.dimension.flare.ui.presenter.home.TimelinePresenter
9+
import kotlinx.coroutines.flow.Flow
10+
import kotlinx.coroutines.flow.map
11+
import org.koin.core.component.KoinComponent
12+
import org.koin.core.component.inject
13+
14+
public class MisskeyHybridTimelinePresenter(
15+
private val accountType: AccountType,
16+
) : TimelinePresenter(),
17+
KoinComponent {
18+
private val accountRepository: AccountRepository by inject()
19+
20+
override val loader: Flow<BaseTimelineLoader> by lazy {
21+
accountServiceFlow(
22+
accountType = accountType,
23+
repository = accountRepository,
24+
).map { service ->
25+
require(service is MisskeyDataSource)
26+
service.hybridTimelineLoader()
27+
}
28+
}
29+
}

0 commit comments

Comments
 (0)