From e61df9d02e9f1b4ed08eba13cafd14c1cfac3938 Mon Sep 17 00:00:00 2001 From: Stefano Sansone Date: Wed, 21 Feb 2024 00:01:02 +0000 Subject: [PATCH] add saved item flow in library view model --- .../omnivore/core/data/DataService.kt | 6 +-- .../{AppDatabase.kt => OmnivoreDatabase.kt} | 39 +++++++++---------- .../core/database/dao/SavedItemDao.kt | 1 - .../core/database/entities/Highlight.kt | 15 ++++++- .../app/omnivore/omnivore/di/AppModule.kt | 8 ++-- .../app/omnivore/omnivore/di/DaosModule.kt | 7 ++-- .../omnivore/omnivore/di/DatabaseModule.kt | 6 +-- .../omnivore/feature/library/LibraryView.kt | 14 ++++--- .../feature/library/LibraryViewModel.kt | 34 +++++++++++----- 9 files changed, 81 insertions(+), 49 deletions(-) rename android/Omnivore/app/src/main/java/app/omnivore/omnivore/core/database/{AppDatabase.kt => OmnivoreDatabase.kt} (58%) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/core/data/DataService.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/core/data/DataService.kt index f21c742de..3ff92541d 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/core/data/DataService.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/core/data/DataService.kt @@ -1,6 +1,6 @@ package app.omnivore.omnivore.core.data -import app.omnivore.omnivore.core.database.AppDatabase +import app.omnivore.omnivore.core.database.OmnivoreDatabase import app.omnivore.omnivore.core.database.entities.SavedItem import app.omnivore.omnivore.core.network.Networker import kotlinx.coroutines.CoroutineScope @@ -11,11 +11,11 @@ import javax.inject.Inject class DataService @Inject constructor( val networker: Networker, - appDatabase: AppDatabase + omnivoreDatabase: OmnivoreDatabase ) { val savedItemSyncChannel = Channel(capacity = Channel.UNLIMITED) - val db = appDatabase + val db = omnivoreDatabase init { CoroutineScope(Dispatchers.IO).launch { diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/core/database/AppDatabase.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/core/database/OmnivoreDatabase.kt similarity index 58% rename from android/Omnivore/app/src/main/java/app/omnivore/omnivore/core/database/AppDatabase.kt rename to android/Omnivore/app/src/main/java/app/omnivore/omnivore/core/database/OmnivoreDatabase.kt index 031e39686..cb8db0c08 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/core/database/AppDatabase.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/core/database/OmnivoreDatabase.kt @@ -19,25 +19,24 @@ import app.omnivore.omnivore.core.database.entities.Viewer import app.omnivore.omnivore.core.database.entities.ViewerDao @Database( - entities = [ - Viewer::class, - SavedItem::class, - SavedItemLabel::class, - Highlight::class, - HighlightChange::class, - SavedItemAndSavedItemLabelCrossRef::class, - SavedItemAndHighlightCrossRef::class - ], - version = 24, - exportSchema = false + entities = [ + Viewer::class, + SavedItem::class, + SavedItemLabel::class, + Highlight::class, + HighlightChange::class, + SavedItemAndSavedItemLabelCrossRef::class, + SavedItemAndHighlightCrossRef::class], + version = 24, + exportSchema = true ) -abstract class AppDatabase : RoomDatabase() { - abstract fun viewerDao(): ViewerDao - abstract fun savedItemDao(): SavedItemDao - abstract fun highlightDao(): HighlightDao - abstract fun highlightChangesDao(): HighlightChangesDao - abstract fun savedItemLabelDao(): SavedItemLabelDao - abstract fun savedItemWithLabelsAndHighlightsDao(): SavedItemWithLabelsAndHighlightsDao - abstract fun savedItemAndSavedItemLabelCrossRefDao(): SavedItemAndSavedItemLabelCrossRefDao - abstract fun savedItemAndHighlightCrossRefDao(): SavedItemAndHighlightCrossRefDao +abstract class OmnivoreDatabase : RoomDatabase() { + abstract fun viewerDao(): ViewerDao + abstract fun savedItemDao(): SavedItemDao + abstract fun highlightDao(): HighlightDao + abstract fun highlightChangesDao(): HighlightChangesDao + abstract fun savedItemLabelDao(): SavedItemLabelDao + abstract fun savedItemWithLabelsAndHighlightsDao(): SavedItemWithLabelsAndHighlightsDao + abstract fun savedItemAndSavedItemLabelCrossRefDao(): SavedItemAndSavedItemLabelCrossRefDao + abstract fun savedItemAndHighlightCrossRefDao(): SavedItemAndHighlightCrossRefDao } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/core/database/dao/SavedItemDao.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/core/database/dao/SavedItemDao.kt index 67eecba8d..3e26960e0 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/core/database/dao/SavedItemDao.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/core/database/dao/SavedItemDao.kt @@ -13,7 +13,6 @@ import kotlinx.coroutines.flow.Flow @Dao interface SavedItemDao { - @Transaction @Query("SELECT * FROM savedItem") fun getAll(): Flow> diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/core/database/entities/Highlight.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/core/database/entities/Highlight.kt index 2086cebe9..50c078ab0 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/core/database/entities/Highlight.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/core/database/entities/Highlight.kt @@ -73,7 +73,20 @@ data class SavedItemWithLabelsAndHighlights( associateBy = Junction(SavedItemAndHighlightCrossRef::class) ) val highlights: List -) +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as SavedItemWithLabelsAndHighlights + + return savedItem.savedItemId == other.savedItem.savedItemId + } + + override fun hashCode(): Int { + return savedItem.savedItemId.hashCode() + } +} @Dao interface HighlightDao { diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/di/AppModule.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/di/AppModule.kt index a4d178eca..3f4b315e2 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/di/AppModule.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/di/AppModule.kt @@ -3,7 +3,7 @@ package app.omnivore.omnivore.di import android.content.Context import app.omnivore.omnivore.core.analytics.EventTracker import app.omnivore.omnivore.core.data.DataService -import app.omnivore.omnivore.core.database.AppDatabase +import app.omnivore.omnivore.core.database.OmnivoreDatabase import app.omnivore.omnivore.core.datastore.DatastoreRepository import app.omnivore.omnivore.core.datastore.OmnivoreDatastore import app.omnivore.omnivore.core.network.Networker @@ -35,8 +35,8 @@ object AppModule { @Singleton @Provides fun provideDataService( - networker: Networker, - appDatabase: AppDatabase - ) = DataService(networker, appDatabase) + networker: Networker, + omnivoreDatabase: OmnivoreDatabase + ) = DataService(networker, omnivoreDatabase) } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/di/DaosModule.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/di/DaosModule.kt index 28d5f3422..f2175bb1f 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/di/DaosModule.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/di/DaosModule.kt @@ -1,6 +1,6 @@ package app.omnivore.omnivore.di -import app.omnivore.omnivore.core.database.AppDatabase +import app.omnivore.omnivore.core.database.OmnivoreDatabase import app.omnivore.omnivore.core.database.dao.SavedItemDao import dagger.Module import dagger.Provides @@ -10,8 +10,9 @@ import dagger.hilt.components.SingletonComponent @Module @InstallIn(SingletonComponent::class) object DaosModule { + @Provides - fun providesTopicsDao( - database: AppDatabase, + fun providesSavedItemDao( + database: OmnivoreDatabase, ): SavedItemDao = database.savedItemDao() } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/di/DatabaseModule.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/di/DatabaseModule.kt index e0433c1b5..4b77f3267 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/di/DatabaseModule.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/di/DatabaseModule.kt @@ -2,7 +2,7 @@ package app.omnivore.omnivore.di import android.content.Context import androidx.room.Room -import app.omnivore.omnivore.core.database.AppDatabase +import app.omnivore.omnivore.core.database.OmnivoreDatabase import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -17,9 +17,9 @@ object DatabaseModule { @Singleton fun providesOmnivoreDatabase( @ApplicationContext context: Context, - ): AppDatabase = Room.databaseBuilder( + ): OmnivoreDatabase = Room.databaseBuilder( context, - AppDatabase::class.java, + OmnivoreDatabase::class.java, "omnivore-database", ).build() } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/library/LibraryView.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/library/LibraryView.kt index 70728d4d5..a654b4bef 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/library/LibraryView.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/library/LibraryView.kt @@ -20,7 +20,6 @@ import androidx.compose.material.DismissValue import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.FractionalThreshold import androidx.compose.material.Icon -import androidx.compose.material.Scaffold import androidx.compose.material.ScaffoldState import androidx.compose.material.SwipeToDismiss import androidx.compose.material.icons.Icons @@ -36,6 +35,7 @@ import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.Scaffold import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -45,6 +45,7 @@ import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -127,7 +128,6 @@ internal fun LibraryView( } Scaffold( - scaffoldState = scaffoldState, topBar = { LibraryNavigationBar( savedItemViewModel = viewModel, @@ -334,8 +334,7 @@ fun LibraryViewContent( val swipeThreshold = 0.45f val currentThresholdFraction = remember { mutableStateOf(0f) } - //val currentItem by rememberUpdatedState(cardDataWithLabels.savedItem) - val currentItem = cardDataWithLabels.savedItem + val currentItem by rememberUpdatedState(cardDataWithLabels.savedItem) val swipeState = rememberDismissState( confirmStateChange = { when(it) { @@ -412,10 +411,15 @@ fun LibraryViewContent( dismissContent = { val selected = currentItem.savedItemId == selectedItem?.savedItem?.savedItemId + val test = SavedItemWithLabelsAndHighlights( + savedItem = cardDataWithLabels.savedItem, + labels = listOf(), + highlights = listOf() + ) SavedItemCard( selected = selected, savedItemViewModel = libraryViewModel, - savedItem = cardDataWithLabels, + savedItem = test, onClickHandler = { libraryViewModel.actionsMenuItemLiveData.postValue(null) val activityClass = diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/library/LibraryViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/library/LibraryViewModel.kt index 68f0e2a36..c42f30cd4 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/library/LibraryViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/library/LibraryViewModel.kt @@ -33,22 +33,16 @@ import com.apollographql.apollo3.api.Optional import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import java.time.Instant import javax.inject.Inject -@OptIn(ExperimentalCoroutinesApi::class) @HiltViewModel class LibraryViewModel @Inject constructor( private val networker: Networker, @@ -75,16 +69,33 @@ class LibraryViewModel @Inject constructor( ) ) - val uiState: StateFlow = _libraryQuery.flatMapLatest { query -> + // Correct way - but not working +/* val uiState: StateFlow = _libraryQuery.flatMapLatest { query -> libraryRepository.getSavedItems(query) } .map(LibraryUiState::Success) .stateIn( scope = viewModelScope, - started = SharingStarted.Eagerly, + started = SharingStarted.Lazily, initialValue = LibraryUiState.Loading - ) + )*/ + // This approach needs to be replaced with the StateFlow above after fixing Room Flow + private val _uiState = MutableStateFlow(LibraryUiState.Loading) + val uiState: StateFlow = _uiState + + init { + loadSavedItems() + } + + private fun loadSavedItems() { + viewModelScope.launch { + libraryRepository.getSavedItems(_libraryQuery.value) + .collect { favoriteNews -> + _uiState.value = LibraryUiState.Success(favoriteNews) + } + } + } private val itemsLiveData = MediatorLiveData>() val appliedFilterLiveData = MutableLiveData(SavedItemFilter.INBOX) @@ -259,6 +270,7 @@ class LibraryViewModel @Inject constructor( excludedLabels = excludeLabels, allowedContentReaders = allowedContentReaders ) + loadSavedItems() } } @@ -338,13 +350,17 @@ class LibraryViewModel @Inject constructor( SavedItemAction.MarkRead -> { viewModelScope.launch { + _uiState.value = LibraryUiState.Success(emptyList()) libraryRepository.updateReadingProgress(itemID, 100.0, 0) + loadSavedItems() } } SavedItemAction.MarkUnread -> { viewModelScope.launch { + _uiState.value = LibraryUiState.Success(emptyList()) libraryRepository.updateReadingProgress(itemID, 0.0, 0) + loadSavedItems() } } }