Skip to content

Commit 57ab9a7

Browse files
committed
add rss source db
1 parent 0893dc5 commit 57ab9a7

File tree

8 files changed

+170
-16
lines changed

8 files changed

+170
-16
lines changed

shared/schemas/dev.dimension.flare.data.database.app.AppDatabase/4.json

+14-15
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"formatVersion": 1,
33
"database": {
44
"version": 4,
5-
"identityHash": "254782b3237e8fbc34a2e5f26647a2fb",
5+
"identityHash": "7f851509975964fb11a67225ecd00f90",
66
"entities": [
77
{
88
"tableName": "DbAccount",
@@ -143,39 +143,38 @@
143143
}
144144
},
145145
{
146-
"tableName": "DbGuestData",
147-
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `host` TEXT NOT NULL, `platformType` TEXT NOT NULL, PRIMARY KEY(`id`))",
146+
"tableName": "DbRssSources",
147+
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `title` TEXT, `lastUpdate` INTEGER NOT NULL, PRIMARY KEY(`url`))",
148148
"fields": [
149149
{
150-
"fieldPath": "id",
151-
"columnName": "id",
152-
"affinity": "INTEGER",
150+
"fieldPath": "url",
151+
"columnName": "url",
152+
"affinity": "TEXT",
153153
"notNull": true
154154
},
155155
{
156-
"fieldPath": "host",
157-
"columnName": "host",
158-
"affinity": "TEXT",
159-
"notNull": true
156+
"fieldPath": "title",
157+
"columnName": "title",
158+
"affinity": "TEXT"
160159
},
161160
{
162-
"fieldPath": "platformType",
163-
"columnName": "platformType",
164-
"affinity": "TEXT",
161+
"fieldPath": "lastUpdate",
162+
"columnName": "lastUpdate",
163+
"affinity": "INTEGER",
165164
"notNull": true
166165
}
167166
],
168167
"primaryKey": {
169168
"autoGenerate": false,
170169
"columnNames": [
171-
"id"
170+
"url"
172171
]
173172
}
174173
}
175174
],
176175
"setupQueries": [
177176
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
178-
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '254782b3237e8fbc34a2e5f26647a2fb')"
177+
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '7f851509975964fb11a67225ecd00f90')"
179178
]
180179
}
181180
}

shared/src/commonMain/kotlin/dev/dimension/flare/common/PagingState.kt

+35
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
package dev.dimension.flare.common
22

3+
import androidx.compose.runtime.Composable
34
import androidx.compose.runtime.Immutable
5+
import androidx.compose.runtime.State
6+
import androidx.compose.runtime.produceState
47
import androidx.paging.LoadState
58
import androidx.paging.compose.LazyPagingItems
69
import androidx.paging.compose.itemContentType
710
import androidx.paging.compose.itemKey
811
import dev.dimension.flare.ui.model.UiState
912
import kotlinx.collections.immutable.ImmutableList
13+
import kotlinx.collections.immutable.toImmutableList
14+
import kotlinx.coroutines.flow.Flow
1015
import kotlin.contracts.ExperimentalContracts
1116
import kotlin.contracts.contract
1217

@@ -47,6 +52,28 @@ public sealed class PagingState<T> {
4752

4853
public abstract fun itemContentType(contentType: ((item: T) -> Any?)? = null): (index: Int) -> Any?
4954

55+
@Immutable
56+
internal data class ImmutableSuccess<T : Any>(
57+
private val data: ImmutableList<T>,
58+
override val itemCount: Int = data.size,
59+
override val isRefreshing: Boolean = false,
60+
override val appendState: LoadState = LoadState.NotLoading(endOfPaginationReached = true),
61+
) : Success<T>() {
62+
override fun get(index: Int): T? = data.getOrNull(index)
63+
64+
override fun peek(index: Int): T? = data.getOrNull(index)
65+
66+
override suspend fun refreshSuspend() {
67+
}
68+
69+
override fun retry() {
70+
}
71+
72+
override fun itemContentType(contentType: ((item: T) -> Any?)?): (index: Int) -> Any? = { null }
73+
74+
override fun itemKey(key: ((item: T) -> Any)?): (index: Int) -> Any = { it }
75+
}
76+
5077
@Immutable
5178
internal data class SingleSuccess<T : Any>(
5279
private val data: CacheableState<ImmutableList<T>>,
@@ -221,3 +248,11 @@ internal fun <T : Any> UiState<PagingState<T>>.flatten(): PagingState<T> =
221248
is UiState.Error -> PagingState.Error(throwable)
222249
is UiState.Success -> data
223250
}
251+
252+
@Composable
253+
internal fun <T : Any> Flow<List<T>>.collectPagingState(): State<PagingState<T>> =
254+
produceState<PagingState<T>>(initialValue = PagingState.Loading<T>()) {
255+
collect {
256+
value = PagingState.Success.ImmutableSuccess(it.toImmutableList())
257+
}
258+
}

shared/src/commonMain/kotlin/dev/dimension/flare/data/database/app/AppDatabase.kt

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dev.dimension.flare.data.database.app
22

3+
import androidx.room.AutoMigration
34
import androidx.room.ConstructedBy
45
import androidx.room.Database
56
import androidx.room.RoomDatabase
@@ -8,6 +9,7 @@ import androidx.room.TypeConverters
89
import dev.dimension.flare.data.database.app.dao.AccountDao
910
import dev.dimension.flare.data.database.app.dao.ApplicationDao
1011
import dev.dimension.flare.data.database.app.dao.KeywordFilterDao
12+
import dev.dimension.flare.data.database.app.dao.RssSourceDao
1113
import dev.dimension.flare.data.database.app.dao.SearchHistoryDao
1214

1315
@Database(
@@ -16,8 +18,15 @@ import dev.dimension.flare.data.database.app.dao.SearchHistoryDao
1618
dev.dimension.flare.data.database.app.model.DbApplication::class,
1719
dev.dimension.flare.data.database.app.model.DbKeywordFilter::class,
1820
dev.dimension.flare.data.database.app.model.DbSearchHistory::class,
21+
dev.dimension.flare.data.database.app.model.DbRssSources::class,
22+
],
23+
version = 4,
24+
autoMigrations = [
25+
AutoMigration(
26+
from = 3,
27+
to = 4,
28+
),
1929
],
20-
version = 3,
2130
)
2231
@TypeConverters(
2332
dev.dimension.flare.data.database.adapter.MicroBlogKeyConverter::class,
@@ -32,6 +41,8 @@ internal abstract class AppDatabase : RoomDatabase() {
3241
abstract fun keywordFilterDao(): KeywordFilterDao
3342

3443
abstract fun searchHistoryDao(): SearchHistoryDao
44+
45+
abstract fun rssSourceDao(): RssSourceDao
3546
}
3647

3748
// The Room compiler generates the `actual` implementations.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package dev.dimension.flare.data.database.app.dao
2+
3+
import androidx.room.Dao
4+
import androidx.room.Insert
5+
import androidx.room.Query
6+
import dev.dimension.flare.data.database.app.model.DbRssSources
7+
import kotlinx.coroutines.flow.Flow
8+
9+
@Dao
10+
internal interface RssSourceDao {
11+
@Insert
12+
suspend fun insert(data: DbRssSources)
13+
14+
@Query("SELECT * FROM DbRssSources")
15+
fun getAll(): Flow<List<DbRssSources>>
16+
17+
@Query("DELETE FROM DbRssSources WHERE url = :url")
18+
suspend fun delete(url: String)
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package dev.dimension.flare.data.database.app.model
2+
3+
import androidx.room.Entity
4+
import androidx.room.PrimaryKey
5+
6+
@Entity
7+
internal data class DbRssSources(
8+
@PrimaryKey
9+
val url: String,
10+
val title: String?,
11+
val lastUpdate: Long,
12+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package dev.dimension.flare.ui.model
2+
3+
import dev.dimension.flare.ui.render.UiDateTime
4+
5+
public data class UiRssSource(
6+
val url: String,
7+
val title: String?,
8+
val lastUpdate: UiDateTime,
9+
)

shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Render.kt

+9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dev.dimension.flare.ui.model.mapper
22

33
import com.fleeksoft.ksoup.nodes.Element
4+
import dev.dimension.flare.data.database.app.model.DbRssSources
45
import dev.dimension.flare.data.database.cache.model.DbDirectMessageTimelineWithRoom
56
import dev.dimension.flare.data.database.cache.model.DbMessageItemWithUser
67
import dev.dimension.flare.data.database.cache.model.DbPagingTimelineWithStatus
@@ -14,6 +15,7 @@ import dev.dimension.flare.model.MicroBlogKey
1415
import dev.dimension.flare.model.ReferenceType
1516
import dev.dimension.flare.ui.model.UiDMItem
1617
import dev.dimension.flare.ui.model.UiDMRoom
18+
import dev.dimension.flare.ui.model.UiRssSource
1719
import dev.dimension.flare.ui.model.UiTimeline
1820
import dev.dimension.flare.ui.render.toUi
1921
import kotlinx.collections.immutable.toImmutableList
@@ -163,3 +165,10 @@ internal fun DbMessageItemWithUser.render(accountKey: MicroBlogKey) =
163165
is MessageContent.Bluesky -> null
164166
},
165167
)
168+
169+
internal fun DbRssSources.render() =
170+
UiRssSource(
171+
url = url,
172+
title = title,
173+
lastUpdate = Instant.fromEpochMilliseconds(lastUpdate).toUi(),
174+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package dev.dimension.flare.ui.presenter.home.rss
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.getValue
5+
import androidx.compose.runtime.remember
6+
import androidx.compose.runtime.rememberCoroutineScope
7+
import dev.dimension.flare.common.PagingState
8+
import dev.dimension.flare.common.collectPagingState
9+
import dev.dimension.flare.data.database.app.AppDatabase
10+
import dev.dimension.flare.ui.model.UiRssSource
11+
import dev.dimension.flare.ui.model.mapper.render
12+
import dev.dimension.flare.ui.presenter.PresenterBase
13+
import kotlinx.coroutines.flow.map
14+
import kotlinx.coroutines.launch
15+
import org.koin.core.component.KoinComponent
16+
import org.koin.core.component.inject
17+
18+
public class RssSourcesPresenter :
19+
PresenterBase<RssSourcesPresenter.State>(),
20+
KoinComponent {
21+
private val appDatabase by inject<AppDatabase>()
22+
23+
public interface State {
24+
public val sources: PagingState<UiRssSource>
25+
26+
public fun add(
27+
url: String,
28+
title: String,
29+
)
30+
31+
public fun delete(url: String)
32+
}
33+
34+
@Composable
35+
override fun body(): State {
36+
val scope = rememberCoroutineScope()
37+
val sources by remember {
38+
appDatabase.rssSourceDao().getAll().map {
39+
it.map {
40+
it.render()
41+
}
42+
}
43+
}.collectPagingState()
44+
return object : State {
45+
override val sources: PagingState<UiRssSource> = sources
46+
47+
override fun add(
48+
url: String,
49+
title: String,
50+
) {
51+
}
52+
53+
override fun delete(url: String) {
54+
scope.launch {
55+
appDatabase.rssSourceDao().delete(url)
56+
}
57+
}
58+
}
59+
}
60+
}

0 commit comments

Comments
 (0)