Skip to content

Implement favorite feature #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 3, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
id("com.google.devtools.ksp")
}

android {
@@ -81,11 +82,19 @@ dependencies {
implementation(libs.ktor.client.content.negotiation)
implementation(libs.ktor.client.logging)

implementation(libs.androidx.room.runtime)
ksp(libs.androidx.room.compiler)
implementation(libs.androidx.room.ktx)

testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.ui.test.junit4)
debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)
}

ksp {
arg("room.schemaLocation", "$projectDir/schemas")
}
52 changes: 52 additions & 0 deletions app/schemas/com.paulcoding.hviewer.database.AppDatabase/1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "f8c3310b39b34e0c6c9c004f8557d416",
"entities": [
{
"tableName": "favorite_posts",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `name` TEXT NOT NULL, `thumbnail` TEXT NOT NULL, `site` TEXT NOT NULL, PRIMARY KEY(`url`))",
"fields": [
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "thumbnail",
"columnName": "thumbnail",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "site",
"columnName": "site",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"url"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f8c3310b39b34e0c6c9c004f8557d416')"
]
}
}
52 changes: 52 additions & 0 deletions app/schemas/com.paulcoding.hviewer.database.AppDatabase/2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"formatVersion": 1,
"database": {
"version": 2,
"identityHash": "f8c3310b39b34e0c6c9c004f8557d416",
"entities": [
{
"tableName": "favorite_posts",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `name` TEXT NOT NULL, `thumbnail` TEXT NOT NULL, `site` TEXT NOT NULL, PRIMARY KEY(`url`))",
"fields": [
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "thumbnail",
"columnName": "thumbnail",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "site",
"columnName": "site",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"url"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f8c3310b39b34e0c6c9c004f8557d416')"
]
}
}
58 changes: 58 additions & 0 deletions app/schemas/com.paulcoding.hviewer.database.AppDatabase/3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"formatVersion": 1,
"database": {
"version": 3,
"identityHash": "40109ede6ba29fdee1d23735114a2981",
"entities": [
{
"tableName": "favorite_posts",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `name` TEXT NOT NULL, `thumbnail` TEXT NOT NULL, `site` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, PRIMARY KEY(`url`))",
"fields": [
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "thumbnail",
"columnName": "thumbnail",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "site",
"columnName": "site",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "createdAt",
"columnName": "createdAt",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"url"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '40109ede6ba29fdee1d23735114a2981')"
]
}
}
10 changes: 10 additions & 0 deletions app/src/main/java/com/paulcoding/hviewer/database/AppDatabase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.paulcoding.hviewer.database

import androidx.room.Database
import androidx.room.RoomDatabase
import com.paulcoding.hviewer.model.PostItem

@Database(entities = [PostItem::class], version = 3, exportSchema = true)
abstract class AppDatabase : RoomDatabase() {
abstract fun favoritePostDao(): FavoritePostDao
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.paulcoding.hviewer.database

import androidx.room.Room
import com.paulcoding.hviewer.MainApp.Companion.appContext

object DatabaseProvider {
private var db: AppDatabase? = null

fun getInstance(): AppDatabase {
if (db == null) {
db = Room.databaseBuilder(
appContext,
AppDatabase::class.java, "hviewer_db"
)
.addMigrations(MIGRATION_1_2, MIGRATION_2_3)
.build()
}
return db!!
}
}
21 changes: 21 additions & 0 deletions app/src/main/java/com/paulcoding/hviewer/database/FavoriteDao.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.paulcoding.hviewer.database

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.paulcoding.hviewer.model.PostItem
import kotlinx.coroutines.flow.Flow

@Dao
interface FavoritePostDao {
@Query("SELECT * FROM favorite_posts ORDER BY createdAt DESC")
fun getAll(): Flow<List<PostItem>>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(post: PostItem)

@Delete
suspend fun delete(post: PostItem)
}
16 changes: 16 additions & 0 deletions app/src/main/java/com/paulcoding/hviewer/database/Migrations.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.paulcoding.hviewer.database

import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase

val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE favorite_posts ADD COLUMN site TEXT NOT NULL DEFAULT \"\"")
}
}

val MIGRATION_2_3 = object : Migration(2, 3) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE favorite_posts ADD COLUMN createdAt INTEGER NOT NULL DEFAULT 0")
}
}
9 changes: 8 additions & 1 deletion app/src/main/java/com/paulcoding/hviewer/model/PostModel.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
package com.paulcoding.hviewer.model

import androidx.room.Entity
import androidx.room.PrimaryKey

data class PostData(
val images: List<String>,
val total: Int,
val next: String? = null
)


@Entity(tableName = "favorite_posts")
data class PostItem(
val name: String = "",
@PrimaryKey
val url: String = "",
val name: String = "",
val thumbnail: String = "",
val site: String = "",
val createdAt: Long = 0,
)

data class Posts(
14 changes: 13 additions & 1 deletion app/src/main/java/com/paulcoding/hviewer/ui/component/HIcon.kt
Original file line number Diff line number Diff line change
@@ -2,10 +2,14 @@ package com.paulcoding.hviewer.ui.component

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.FavoriteBorder
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalContentColor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import com.paulcoding.hviewer.ui.icon.SettingsIcon

@@ -16,14 +20,22 @@ fun HBackIcon(onClick: () -> Unit) {
}
}

@Composable
fun HFavoriteIcon(modifier: Modifier = Modifier, isFavorite: Boolean, onClick: () -> Unit) {
val icon = if (isFavorite) Icons.Filled.Favorite else Icons.Filled.FavoriteBorder
val tint: Color = if (isFavorite) Color.Red else Color.Gray
HIcon(modifier = modifier, imageVector = icon, tint = tint, onClick = onClick)
}

@Composable
fun HIcon(
modifier: Modifier = Modifier,
imageVector: ImageVector = SettingsIcon,
description: String = "",
tint: Color = LocalContentColor.current,
onClick: () -> Unit
) {
IconButton(onClick = { onClick() }, modifier = modifier) {
Icon(imageVector, description)
Icon(imageVector, description, tint = tint)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.paulcoding.hviewer.ui.favorite

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import com.paulcoding.hviewer.model.PostItem
import com.paulcoding.hviewer.ui.component.HBackIcon
import com.paulcoding.hviewer.ui.component.HEmpty
import com.paulcoding.hviewer.ui.page.AppViewModel
import com.paulcoding.hviewer.ui.page.posts.PostCard
import kotlinx.coroutines.launch

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FavoritePage(
appViewModel: AppViewModel,
navToImages: (PostItem) -> Unit,
goBack: () -> Boolean
) {
val viewModel: AppViewModel = viewModel()
val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()
val favoritePosts by viewModel.favoritePosts.collectAsState(initial = emptyList())

fun onDelete(post: PostItem) {
appViewModel.deleteFavorite(post)

scope.launch {
val result = snackbarHostState.showSnackbar(
"${post.name} removed from favorite",
"Undo",
duration = SnackbarDuration.Short
)
when (result) {
SnackbarResult.ActionPerformed -> {
appViewModel.addFavorite(post, true)
}

SnackbarResult.Dismissed -> {
}
}
}
}

Scaffold(
modifier = Modifier.fillMaxSize(),
snackbarHost = { SnackbarHost(snackbarHostState) },
topBar = {
TopAppBar(title = { Text("Favorite") }, navigationIcon = {
HBackIcon { goBack() }
})
}) { paddings ->
LazyColumn(modifier = Modifier.padding(paddings)) {
items(items = favoritePosts, key = { it.url }) { item ->
FavoriteItem(item, navToImages = { navToImages(item) }, deleteFavorite = {
onDelete(item)
})
}
if (favoritePosts.isEmpty())
item { HEmpty() }
}
}

}

@Composable
fun FavoriteItem(
post: PostItem,
navToImages: () -> Unit,
deleteFavorite: () -> Unit
) {
PostCard(post, isFavorite = true, setFavorite = {
deleteFavorite()
}) {
navToImages()
}
}
16 changes: 15 additions & 1 deletion app/src/main/java/com/paulcoding/hviewer/ui/page/AppEntry.kt
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@ import androidx.navigation.compose.rememberNavController
import com.paulcoding.hviewer.model.PostItem
import com.paulcoding.hviewer.model.SiteConfigs
import com.paulcoding.hviewer.network.Github
import com.paulcoding.hviewer.ui.favorite.FavoritePage
import com.paulcoding.hviewer.ui.page.post.PostPage
import com.paulcoding.hviewer.ui.page.posts.PostsPage
import com.paulcoding.hviewer.ui.page.search.SearchPage
@@ -44,11 +45,14 @@ fun AppEntry() {
SitesPage(siteConfigs = siteConfigs,
refresh = { Github.refreshLocalConfigs() },
navToTopics = { site ->
appViewModel.setSiteConfig(siteConfigs.sites[site]!!)
appViewModel.setSiteConfig(site, siteConfigs.sites[site]!!)
navController.navigate(Route.POSTS)
}, navToSettings = {
navController.navigate(Route.SETTINGS)
},
navToFavorite = {
navController.navigate(Route.FAVORITE)
},
goBack = { navController.popBackStack() })
}
animatedComposable(Route.SETTINGS) {
@@ -80,6 +84,16 @@ fun AppEntry() {
goBack = { navController.popBackStack() },
)
}
animatedComposable(Route.FAVORITE) {
FavoritePage(
appViewModel = appViewModel,
navToImages = { post: PostItem ->
appViewModel.setSiteConfig(post.site, siteConfigs.sites[post.site]!!)
navToImages(post)
},
goBack = { navController.popBackStack() }
)
}
}
}

