mirror of
https://github.com/msfjarvis/compose-lobsters
synced 2025-08-18 07:57:03 +05:30
Add saved lists feature
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
This commit is contained in:
parent
3bd3f2dff8
commit
d6d82248a8
8 changed files with 185 additions and 39 deletions
|
@ -3,18 +3,34 @@ package dev.msfjarvis.lobsters
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.compose.animation.animate
|
||||||
|
import androidx.compose.foundation.Icon
|
||||||
import androidx.compose.foundation.Text
|
import androidx.compose.foundation.Text
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.offset
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumnForIndexed
|
import androidx.compose.foundation.lazy.LazyColumnForIndexed
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.material.FloatingActionButton
|
import androidx.compose.material.FloatingActionButton
|
||||||
|
import androidx.compose.material.IconToggleButton
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.Scaffold
|
import androidx.compose.material.Scaffold
|
||||||
|
import androidx.compose.material.TopAppBar
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Favorite
|
||||||
|
import androidx.compose.material.icons.filled.FavoriteBorder
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.Providers
|
import androidx.compose.runtime.Providers
|
||||||
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.ambientOf
|
import androidx.compose.runtime.ambientOf
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.setContent
|
import androidx.compose.ui.platform.setContent
|
||||||
|
@ -23,8 +39,10 @@ import androidx.compose.ui.unit.dp
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import dev.msfjarvis.lobsters.compose.utils.IconResource
|
import dev.msfjarvis.lobsters.compose.utils.IconResource
|
||||||
import dev.msfjarvis.lobsters.data.LobstersViewModel
|
import dev.msfjarvis.lobsters.data.LobstersViewModel
|
||||||
|
import dev.msfjarvis.lobsters.model.LobstersPost
|
||||||
import dev.msfjarvis.lobsters.ui.LobstersItem
|
import dev.msfjarvis.lobsters.ui.LobstersItem
|
||||||
import dev.msfjarvis.lobsters.ui.LobstersTheme
|
import dev.msfjarvis.lobsters.ui.LobstersTheme
|
||||||
|
import dev.msfjarvis.lobsters.ui.savedTitleColor
|
||||||
import dev.msfjarvis.lobsters.urllauncher.UrlLauncher
|
import dev.msfjarvis.lobsters.urllauncher.UrlLauncher
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -52,40 +70,111 @@ fun LobstersApp(
|
||||||
viewModel: LobstersViewModel
|
viewModel: LobstersViewModel
|
||||||
) {
|
) {
|
||||||
val urlLauncher = UrlLauncherAmbient.current
|
val urlLauncher = UrlLauncherAmbient.current
|
||||||
val state = viewModel.posts.collectAsState()
|
val hottestPostsState = viewModel.posts.collectAsState()
|
||||||
val lastIndex = state.value.lastIndex
|
val savedPostsState = viewModel.savedPosts.collectAsState()
|
||||||
|
val lastIndex = hottestPostsState.value.lastIndex
|
||||||
|
val showSaved = remember { mutableStateOf(false) }
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
|
topBar = { LobsterTopAppbar(showSaved) },
|
||||||
bodyContent = {
|
bodyContent = {
|
||||||
if (state.value.isEmpty()) {
|
if ((!showSaved.value && hottestPostsState.value.isEmpty()) || (showSaved.value && savedPostsState.value.isEmpty())) {
|
||||||
Column(
|
LobsterEmptyList(showSaved)
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
verticalArrangement = Arrangement.Center,
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
) {
|
|
||||||
IconResource(R.drawable.ic_sync_problem_24px)
|
|
||||||
Text(stringResource(R.string.loading))
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
LazyColumnForIndexed(
|
LobsterList(
|
||||||
items = state.value,
|
showSaved,
|
||||||
modifier = Modifier.padding(horizontal = 8.dp)
|
savedPostsState,
|
||||||
) { index, item ->
|
hottestPostsState,
|
||||||
if (lastIndex == index) {
|
lastIndex,
|
||||||
viewModel.getMorePosts()
|
viewModel,
|
||||||
}
|
urlLauncher
|
||||||
LobstersItem(
|
)
|
||||||
item,
|
|
||||||
linkOpenAction = { post -> urlLauncher.launch(post.url.ifEmpty { post.commentsUrl }) },
|
|
||||||
commentOpenAction = { post -> urlLauncher.launch(post.commentsUrl) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
floatingActionButton = {
|
|
||||||
FloatingActionButton(onClick = { viewModel.refreshPosts() }) {
|
|
||||||
IconResource(resourceId = R.drawable.ic_refresh_24px)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
floatingActionButton = { LobsterFAB(showSaved, viewModel) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun LobsterFAB(
|
||||||
|
showSaved: MutableState<Boolean>,
|
||||||
|
viewModel: LobstersViewModel
|
||||||
|
) {
|
||||||
|
val animatedYOffset = animate(if (showSaved.value) 100.dp else 0.dp)
|
||||||
|
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = { viewModel.refreshPosts() },
|
||||||
|
modifier = Modifier.offset(y = animatedYOffset)
|
||||||
|
) {
|
||||||
|
IconResource(resourceId = R.drawable.ic_refresh_24px)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun LobsterList(
|
||||||
|
showSaved: MutableState<Boolean>,
|
||||||
|
savedPostsState: State<List<LobstersPost>>,
|
||||||
|
hottestPostsState: State<List<LobstersPost>>,
|
||||||
|
lastIndex: Int,
|
||||||
|
viewModel: LobstersViewModel,
|
||||||
|
urlLauncher: UrlLauncher
|
||||||
|
) {
|
||||||
|
val hottestPostsListState = rememberLazyListState()
|
||||||
|
val savedPostsListState = rememberLazyListState()
|
||||||
|
|
||||||
|
LazyColumnForIndexed(
|
||||||
|
items = if (showSaved.value) savedPostsState.value else hottestPostsState.value,
|
||||||
|
state = if (showSaved.value) savedPostsListState else hottestPostsListState,
|
||||||
|
modifier = Modifier.padding(horizontal = 8.dp)
|
||||||
|
) { index, item ->
|
||||||
|
if (lastIndex == index && !showSaved.value) {
|
||||||
|
viewModel.getMorePosts()
|
||||||
|
}
|
||||||
|
LobstersItem(
|
||||||
|
item,
|
||||||
|
linkOpenAction = { post -> urlLauncher.launch(post.url.ifEmpty { post.commentsUrl }) },
|
||||||
|
commentOpenAction = { post -> urlLauncher.launch(post.commentsUrl) },
|
||||||
|
saveAction = { post -> if (!showSaved.value) viewModel.savePost(post) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun LobsterEmptyList(showSaved: MutableState<Boolean>) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
if (!showSaved.value) {
|
||||||
|
IconResource(R.drawable.ic_sync_problem_24px, modifier = Modifier.padding(16.dp))
|
||||||
|
Text(stringResource(R.string.loading))
|
||||||
|
} else {
|
||||||
|
Icon(Icons.Default.FavoriteBorder, tint = savedTitleColor, modifier = Modifier.padding(16.dp))
|
||||||
|
Text(stringResource(R.string.no_saved_posts))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun LobsterTopAppbar(showSaved: MutableState<Boolean>) {
|
||||||
|
TopAppBar {
|
||||||
|
Box(modifier = Modifier.fillMaxWidth()) {
|
||||||
|
Text(
|
||||||
|
text = if (showSaved.value) "Saved" else "Home",
|
||||||
|
modifier = Modifier.padding(16.dp).align(Alignment.CenterStart),
|
||||||
|
style = MaterialTheme.typography.h6,
|
||||||
|
)
|
||||||
|
IconToggleButton(
|
||||||
|
checked = showSaved.value,
|
||||||
|
onCheckedChange = { showSaved.value = !showSaved.value },
|
||||||
|
modifier = Modifier.padding(8.dp).align(Alignment.CenterEnd),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
asset = if (showSaved.value) Icons.Default.Favorite else Icons.Default.FavoriteBorder,
|
||||||
|
tint = savedTitleColor,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.net.SocketTimeoutException
|
import java.net.SocketTimeoutException
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
|
@ -20,14 +21,16 @@ class LobstersViewModel @ViewModelInject constructor(
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private var apiPage = 1
|
private var apiPage = 1
|
||||||
private val _posts = MutableStateFlow<List<LobstersPost>>(emptyList())
|
private val _posts = MutableStateFlow<List<LobstersPost>>(emptyList())
|
||||||
private val dao = database.postsDao()
|
private val _savedPosts = MutableStateFlow<List<LobstersPost>>(emptyList())
|
||||||
|
private val postsDao = database.postsDao()
|
||||||
|
private val savedPostsDao = database.savedPostsDao()
|
||||||
private val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable ->
|
private val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable ->
|
||||||
when (throwable) {
|
when (throwable) {
|
||||||
// Swallow known network errors that can be recovered from.
|
// Swallow known network errors that can be recovered from.
|
||||||
is UnknownHostException, is SocketTimeoutException -> {
|
is UnknownHostException, is SocketTimeoutException -> {
|
||||||
if (_posts.value.isEmpty()) {
|
if (_posts.value.isEmpty()) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
dao.loadPosts().collectLatest { _posts.value = it }
|
postsDao.loadPosts().collectLatest { _posts.value = it }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,9 +38,17 @@ class LobstersViewModel @ViewModelInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val posts: StateFlow<List<LobstersPost>> get() = _posts
|
val posts: StateFlow<List<LobstersPost>> get() = _posts
|
||||||
|
val savedPosts: StateFlow<List<LobstersPost>> get() = _savedPosts
|
||||||
|
|
||||||
init {
|
init {
|
||||||
getMorePostsInternal(true)
|
getMorePostsInternal(true)
|
||||||
|
getSavedPosts()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSavedPosts() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
savedPostsDao.loadPosts().collectLatest { _savedPosts.value = it }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMorePosts() {
|
fun getMorePosts() {
|
||||||
|
@ -54,12 +65,24 @@ class LobstersViewModel @ViewModelInject constructor(
|
||||||
val newPosts = lobstersApi.getHottestPosts(apiPage)
|
val newPosts = lobstersApi.getHottestPosts(apiPage)
|
||||||
if (firstLoad) {
|
if (firstLoad) {
|
||||||
_posts.value = newPosts
|
_posts.value = newPosts
|
||||||
dao.deleteAllPosts()
|
postsDao.deleteAllPosts()
|
||||||
} else {
|
} else {
|
||||||
_posts.value += newPosts
|
_posts.value += newPosts
|
||||||
}
|
}
|
||||||
apiPage += 1
|
apiPage += 1
|
||||||
dao.insertPosts(*_posts.value.toTypedArray())
|
postsDao.insertPosts(*_posts.value.toTypedArray())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun savePost(post: LobstersPost) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val tempList = _posts.value
|
||||||
|
_posts.value = tempList.map {
|
||||||
|
if (it.shortId == post.shortId) it.isLiked = true
|
||||||
|
it
|
||||||
|
}
|
||||||
|
savedPostsDao.insertPosts(post)
|
||||||
|
getSavedPosts()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package dev.msfjarvis.lobsters.ui
|
package dev.msfjarvis.lobsters.ui
|
||||||
|
|
||||||
|
import androidx.compose.animation.animate
|
||||||
import androidx.compose.foundation.Text
|
import androidx.compose.foundation.Text
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
@ -13,6 +14,8 @@ import androidx.compose.foundation.lazy.LazyColumnFor
|
||||||
import androidx.compose.foundation.lazy.LazyItemScope
|
import androidx.compose.foundation.lazy.LazyItemScope
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
@ -31,18 +34,27 @@ fun LazyItemScope.LobstersItem(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
linkOpenAction: (LobstersPost) -> Unit,
|
linkOpenAction: (LobstersPost) -> Unit,
|
||||||
commentOpenAction: (LobstersPost) -> Unit,
|
commentOpenAction: (LobstersPost) -> Unit,
|
||||||
|
saveAction: (LobstersPost) -> Unit,
|
||||||
) {
|
) {
|
||||||
|
val liked = remember { mutableStateOf(false) }
|
||||||
|
val titleColor = if (post.isLiked || liked.value) savedTitleColor else titleColor
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillParentMaxWidth()
|
.fillParentMaxWidth()
|
||||||
.clickable(
|
.clickable(
|
||||||
onClick = { linkOpenAction.invoke(post) },
|
onClick = { linkOpenAction.invoke(post) },
|
||||||
onLongClick = { commentOpenAction.invoke(post) }
|
onLongClick = { commentOpenAction.invoke(post) },
|
||||||
|
onDoubleClick = {
|
||||||
|
post.isLiked = true
|
||||||
|
liked.value = true
|
||||||
|
saveAction.invoke(post)
|
||||||
|
},
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = post.title,
|
text = post.title,
|
||||||
color = Color(0xFF7395D9),
|
color = titleColor,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
modifier = Modifier.padding(top = 4.dp)
|
modifier = Modifier.padding(top = 4.dp)
|
||||||
)
|
)
|
||||||
|
@ -106,11 +118,15 @@ fun PreviewLobstersItem() {
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
),
|
),
|
||||||
listOf("openbsd")
|
listOf("openbsd"),
|
||||||
)
|
)
|
||||||
LobstersTheme {
|
LobstersTheme {
|
||||||
LazyColumnFor(items = listOf(post)) { item ->
|
LazyColumnFor(items = listOf(post)) { item ->
|
||||||
LobstersItem(post = item, linkOpenAction = {}, commentOpenAction = {})
|
LobstersItem(
|
||||||
|
post = item,
|
||||||
|
linkOpenAction = {},
|
||||||
|
commentOpenAction = {},
|
||||||
|
saveAction = {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,9 @@ import androidx.compose.material.darkColors
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
|
val titleColor = Color(0xFF7395D9)
|
||||||
|
val savedTitleColor = Color(0xFFD97373)
|
||||||
|
|
||||||
val darkColors = darkColors(
|
val darkColors = darkColors(
|
||||||
primary = Color.White,
|
primary = Color.White,
|
||||||
secondary = Color(0xFF6C0000),
|
secondary = Color(0xFF6C0000),
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">lobste.rs</string>
|
<string name="app_name">lobste.rs</string>
|
||||||
<string name="loading">Loading posts…</string>
|
<string name="loading">Loading posts…</string>
|
||||||
|
<string name="no_saved_posts">You don\'t have any saved posts</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -6,6 +6,7 @@ import androidx.room.Insert
|
||||||
import androidx.room.OnConflictStrategy
|
import androidx.room.OnConflictStrategy
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import androidx.room.Transaction
|
import androidx.room.Transaction
|
||||||
|
import androidx.room.Update
|
||||||
import dev.msfjarvis.lobsters.data.model.LobstersEntity
|
import dev.msfjarvis.lobsters.data.model.LobstersEntity
|
||||||
import dev.msfjarvis.lobsters.model.LobstersPost
|
import dev.msfjarvis.lobsters.model.LobstersPost
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
@ -15,6 +16,15 @@ abstract class PostsDao {
|
||||||
@Query("SELECT * FROM lobsters_posts")
|
@Query("SELECT * FROM lobsters_posts")
|
||||||
abstract fun loadPosts(): Flow<List<LobstersPost>>
|
abstract fun loadPosts(): Flow<List<LobstersPost>>
|
||||||
|
|
||||||
|
@Update
|
||||||
|
suspend fun updatePost(vararg posts: LobstersPost) {
|
||||||
|
updatePosts(posts.map { LobstersEntity(it) })
|
||||||
|
}
|
||||||
|
|
||||||
|
@Update(onConflict = OnConflictStrategy.IGNORE)
|
||||||
|
protected abstract suspend fun updatePosts(posts: List<LobstersEntity>)
|
||||||
|
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
open suspend fun insertPosts(vararg posts: LobstersPost) {
|
open suspend fun insertPosts(vararg posts: LobstersPost) {
|
||||||
insertPosts(posts.map { LobstersEntity(it) })
|
insertPosts(posts.map { LobstersEntity(it) })
|
||||||
|
|
|
@ -4,12 +4,14 @@ import androidx.room.Database
|
||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
import androidx.room.TypeConverters
|
import androidx.room.TypeConverters
|
||||||
import dev.msfjarvis.lobsters.data.model.LobstersEntity
|
import dev.msfjarvis.lobsters.data.model.LobstersEntity
|
||||||
|
import dev.msfjarvis.lobsters.data.model.SavedLobstersEntity
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
entities = [
|
entities = [
|
||||||
LobstersEntity::class,
|
LobstersEntity::class,
|
||||||
|
SavedLobstersEntity::class
|
||||||
],
|
],
|
||||||
version = 1,
|
version = 2,
|
||||||
exportSchema = false,
|
exportSchema = false,
|
||||||
)
|
)
|
||||||
@TypeConverters(
|
@TypeConverters(
|
||||||
|
@ -17,4 +19,5 @@ import dev.msfjarvis.lobsters.data.model.LobstersEntity
|
||||||
)
|
)
|
||||||
abstract class PostsDatabase : RoomDatabase() {
|
abstract class PostsDatabase : RoomDatabase() {
|
||||||
abstract fun postsDao(): PostsDao
|
abstract fun postsDao(): PostsDao
|
||||||
|
abstract fun savedPostsDao(): SavedPostsDao
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,5 +22,6 @@ class LobstersPost(
|
||||||
val commentsUrl: String,
|
val commentsUrl: String,
|
||||||
@Json(name = "submitter_user")
|
@Json(name = "submitter_user")
|
||||||
val submitterUser: Submitter,
|
val submitterUser: Submitter,
|
||||||
val tags: List<String>
|
val tags: List<String>,
|
||||||
|
var isLiked: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue