From c38e95d8b0fa8796f9786be9a538cd562e05fb53 Mon Sep 17 00:00:00 2001 From: Florent CHAMPIGNY Date: Sun, 12 Oct 2025 21:30:40 +0200 Subject: [PATCH] feat: [DATABASE] display previous queries (#333) Co-authored-by: Florent Champigny --- .../features/database/DatabaseTabViewModel.kt | 23 ++++++- .../database/view/DatabaseQueryToolbarView.kt | 61 +++++++++++++++---- .../database/view/DatabaseQueryView.kt | 5 +- .../features/database/view/DatabaseTab.kt | 6 +- .../ObserveLastSuccessQueriesUseCase.kt | 3 +- 5 files changed, 81 insertions(+), 17 deletions(-) diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseTabViewModel.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseTabViewModel.kt index f7360541..ca925d88 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseTabViewModel.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseTabViewModel.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.viewModelScope import io.github.openflocon.domain.common.DispatcherProvider import io.github.openflocon.domain.common.combines import io.github.openflocon.domain.database.usecase.ExecuteDatabaseQueryUseCase +import io.github.openflocon.domain.database.usecase.ObserveLastSuccessQueriesUseCase import io.github.openflocon.domain.database.usecase.favorite.GetFavoriteQueryByIdDatabaseUseCase import io.github.openflocon.domain.database.usecase.favorite.SaveQueryAsFavoriteDatabaseUseCase import io.github.openflocon.domain.feedback.FeedbackDisplayer @@ -14,7 +15,6 @@ import io.github.openflocon.flocondesktop.features.database.mapper.toUi import io.github.openflocon.flocondesktop.features.database.model.DatabaseScreenState import io.github.openflocon.flocondesktop.features.database.model.DatabaseTabAction import io.github.openflocon.flocondesktop.features.database.model.QueryResultUiModel -import io.github.openflocon.flocondesktop.features.network.list.model.NetworkAction import io.github.openflocon.library.designsystem.common.copyToClipboard import kotlinx.coroutines.Job import kotlinx.coroutines.delay @@ -38,6 +38,7 @@ class DatabaseTabViewModel( private val getFavoriteQueryUseCase: GetFavoriteQueryByIdDatabaseUseCase, private val dispatcherProvider: DispatcherProvider, private val feedbackDisplayer: FeedbackDisplayer, + private val observeLastSuccessQueriesUseCase: ObserveLastSuccessQueriesUseCase, ) : ViewModel() { @Immutable @@ -49,6 +50,15 @@ class DatabaseTabViewModel( var query = mutableStateOf("") + val lastQueries = observeLastSuccessQueriesUseCase(params.databaseId) + .map { it.filterNot { it.isBlank() } } + .flowOn(dispatcherProvider.data) + .stateIn( + viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + emptyList() + ) + private val autoUpdateJob = AtomicReference(null) data class AutoUpdate( @@ -119,12 +129,21 @@ class DatabaseTabViewModel( viewModelScope.launch(dispatcherProvider.viewModel) { when (action) { is DatabaseTabAction.ClearQuery -> clearQuery() - is DatabaseTabAction.ExecuteQuery -> executeQuery(query = query.value, editAutoUpdate = true) + is DatabaseTabAction.ExecuteQuery -> { + query.value = action.query + updateQuery(action.query) + executeQuery( + query = action.query, + editAutoUpdate = true + ) + } + is DatabaseTabAction.UpdateAutoUpdate -> updateAutoUpdate(action.value) DatabaseTabAction.Copy -> { copyToClipboard(query.value) feedbackDisplayer.displayMessage("copied") } + DatabaseTabAction.Import -> { // TODO } diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseQueryToolbarView.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseQueryToolbarView.kt index b1f61c76..ffa87d90 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseQueryToolbarView.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseQueryToolbarView.kt @@ -9,12 +9,15 @@ import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CopyAll import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.StarBorder import androidx.compose.material.icons.outlined.History +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Text import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -27,14 +30,19 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.unit.dp +import androidx.compose.ui.util.fastForEach import io.github.openflocon.flocondesktop.features.database.model.DatabaseTabAction import io.github.openflocon.library.designsystem.FloconTheme +import io.github.openflocon.library.designsystem.components.FloconExposedDropdownMenu +import io.github.openflocon.library.designsystem.components.FloconExposedDropdownMenuBox +@OptIn(ExperimentalMaterial3Api::class) @Composable internal fun DatabaseQueryToolbarView( favoritesTitles: Set, onAction: (action: DatabaseTabAction) -> Unit, isQueryEmpty: Boolean, + lastQueries: List, modifier: Modifier = Modifier, ) { var showFavoriteDialog by remember { mutableStateOf(false) } @@ -62,6 +70,7 @@ internal fun DatabaseQueryToolbarView( ) } VerticalDivider(modifier = Modifier.padding(vertical = 6.dp, horizontal = 2.dp)) + Box( modifier = Modifier.clip(RoundedCornerShape(2.dp)) .clickable(enabled = isQueryEmpty.not()) { @@ -79,19 +88,47 @@ internal fun DatabaseQueryToolbarView( colorFilter = ColorFilter.tint(FloconTheme.colorPalette.onPrimary) ) } - VerticalDivider(modifier = Modifier.padding(vertical = 6.dp, horizontal = 2.dp)) - Box( - modifier = Modifier.clip(RoundedCornerShape(2.dp)).clickable { - }.aspectRatio(1f, true), - contentAlignment = Alignment.Center + VerticalDivider(modifier = Modifier.padding(vertical = 6.dp, horizontal = 2.dp)) + + var isHistoryExpanded by remember { mutableStateOf(false) } + val displayOldQueries = isHistoryExpanded && lastQueries.isNotEmpty() + + FloconExposedDropdownMenuBox( + expanded = displayOldQueries, + onExpandedChange = { isHistoryExpanded = false }, ) { - Image( - Icons.Outlined.History, - contentDescription = null, - modifier = Modifier.size(20.dp), - colorFilter = ColorFilter.tint(FloconTheme.colorPalette.onPrimary) - ) + Box( + modifier = Modifier.clip(RoundedCornerShape(2.dp)) + .clickable(enabled = lastQueries.isNotEmpty()) { + isHistoryExpanded = true + }.aspectRatio(1f, true), + contentAlignment = Alignment.Center + ) { + Image( + Icons.Outlined.History, + contentDescription = null, + modifier = Modifier.size(20.dp), + colorFilter = ColorFilter.tint(FloconTheme.colorPalette.onPrimary) + ) + } + + FloconExposedDropdownMenu( + expanded = displayOldQueries, + onDismissRequest = { isHistoryExpanded = false }, + modifier = Modifier.width(300.dp) + ) { + lastQueries.fastForEach { query -> + Text( + modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp).clickable { + onAction(DatabaseTabAction.ExecuteQuery(query)) + isHistoryExpanded = false + }, + text = query, + style = FloconTheme.typography.bodySmall, + ) + } + } } /* for another MR @@ -138,4 +175,4 @@ internal fun DatabaseQueryToolbarView( } ) } -} \ No newline at end of file +} diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseQueryView.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseQueryView.kt index 02d41e4a..7b7d7910 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseQueryView.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseQueryView.kt @@ -40,6 +40,7 @@ fun DatabaseQueryView( query: String, autoUpdate: Boolean, favoritesTitles: Set, + lastQueries: List, updateQuery: (query: String) -> Unit, onAction: (action: DatabaseTabAction) -> Unit, modifier: Modifier = Modifier, @@ -54,6 +55,7 @@ fun DatabaseQueryView( DatabaseQueryToolbarView( favoritesTitles = favoritesTitles, onAction = onAction, + lastQueries = lastQueries, modifier = Modifier.fillMaxWidth(), isQueryEmpty = query.isBlank(), ) @@ -148,7 +150,8 @@ private fun DatabaseQueryViewPreview() { updateQuery = {}, autoUpdate = true, onAction = {}, - favoritesTitles = emptySet() + favoritesTitles = emptySet(), + lastQueries = emptyList(), ) } } diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseTab.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseTab.kt index c6bbaab9..b859371e 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseTab.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseTab.kt @@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -44,12 +43,15 @@ fun DatabaseTabView( val state: DatabaseScreenState by viewModel.state.collectAsStateWithLifecycle() val autoUpdate by viewModel.isAutoUpdateEnabled.collectAsStateWithLifecycle() + val lastQueries by viewModel.lastQueries.collectAsStateWithLifecycle() + DatabaseTabViewContent( query = viewModel.query.value, autoUpdate = autoUpdate, updateQuery = viewModel::updateQuery, onAction = viewModel::onAction, state = state, + lastQueries = lastQueries, favoritesTitles = favoritesTitles, ) } @@ -62,6 +64,7 @@ private fun DatabaseTabViewContent( updateQuery: (String) -> Unit, onAction: (action: DatabaseTabAction) -> Unit, state: DatabaseScreenState, + lastQueries: List, ) { Column( Modifier.fillMaxSize(), @@ -76,6 +79,7 @@ private fun DatabaseTabViewContent( autoUpdate = autoUpdate, onAction = onAction, favoritesTitles = favoritesTitles, + lastQueries = lastQueries, modifier = Modifier .fillMaxWidth() ) diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/ObserveLastSuccessQueriesUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/ObserveLastSuccessQueriesUseCase.kt index e64637aa..936d1c54 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/ObserveLastSuccessQueriesUseCase.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/ObserveLastSuccessQueriesUseCase.kt @@ -2,6 +2,7 @@ package io.github.openflocon.domain.database.usecase import io.github.openflocon.domain.device.usecase.ObserveCurrentDeviceIdAndPackageNameUseCase import io.github.openflocon.domain.database.repository.DatabaseRepository +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf @@ -11,7 +12,7 @@ class ObserveLastSuccessQueriesUseCase( ) { operator fun invoke( databaseId: String, - ) = observeCurrentDeviceIdAndPackageNameUseCase().flatMapLatest { model -> + ): Flow> = observeCurrentDeviceIdAndPackageNameUseCase().flatMapLatest { model -> if (model == null) { flowOf(emptyList()) } else {