27 changes: 24 additions & 3 deletions app/src/main/java/com/paulcoding/hviewer/ui/page/AppViewModel.kt
Original file line number Diff line number Diff line change
@@ -1,26 +1,47 @@
package com.paulcoding.hviewer.ui.page

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.paulcoding.hviewer.database.DatabaseProvider
import com.paulcoding.hviewer.model.PostItem
import com.paulcoding.hviewer.model.SiteConfig
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

class AppViewModel : ViewModel() {
private var _stateFlow = MutableStateFlow(UiState())
val stateFlow = _stateFlow.asStateFlow()

val favoritePosts = DatabaseProvider.getInstance().favoritePostDao().getAll()

fun setCurrentPost(post: PostItem) {
_stateFlow.update { it.copy(post = post) }
}

fun setSiteConfig(siteConfig: SiteConfig) {
_stateFlow.update { it.copy(siteConfig = siteConfig) }
fun setSiteConfig(site: String, siteConfig: SiteConfig) {
_stateFlow.update { it.copy(site = site to siteConfig) }
}

data class UiState(
val post: PostItem = PostItem(),
val siteConfig: SiteConfig = SiteConfig(),
val site: Pair<String, SiteConfig> = "" to SiteConfig(),
)

fun addFavorite(postItem: PostItem, reAdded: Boolean = false) {
viewModelScope.launch {
val item = if (reAdded) postItem else postItem.copy(
site = _stateFlow.value.site.first,
createdAt = System.currentTimeMillis()
)
DatabaseProvider.getInstance().favoritePostDao().insert(item)
}
}

fun deleteFavorite(postItem: PostItem) {
viewModelScope.launch {
DatabaseProvider.getInstance().favoritePostDao().delete(postItem)
}
}
}
1 change: 1 addition & 0 deletions app/src/main/java/com/paulcoding/hviewer/ui/page/Route.kt
Original file line number Diff line number Diff line change
@@ -8,4 +8,5 @@ object Route {
const val POST = "post"
const val SEARCH = "search"
const val SETTINGS = "settings"
const val FAVORITE = "favorite"
}
Original file line number Diff line number Diff line change
@@ -42,7 +42,7 @@ import me.saket.telephoto.zoomable.zoomable
fun PostPage(appViewModel: AppViewModel, goBack: () -> Unit) {
val appState by appViewModel.stateFlow.collectAsState()
val post = appState.post
val siteConfig = appState.siteConfig
val siteConfig = appState.site.second

val viewModel: PostViewModel = viewModel(
factory = PostViewModelFactory(post.url, siteConfig = siteConfig)
58 changes: 45 additions & 13 deletions app/src/main/java/com/paulcoding/hviewer/ui/page/posts/PostsPage.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.paulcoding.hviewer.ui.page.posts

import android.widget.Toast
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.tween
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -29,6 +31,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -40,6 +43,7 @@ import com.paulcoding.hviewer.model.PostItem
import com.paulcoding.hviewer.model.SiteConfig
import com.paulcoding.hviewer.ui.component.HBackIcon
import com.paulcoding.hviewer.ui.component.HEmpty
import com.paulcoding.hviewer.ui.component.HFavoriteIcon
import com.paulcoding.hviewer.ui.component.HGoTop
import com.paulcoding.hviewer.ui.component.HIcon
import com.paulcoding.hviewer.ui.component.HImage
@@ -59,7 +63,7 @@ fun PostsPage(
goBack: () -> Unit
) {
val appState by appViewModel.stateFlow.collectAsState()
val siteConfig = appState.siteConfig
val siteConfig = appState.site.second

val listTopic = siteConfig.tags.keys.toList()
val pagerState = rememberPagerState { listTopic.size }
@@ -103,6 +107,7 @@ fun PostsPage(
) { pageIndex ->
val page = listTopic[pageIndex]
PageContent(
appViewModel,
siteConfig,
page,
onPageChange = { currentPage, total ->
@@ -117,11 +122,13 @@ fun PostsPage(

@Composable
fun PageContent(
appViewModel: AppViewModel,
siteConfig: SiteConfig,
topic: String,
onPageChange: (Int, Int) -> Unit,
onClick: (PostItem) -> Unit
) {
val listFavorite by appViewModel.favoritePosts.collectAsState(initial = emptyList())
val viewModel: PostsViewModel = viewModel(
factory = PostsViewModelFactory(siteConfig, topic),
key = topic
@@ -154,7 +161,16 @@ fun PageContent(
state = listState
) {
items(uiState.postItems) { post ->
PostCard(post) {
PostCard(
post,
isFavorite = listFavorite.find { it.url == post.url } != null,
setFavorite = { isFavorite ->
if (isFavorite)
appViewModel.addFavorite(post)
else
appViewModel.deleteFavorite(post)
}
) {
onClick(post)
}
}
@@ -177,22 +193,38 @@ fun PageContent(
}

@Composable
fun PostCard(postItem: PostItem, viewPost: () -> Unit) {
fun PostCard(
postItem: PostItem,
isFavorite: Boolean = false,
setFavorite: (Boolean) -> Unit = {},
viewPost: () -> Unit
) {
Card(
elevation = CardDefaults.cardElevation(8.dp),
modifier = Modifier
.padding(horizontal = 16.dp, vertical = 12.dp),
.padding(horizontal = 16.dp, vertical = 12.dp)
.animateContentSize(
animationSpec = tween(durationMillis = 300)
),
shape = MaterialTheme.shapes.medium,
) {
Column(modifier = Modifier
.padding(8.dp)
.clickable {
viewPost()
}) {
HImage(
url = postItem.thumbnail
)
Text(postItem.name, fontSize = 12.sp)
Box(modifier = Modifier.fillMaxSize()) {
Column(modifier = Modifier
.padding(8.dp)
.clickable {
viewPost()
}) {
HImage(
url = postItem.thumbnail
)
Text(postItem.name, fontSize = 12.sp)
}
HFavoriteIcon(
modifier = Modifier.align(Alignment.TopEnd),
isFavorite = isFavorite
) {
setFavorite(!isFavorite)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@ fun SearchPage(
goBack: () -> Unit,
) {
val appState by appViewModel.stateFlow.collectAsState()
val siteConfig = appState.siteConfig
val siteConfig = appState.site.second

val viewModel: SearchViewModel = viewModel(
factory = SearchViewModelFactory(siteConfig),
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@ import androidx.compose.ui.unit.dp
import com.paulcoding.hviewer.model.SiteConfig
import com.paulcoding.hviewer.model.SiteConfigs
import com.paulcoding.hviewer.ui.component.HEmpty
import com.paulcoding.hviewer.ui.component.HFavoriteIcon
import com.paulcoding.hviewer.ui.icon.SettingsIcon
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -42,6 +43,7 @@ fun SitesPage(
siteConfigs: SiteConfigs,
navToSettings: () -> Unit,
refresh: () -> Unit,
navToFavorite: () -> Unit,
) {
val state = rememberPullToRefreshState()

@@ -53,6 +55,9 @@ fun SitesPage(

Scaffold(topBar = {
TopAppBar(title = { Text("Sites") }, actions = {
HFavoriteIcon(isFavorite = false) {
navToFavorite()
}
IconButton(onClick = navToSettings) {
Icon(SettingsIcon, "Settings")
}
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -3,4 +3,5 @@ plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.compose) apply false
id("com.google.devtools.ksp") version "2.0.21-1.0.27" apply false
}
4 changes: 4 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@ lottieCompose = "6.6.0"
mmkv = "2.0.0"
navigationCompose = "2.8.4"
rhino = "1.7.15"
roomRuntime = "2.6.1"
zoomable = "0.14.0"
material3 = "1.3.1"

@@ -35,6 +36,9 @@ androidx-concurrent-futures-ktx = { module = "androidx.concurrent:concurrent-fut
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-javascriptengine = { module = "androidx.javascriptengine:javascriptengine", version.ref = "javascriptengine" }
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomRuntime" }
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomRuntime" }
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" }
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coilNetworkOkhttp" }
coil-gif = { module = "io.coil-kt.coil3:coil-gif", version.ref = "coilNetworkOkhttp" }
coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coilNetworkOkhttp" }