mirror of
https://github.com/openflocon/Flocon.git
synced 2026-05-06 05:18:41 +00:00
* feature: Add filters * fix: Use adbPath set * fix: Remove old method * fix: Remove state flow * featrure: Rework methods * feature: (start) Rework network screen * feature: Add molecule * feature: Migrate * fix: Merge * feature: Rework * feature: Filter methods * fix: Clean animation * feature: Add minimum size * fix: Remove molecule * fix: NetworkUiState default params * fix: Discussion * fix: Filter --------- Co-authored-by: Raphael Teyssandier <rteyssandier@sephora.fr>
This commit is contained in:
parent
6f9c6c7228
commit
72d3939858
22 changed files with 577 additions and 237 deletions
|
|
@ -22,6 +22,7 @@ kotlin {
|
|||
// Pour Kotlin 1.9+
|
||||
freeCompilerArgs.add("-opt-in=kotlin.time.ExperimentalTime")
|
||||
freeCompilerArgs.add("-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi")
|
||||
freeCompilerArgs.add("-Xcontext-parameters")
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalKotlinGradlePluginApi::class)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package io.github.openflocon.flocondesktop.common.ui
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
interface ViewModelEvent<E> {
|
||||
|
||||
val events: SharedFlow<E>
|
||||
|
||||
fun ViewModel.sendEvents(vararg event: E)
|
||||
|
||||
interface Event
|
||||
|
||||
}
|
||||
|
||||
class ViewModelEventImpl<E : ViewModelEvent.Event> : ViewModelEvent<E> {
|
||||
|
||||
private val _events = MutableSharedFlow<E>()
|
||||
override val events: SharedFlow<E> = _events.asSharedFlow()
|
||||
|
||||
override fun ViewModel.sendEvents(vararg event: E) {
|
||||
viewModelScope.launch {
|
||||
event.forEach { _events.emit(it) }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package io.github.openflocon.flocondesktop.features.network.ui
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
|
||||
@Immutable
|
||||
data class ContentUiState(
|
||||
val selectedRequestId: String?
|
||||
)
|
||||
|
||||
fun previewContentUiState() = ContentUiState(
|
||||
selectedRequestId = null
|
||||
)
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package io.github.openflocon.flocondesktop.features.network.ui
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.NetworkMethodUi
|
||||
|
||||
@Immutable
|
||||
data class FilterUiState(
|
||||
val query: String,
|
||||
val methods: List<NetworkMethodUi>
|
||||
)
|
||||
|
||||
fun previewFilterUiState() = FilterUiState(
|
||||
query = "",
|
||||
methods = emptyList()
|
||||
)
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package io.github.openflocon.flocondesktop.features.network.ui
|
||||
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.NetworkItemViewState
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.NetworkMethodUi
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.view.filters.MethodFilter
|
||||
|
||||
sealed interface NetworkAction {
|
||||
|
||||
data class SelectRequest(val id: String) : NetworkAction
|
||||
|
||||
data class CopyText(val text: String) : NetworkAction
|
||||
|
||||
data object ClosePanel : NetworkAction
|
||||
|
||||
data object Reset : NetworkAction
|
||||
|
||||
data class CopyUrl(val item: NetworkItemViewState) : NetworkAction
|
||||
|
||||
data class CopyCUrl(val item: NetworkItemViewState) : NetworkAction
|
||||
|
||||
data class Remove(val item: NetworkItemViewState) : NetworkAction
|
||||
|
||||
data class RemoveLinesAbove(val item: NetworkItemViewState) : NetworkAction
|
||||
|
||||
data class FilterQuery(val query: String) : NetworkAction
|
||||
|
||||
data class FilterMethod(val method: NetworkMethodUi, val add: Boolean) : NetworkAction
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package io.github.openflocon.flocondesktop.features.network.ui
|
||||
|
||||
import io.github.openflocon.flocondesktop.common.ui.ViewModelEvent
|
||||
|
||||
sealed interface NetworkEvent : ViewModelEvent.Event
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package io.github.openflocon.flocondesktop.features.network.ui
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.NetworkDetailViewState
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.NetworkItemViewState
|
||||
|
||||
@Immutable
|
||||
data class NetworkUiState(
|
||||
val items: List<NetworkItemViewState>,
|
||||
val contentState: ContentUiState,
|
||||
val detailState: NetworkDetailViewState?,
|
||||
val filterState: FilterUiState
|
||||
)
|
||||
|
||||
fun previewNetworkUiState() = NetworkUiState(
|
||||
items = emptyList(),
|
||||
detailState = null,
|
||||
contentState = previewContentUiState(),
|
||||
filterState = previewFilterUiState()
|
||||
)
|
||||
|
|
@ -15,10 +15,12 @@ import io.github.openflocon.flocondesktop.features.network.ui.mapper.toDetailUi
|
|||
import io.github.openflocon.flocondesktop.features.network.ui.mapper.toUi
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.NetworkDetailViewState
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.NetworkItemViewState
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.OnNetworkItemUserAction
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.NetworkMethodUi
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.view.filters.MethodFilter
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
|
|
@ -40,85 +42,158 @@ class NetworkViewModel(
|
|||
private val feedbackDisplayer: FeedbackDisplayer,
|
||||
) : ViewModel() {
|
||||
|
||||
val state: StateFlow<List<NetworkItemViewState>> =
|
||||
observeHttpRequestsUseCase()
|
||||
.map { list -> list.map { toUi(it) } }
|
||||
.flowOn(dispatcherProvider.viewModel)
|
||||
.stateIn(viewModelScope, started = SharingStarted.WhileSubscribed(5_000), emptyList())
|
||||
private val filterMethod = MethodFilter()
|
||||
|
||||
private val clickedRequestId = MutableStateFlow<String?>(null)
|
||||
private val contentState = MutableStateFlow(ContentUiState(selectedRequestId = null))
|
||||
private val filterUiState = MutableStateFlow(FilterUiState(query = "", methods = NetworkMethodUi.all()))
|
||||
|
||||
val detailState: StateFlow<NetworkDetailViewState?> =
|
||||
clickedRequestId
|
||||
private val detailState: StateFlow<NetworkDetailViewState?> =
|
||||
contentState.map { it.selectedRequestId }
|
||||
.flatMapLatest { id ->
|
||||
if (id == null) {
|
||||
flowOf(null)
|
||||
} else {
|
||||
observeHttpRequestsByIdUseCase(id)
|
||||
.distinctUntilChanged()
|
||||
.map {
|
||||
it?.let {
|
||||
toDetailUi(it)
|
||||
}
|
||||
}
|
||||
.map { it?.let { toDetailUi(it) } }
|
||||
}
|
||||
}
|
||||
.flowOn(dispatcherProvider.viewModel)
|
||||
.stateIn(viewModelScope, started = SharingStarted.WhileSubscribed(5_000), null)
|
||||
|
||||
fun onNetworkItemUserAction(action: OnNetworkItemUserAction) {
|
||||
viewModelScope.launch(dispatcherProvider.viewModel) {
|
||||
when (action) {
|
||||
is OnNetworkItemUserAction.CopyCUrl -> {
|
||||
val domainModel = observeHttpRequestsByIdUseCase(action.item.uuid).firstOrNull()
|
||||
?: return@launch
|
||||
val curl = generateCurlCommandUseCase(domainModel)
|
||||
copyToClipboard(curl)
|
||||
}
|
||||
private val filteredItems = combine(
|
||||
observeHttpRequestsUseCase().map { list -> list.map { toUi(it) } },
|
||||
filterUiState
|
||||
) { items, filterState ->
|
||||
filterItems(items, filterState)
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
||||
is OnNetworkItemUserAction.CopyUrl -> {
|
||||
val domainModel = observeHttpRequestsByIdUseCase(action.item.uuid).firstOrNull()
|
||||
?: return@launch
|
||||
copyToClipboard(domainModel.url)
|
||||
}
|
||||
val uiState = combine(
|
||||
filteredItems,
|
||||
contentState,
|
||||
detailState,
|
||||
filterUiState
|
||||
) { items, content, detail, filter ->
|
||||
NetworkUiState(
|
||||
items = items,
|
||||
contentState = content,
|
||||
detailState = detail,
|
||||
filterState = filter
|
||||
)
|
||||
}
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(5_000),
|
||||
initialValue = NetworkUiState(
|
||||
items = emptyList(),
|
||||
detailState = detailState.value,
|
||||
contentState = contentState.value,
|
||||
filterState = filterUiState.value
|
||||
)
|
||||
)
|
||||
|
||||
is OnNetworkItemUserAction.OnClicked -> {
|
||||
clickedRequestId.update {
|
||||
if (it == action.item.uuid) {
|
||||
null
|
||||
} else {
|
||||
action.item.uuid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is OnNetworkItemUserAction.Remove -> {
|
||||
removeHttpRequestUseCase(requestId = action.item.uuid)
|
||||
}
|
||||
|
||||
is OnNetworkItemUserAction.RemoveLinesAbove -> {
|
||||
removeHttpRequestsBeforeUseCase(requestId = action.item.uuid)
|
||||
}
|
||||
}
|
||||
fun onAction(action: NetworkAction) {
|
||||
when (action) {
|
||||
is NetworkAction.SelectRequest -> onSelectRequest(action)
|
||||
NetworkAction.ClosePanel -> onClosePanel()
|
||||
is NetworkAction.CopyText -> onCopyText(action)
|
||||
NetworkAction.Reset -> onReset()
|
||||
is NetworkAction.CopyCUrl -> onCopyCUrl(action)
|
||||
is NetworkAction.CopyUrl -> onCopyUrl(action)
|
||||
is NetworkAction.Remove -> onRemove(action)
|
||||
is NetworkAction.RemoveLinesAbove -> onRemoveLinesAbove(action)
|
||||
is NetworkAction.FilterQuery -> onFilterQuery(action)
|
||||
is NetworkAction.FilterMethod -> onFilterMethod(action)
|
||||
}
|
||||
}
|
||||
|
||||
fun onCopyText(text: String) {
|
||||
viewModelScope.launch(dispatcherProvider.viewModel) {
|
||||
copyToClipboard(text)
|
||||
feedbackDisplayer.displayMessage("copied")
|
||||
private fun onSelectRequest(action: NetworkAction.SelectRequest) {
|
||||
contentState.update { state ->
|
||||
state.copy(
|
||||
selectedRequestId = if (state.selectedRequestId == action.id) {
|
||||
null
|
||||
} else {
|
||||
action.id
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun closeDetailPanel() {
|
||||
viewModelScope.launch(dispatcherProvider.viewModel) {
|
||||
clickedRequestId.update { null }
|
||||
}
|
||||
private fun onClosePanel() {
|
||||
contentState.update { it.copy(selectedRequestId = null) }
|
||||
}
|
||||
|
||||
fun onReset() {
|
||||
private fun onCopyText(action: NetworkAction.CopyText) {
|
||||
copyToClipboard(action.text)
|
||||
feedbackDisplayer.displayMessage("copied")
|
||||
}
|
||||
|
||||
private fun onReset() {
|
||||
viewModelScope.launch(dispatcherProvider.viewModel) {
|
||||
resetCurrentDeviceHttpRequestsUseCase()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onCopyCUrl(action: NetworkAction.CopyCUrl) {
|
||||
viewModelScope.launch(dispatcherProvider.viewModel) {
|
||||
val domainModel = observeHttpRequestsByIdUseCase(action.item.uuid).firstOrNull()
|
||||
?: return@launch
|
||||
val curl = generateCurlCommandUseCase(domainModel)
|
||||
copyToClipboard(curl)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onCopyUrl(action: NetworkAction.CopyUrl) {
|
||||
viewModelScope.launch(dispatcherProvider.viewModel) {
|
||||
val domainModel = observeHttpRequestsByIdUseCase(action.item.uuid).firstOrNull()
|
||||
?: return@launch
|
||||
copyToClipboard(domainModel.url)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onRemove(action: NetworkAction.Remove) {
|
||||
viewModelScope.launch(dispatcherProvider.viewModel) {
|
||||
removeHttpRequestUseCase(requestId = action.item.uuid)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onRemoveLinesAbove(action: NetworkAction.RemoveLinesAbove) {
|
||||
viewModelScope.launch(dispatcherProvider.viewModel) {
|
||||
removeHttpRequestsBeforeUseCase(requestId = action.item.uuid)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onFilterQuery(action: NetworkAction.FilterQuery) {
|
||||
filterUiState.update { state ->
|
||||
state.copy(query = action.query)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onFilterMethod(action: NetworkAction.FilterMethod) {
|
||||
filterUiState.update { state ->
|
||||
state.copy(
|
||||
methods = if (action.add) {
|
||||
state.methods + action.method
|
||||
} else {
|
||||
state.methods - action.method
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun filterItems(
|
||||
items: List<NetworkItemViewState>,
|
||||
filterState: FilterUiState
|
||||
): List<NetworkItemViewState> {
|
||||
var filteredItems = items
|
||||
|
||||
if (filterState.query.isNotEmpty())
|
||||
filteredItems = filteredItems.filter { it.contains(filterState.query) }
|
||||
if (filterState.methods.isNotEmpty())
|
||||
filteredItems = filterMethod.filter(filterState, filteredItems)
|
||||
|
||||
return filteredItems
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ fun toTypeUi(networkRequest: FloconHttpRequestDomainModel): NetworkItemViewState
|
|||
val query = extractPath(networkRequest.url)
|
||||
NetworkItemViewState.NetworkTypeUi.Url(
|
||||
query = query,
|
||||
method = networkRequest.request.method
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ data class NetworkItemViewState(
|
|||
|
||||
@Immutable
|
||||
data class Url(
|
||||
val method: String,
|
||||
val query: String,
|
||||
) : NetworkTypeUi {
|
||||
override fun contains(text: String): Boolean = query.contains(text, ignoreCase = true)
|
||||
|
|
@ -59,6 +60,7 @@ fun previewNetworkItemViewState(): NetworkItemViewState = NetworkItemViewState(
|
|||
status = NetworkStatusUi("200", true),
|
||||
type = NetworkItemViewState.NetworkTypeUi.Url(
|
||||
query = "/search?q=test",
|
||||
method = "get"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ sealed interface NetworkMethodUi {
|
|||
override val text: String = "QUERY"
|
||||
override val icon = Res.drawable.graphql
|
||||
}
|
||||
|
||||
data object MUTATION : GraphQl {
|
||||
override val text: String = "MUTATION"
|
||||
override val icon = Res.drawable.graphql
|
||||
|
|
@ -52,5 +53,22 @@ sealed interface NetworkMethodUi {
|
|||
data class OTHER(
|
||||
override val text: String,
|
||||
override val icon: DrawableResource?,
|
||||
) : NetworkMethodUi
|
||||
) : NetworkMethodUi {
|
||||
companion object {
|
||||
val EMPTY = OTHER(text = "", icon = null)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun all() = listOf(
|
||||
Http.GET,
|
||||
Http.POST,
|
||||
Http.PUT,
|
||||
Http.DELETE,
|
||||
GraphQl.QUERY,
|
||||
GraphQl.MUTATION,
|
||||
Grpc,
|
||||
OTHER.EMPTY
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
package io.github.openflocon.flocondesktop.features.network.ui.model
|
||||
|
||||
sealed interface OnNetworkItemUserAction {
|
||||
data class OnClicked(
|
||||
val item: NetworkItemViewState,
|
||||
) : OnNetworkItemUserAction
|
||||
|
||||
data class CopyUrl(
|
||||
val item: NetworkItemViewState,
|
||||
) : OnNetworkItemUserAction
|
||||
|
||||
data class CopyCUrl(
|
||||
val item: NetworkItemViewState,
|
||||
) : OnNetworkItemUserAction
|
||||
|
||||
data class Remove(
|
||||
val item: NetworkItemViewState,
|
||||
) : OnNetworkItemUserAction
|
||||
|
||||
data class RemoveLinesAbove(
|
||||
val item: NetworkItemViewState,
|
||||
) : OnNetworkItemUserAction
|
||||
}
|
||||
|
|
@ -22,8 +22,8 @@ import androidx.compose.ui.unit.sp
|
|||
import io.github.openflocon.flocondesktop.common.ui.ContextualItem
|
||||
import io.github.openflocon.flocondesktop.common.ui.ContextualView
|
||||
import io.github.openflocon.flocondesktop.common.ui.FloconColors
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.NetworkAction
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.NetworkItemViewState
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.OnNetworkItemUserAction
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.previewNetworkItemViewState
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.view.components.MethodView
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.view.components.StatusView
|
||||
|
|
@ -47,9 +47,9 @@ data class NetworkItemColumnWidths(
|
|||
@Composable
|
||||
fun NetworkItemView(
|
||||
state: NetworkItemViewState,
|
||||
columnWidths: NetworkItemColumnWidths = NetworkItemColumnWidths(), // Default widths provided
|
||||
onUserAction: (OnNetworkItemUserAction) -> Unit,
|
||||
onAction: (NetworkAction) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
columnWidths: NetworkItemColumnWidths = NetworkItemColumnWidths(), // Default widths provided
|
||||
) {
|
||||
// Use FloconTheme.typography for consistent text sizes
|
||||
val bodySmall = FloconTheme.typography.bodySmall.copy(fontSize = 11.sp)
|
||||
|
|
@ -76,10 +76,10 @@ fun NetworkItemView(
|
|||
),
|
||||
onSelect = {
|
||||
when (it.id) {
|
||||
"copy_url" -> onUserAction(OnNetworkItemUserAction.CopyUrl(state))
|
||||
"copy_curl" -> onUserAction(OnNetworkItemUserAction.CopyCUrl(state))
|
||||
"remove" -> onUserAction(OnNetworkItemUserAction.Remove(state))
|
||||
"remove_lines_above" -> onUserAction(OnNetworkItemUserAction.RemoveLinesAbove(state))
|
||||
"copy_url" -> onAction(NetworkAction.CopyUrl(state))
|
||||
"copy_curl" -> onAction(NetworkAction.CopyCUrl(state))
|
||||
"remove" -> onAction(NetworkAction.Remove(state))
|
||||
"remove_lines_above" -> onAction(NetworkAction.RemoveLinesAbove(state))
|
||||
}
|
||||
},
|
||||
) {
|
||||
|
|
@ -87,9 +87,7 @@ fun NetworkItemView(
|
|||
modifier = modifier
|
||||
.padding(vertical = 4.dp)
|
||||
.clip(shape = RoundedCornerShape(8.dp))
|
||||
.clickable(onClick = {
|
||||
onUserAction(OnNetworkItemUserAction.OnClicked(state))
|
||||
})
|
||||
.clickable(onClick = { onAction(NetworkAction.SelectRequest(state.uuid)) })
|
||||
.padding(horizontal = 8.dp, vertical = 6.dp),
|
||||
// Inner padding for content
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
|
@ -205,7 +203,7 @@ private fun ItemViewPreview() {
|
|||
NetworkItemView(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
state = previewNetworkItemViewState(),
|
||||
onUserAction = {},
|
||||
onAction = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
package io.github.openflocon.flocondesktop.features.network.ui.view
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedContentTransitionScope.SlideDirection
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
|
|
@ -8,29 +11,28 @@ import androidx.compose.foundation.layout.fillMaxHeight
|
|||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.requiredWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import io.github.openflocon.flocondesktop.common.ui.FloconColors
|
||||
import io.github.openflocon.flocondesktop.common.ui.FloconTheme
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.NetworkAction
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.NetworkUiState
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.NetworkViewModel
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.NetworkDetailViewState
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.NetworkItemViewState
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.OnNetworkItemUserAction
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.previewGraphQlItemViewState
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.previewNetworkItemViewState
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.view.components.NetworkFilterBar
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.previewNetworkUiState
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.view.header.NetworkFilter
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.view.components.NetworkItemHeaderView
|
||||
import io.github.openflocon.library.designsystem.FloconTheme
|
||||
import org.jetbrains.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -39,34 +41,24 @@ import org.koin.compose.viewmodel.koinViewModel
|
|||
@Composable
|
||||
fun NetworkScreen(modifier: Modifier = Modifier) {
|
||||
val viewModel: NetworkViewModel = koinViewModel()
|
||||
val items by viewModel.state.collectAsStateWithLifecycle()
|
||||
val detailState by viewModel.detailState.collectAsStateWithLifecycle()
|
||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
NetworkScreen(
|
||||
networkItems = items,
|
||||
modifier = modifier,
|
||||
detailState = detailState,
|
||||
onNetworkItemUserAction = viewModel::onNetworkItemUserAction,
|
||||
onCopyText = viewModel::onCopyText,
|
||||
onReset = viewModel::onReset,
|
||||
closeDetailPanel = viewModel::closeDetailPanel,
|
||||
uiState = uiState,
|
||||
onAction = viewModel::onAction,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NetworkScreen(
|
||||
networkItems: List<NetworkItemViewState>,
|
||||
detailState: NetworkDetailViewState?,
|
||||
onNetworkItemUserAction: (OnNetworkItemUserAction) -> Unit,
|
||||
onCopyText: (String) -> Unit,
|
||||
closeDetailPanel: () -> Unit,
|
||||
onReset: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
uiState: NetworkUiState,
|
||||
onAction: (NetworkAction) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val columnWidths: NetworkItemColumnWidths =
|
||||
remember { NetworkItemColumnWidths() } // Default widths provided
|
||||
|
||||
var filteredItems by remember { mutableStateOf<List<NetworkItemViewState>>(emptyList()) }
|
||||
|
||||
Surface(modifier = modifier) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
|
|
@ -79,17 +71,13 @@ fun NetworkScreen(
|
|||
style = FloconTheme.typography.titleLarge,
|
||||
color = FloconTheme.colorScheme.onSurface,
|
||||
)
|
||||
NetworkFilterBar(
|
||||
modifier =
|
||||
Modifier
|
||||
NetworkFilter(
|
||||
uiState = uiState,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(FloconColors.pannel)
|
||||
.padding(horizontal = 12.dp),
|
||||
networkItems = networkItems,
|
||||
onResetClicked = onReset,
|
||||
onItemsChange = {
|
||||
filteredItems = it
|
||||
},
|
||||
onAction = onAction
|
||||
)
|
||||
NetworkItemHeaderView(
|
||||
columnWidths = columnWidths,
|
||||
|
|
@ -101,37 +89,53 @@ fun NetworkScreen(
|
|||
) {
|
||||
LazyColumn(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.clickable(
|
||||
interactionSource = null,
|
||||
indication = null,
|
||||
enabled = detailState != null,
|
||||
) {
|
||||
closeDetailPanel()
|
||||
},
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.clickable(
|
||||
interactionSource = null,
|
||||
indication = null,
|
||||
enabled = uiState.detailState != null,
|
||||
onClick = { onAction(NetworkAction.ClosePanel) }
|
||||
),
|
||||
) {
|
||||
items(filteredItems) {
|
||||
items(
|
||||
items = uiState.items,
|
||||
key = NetworkItemViewState::uuid
|
||||
) {
|
||||
NetworkItemView(
|
||||
state = it,
|
||||
columnWidths = columnWidths,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onUserAction = onNetworkItemUserAction,
|
||||
onAction = onAction,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.animateItem()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
detailState?.let {
|
||||
NetworkDetailView(
|
||||
modifier =
|
||||
Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.fillMaxHeight()
|
||||
.width(500.dp),
|
||||
state = it,
|
||||
onCopy = onCopyText,
|
||||
)
|
||||
AnimatedContent(
|
||||
targetState = uiState.detailState,
|
||||
transitionSpec = {
|
||||
slideIntoContainer(SlideDirection.Start)
|
||||
.togetherWith(slideOutOfContainer(SlideDirection.End))
|
||||
},
|
||||
contentKey = { it != null },
|
||||
contentAlignment = Alignment.TopEnd,
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.requiredWidth(500.dp)
|
||||
.align(Alignment.TopEnd)
|
||||
) {
|
||||
if (it != null) {
|
||||
NetworkDetailView(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = it,
|
||||
onCopy = { onAction(NetworkAction.CopyText(it)) },
|
||||
)
|
||||
} else {
|
||||
Box(Modifier.fillMaxSize())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -141,8 +145,8 @@ fun NetworkScreen(
|
|||
@Preview
|
||||
private fun NetworkScreenPreview() {
|
||||
FloconTheme {
|
||||
val networkItems =
|
||||
remember {
|
||||
val uiState = previewNetworkUiState().copy(
|
||||
items = remember {
|
||||
listOf(
|
||||
previewNetworkItemViewState(),
|
||||
previewNetworkItemViewState(),
|
||||
|
|
@ -152,13 +156,11 @@ private fun NetworkScreenPreview() {
|
|||
previewNetworkItemViewState(),
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
NetworkScreen(
|
||||
networkItems = networkItems,
|
||||
detailState = null,
|
||||
closeDetailPanel = {},
|
||||
onNetworkItemUserAction = {},
|
||||
onCopyText = {},
|
||||
onReset = {},
|
||||
uiState = uiState,
|
||||
onAction = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
package io.github.openflocon.flocondesktop.features.network.ui.view.components
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.SuggestionChip
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun FilterDropdown(
|
||||
text: String,
|
||||
icon: ImageVector?,
|
||||
content: @Composable ColumnScope.(dismiss: () -> Unit) -> Unit
|
||||
) {
|
||||
var filterExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
Column {
|
||||
SuggestionChip(
|
||||
onClick = { filterExpanded = !filterExpanded },
|
||||
label = { Text(text = text) },
|
||||
icon = {
|
||||
if (icon != null)
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(20.dp),
|
||||
)
|
||||
}
|
||||
)
|
||||
DropdownMenu(
|
||||
expanded = filterExpanded,
|
||||
onDismissRequest = { filterExpanded = false }
|
||||
) {
|
||||
content({ filterExpanded = false })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package io.github.openflocon.flocondesktop.features.network.ui.view.filters
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.FilterUiState
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.NetworkItemViewState
|
||||
|
||||
@Stable
|
||||
interface Filters {
|
||||
|
||||
fun filter(state: FilterUiState, list: List<NetworkItemViewState>): List<NetworkItemViewState>
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package io.github.openflocon.flocondesktop.features.network.ui.view.filters
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import io.github.openflocon.flocondesktop.features.network.domain.model.FloconHttpRequestDomainModel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
//class HostFilter(
|
||||
// private val hosts: List<String>
|
||||
//) : Filters {
|
||||
// override val sort: Int
|
||||
// get() = 1
|
||||
//
|
||||
// override val content: @Composable (() -> Unit) = {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// override fun filter(list: List<FloconHttpRequestDomainModel>): Flow<List<FloconHttpRequestDomainModel>> {
|
||||
// return flowOf(list)
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
package io.github.openflocon.flocondesktop.features.network.ui.view.filters
|
||||
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.FilterUiState
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.NetworkAction
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.NetworkItemViewState
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.NetworkMethodUi
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.view.components.FilterDropdown
|
||||
|
||||
class MethodFilter : Filters {
|
||||
|
||||
override fun filter(state: FilterUiState, list: List<NetworkItemViewState>): List<NetworkItemViewState> {
|
||||
if (state.methods.isEmpty())
|
||||
return list
|
||||
|
||||
return list.filter { item ->
|
||||
when (item.method) {
|
||||
NetworkMethodUi.GraphQl.MUTATION -> state.methods.contains(NetworkMethodUi.GraphQl.MUTATION)
|
||||
NetworkMethodUi.GraphQl.QUERY -> state.methods.contains(NetworkMethodUi.GraphQl.QUERY)
|
||||
NetworkMethodUi.Grpc -> state.methods.contains(NetworkMethodUi.Grpc)
|
||||
NetworkMethodUi.Http.DELETE -> state.methods.contains(NetworkMethodUi.Http.DELETE)
|
||||
NetworkMethodUi.Http.GET -> state.methods.contains(NetworkMethodUi.Http.GET)
|
||||
NetworkMethodUi.Http.POST -> state.methods.contains(NetworkMethodUi.Http.POST)
|
||||
NetworkMethodUi.Http.PUT -> state.methods.contains(NetworkMethodUi.Http.PUT)
|
||||
is NetworkMethodUi.OTHER -> state.methods.contains(NetworkMethodUi.OTHER.EMPTY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FilterMethods(
|
||||
filterState: FilterUiState,
|
||||
onAction: (NetworkAction) -> Unit
|
||||
) {
|
||||
FilterDropdown(
|
||||
text = "Method",
|
||||
icon = null // TODO Find better icon
|
||||
) {
|
||||
NetworkMethodUi.all()
|
||||
.forEach { method ->
|
||||
val selected = filterState.methods.contains(method)
|
||||
val onClick = { onAction(NetworkAction.FilterMethod(method, !selected)) }
|
||||
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = method.label) },
|
||||
leadingIcon = {},
|
||||
trailingIcon = {
|
||||
Checkbox(
|
||||
checked = selected,
|
||||
onCheckedChange = { onClick() },
|
||||
interactionSource = null
|
||||
)
|
||||
},
|
||||
onClick = onClick
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val NetworkMethodUi.label
|
||||
get() = when (this) {
|
||||
NetworkMethodUi.GraphQl.MUTATION -> "GraphQL - Mutation"
|
||||
NetworkMethodUi.GraphQl.QUERY -> "GraphQL - Query"
|
||||
NetworkMethodUi.Grpc -> "Grpc"
|
||||
NetworkMethodUi.Http.DELETE -> "Http - DELETE"
|
||||
NetworkMethodUi.Http.GET -> "Http - GET"
|
||||
NetworkMethodUi.Http.POST -> "Http - POST"
|
||||
NetworkMethodUi.Http.PUT -> "Http - PUT"
|
||||
is NetworkMethodUi.OTHER -> "Other"
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package io.github.openflocon.flocondesktop.features.network.ui.view.header
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.NetworkAction
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.NetworkUiState
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.view.components.FilterBar
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.view.filters.FilterMethods
|
||||
|
||||
@Composable
|
||||
fun NetworkFilter(
|
||||
uiState: NetworkUiState,
|
||||
onAction: (NetworkAction) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
FilterBar(
|
||||
placeholderText = "Filter route",
|
||||
onTextChange = { onAction(NetworkAction.FilterQuery(it)) },
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(4.dp))
|
||||
.clickable(onClick = { onAction(NetworkAction.Reset) })
|
||||
.padding(all = 8.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Delete,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(20.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
FilterMethods(
|
||||
filterState = uiState.filterState,
|
||||
onAction = onAction
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
package io.github.openflocon.flocondesktop.features.network.ui.view.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.NetworkItemViewState
|
||||
|
||||
@Composable
|
||||
fun NetworkFilterBar(
|
||||
networkItems: List<NetworkItemViewState>,
|
||||
onItemsChange: (List<NetworkItemViewState>) -> Unit,
|
||||
onResetClicked: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var filterText by remember {
|
||||
mutableStateOf("")
|
||||
}
|
||||
val onItemsChangeCallback by rememberUpdatedState(onItemsChange)
|
||||
val filteredNetworkItems: List<NetworkItemViewState> =
|
||||
remember(networkItems, filterText) {
|
||||
if (filterText.isBlank()) {
|
||||
networkItems
|
||||
} else {
|
||||
networkItems.filter {
|
||||
it.contains(filterText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(filteredNetworkItems) {
|
||||
onItemsChangeCallback(filteredNetworkItems)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
FilterBar(
|
||||
placeholderText = "Filter Route",
|
||||
modifier = Modifier.weight(1f),
|
||||
onTextChange = {
|
||||
filterText = it
|
||||
},
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(4.dp))
|
||||
.clickable(onClick = onResetClicked)
|
||||
.padding(all = 8.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Delete,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(20.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package io.github.openflocon.flocondesktop
|
||||
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Window
|
||||
|
|
@ -13,6 +14,7 @@ import coil3.network.ktor3.KtorNetworkFetcherFactory
|
|||
import flocondesktop.composeapp.generated.resources.Res
|
||||
import flocondesktop.composeapp.generated.resources.app_icon_small
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
import java.awt.Dimension
|
||||
|
||||
fun main() = application {
|
||||
startKoinApp()
|
||||
|
|
@ -32,6 +34,8 @@ fun main() = application {
|
|||
position = WindowPosition(Alignment.Center),
|
||||
),
|
||||
) {
|
||||
window.minimumSize = Dimension(1200, 800)
|
||||
|
||||
App()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ room = "2.7.1"
|
|||
ksp = "2.2.0-2.0.2"
|
||||
ktlintGradle = "13.0.0"
|
||||
aboutLibraries = "12.2.4"
|
||||
other-molecule = "2.1.0"
|
||||
kotlinStdlib = "2.2.0"
|
||||
runner = "1.5.2"
|
||||
core = "1.5.0"
|
||||
|
|
@ -44,7 +45,7 @@ androidx-activity-compose = { module = "androidx.activity:activity-compose", ver
|
|||
androidx-lifecycle-viewmodel = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel", version.ref = "androidx-lifecycle" }
|
||||
androidx-lifecycle-runtimeCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
|
||||
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }
|
||||
kotlinx-coroutinesCore = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref= "kotlinx-coroutines" }
|
||||
kotlinx-coroutinesCore = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
|
||||
kotlinx-coroutinesSwing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" }
|
||||
kotlinx-dateTime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-dateTime" }
|
||||
kotlinx-serializationJson = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
|
||||
|
|
@ -57,12 +58,12 @@ ktor-serverContentNegociation = { module = "io.ktor:ktor-server-content-negotiat
|
|||
ktor-serverWebsocket = { module = "io.ktor:ktor-server-websockets", version.ref = "ktor" }
|
||||
ktor-serverTestHost = { module = "io.ktor:ktor-server-test-host-jvm", version.ref = "ktor" }
|
||||
koin-bom = { module = "io.insert-koin:koin-bom", version.ref = "koin" }
|
||||
koin-core = { module = "io.insert-koin:koin-core"}
|
||||
koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel"}
|
||||
koin-compose-viewmodel-navigation = { module = "io.insert-koin:koin-compose-viewmodel-navigation"}
|
||||
koin-android = { module = "io.insert-koin:koin-android"}
|
||||
multiplatform-settings = { module = "com.russhwolf:multiplatform-settings", version.ref = "multiplatform-settings"}
|
||||
multiplatform-settings-coroutines = { module = "com.russhwolf:multiplatform-settings-coroutines", version.ref = "multiplatform-settings"}
|
||||
koin-core = { module = "io.insert-koin:koin-core" }
|
||||
koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel" }
|
||||
koin-compose-viewmodel-navigation = { module = "io.insert-koin:koin-compose-viewmodel-navigation" }
|
||||
koin-android = { module = "io.insert-koin:koin-android" }
|
||||
multiplatform-settings = { module = "com.russhwolf:multiplatform-settings", version.ref = "multiplatform-settings" }
|
||||
multiplatform-settings-coroutines = { module = "com.russhwolf:multiplatform-settings-coroutines", version.ref = "multiplatform-settings" }
|
||||
coil-network-ktor = { module = "io.coil-kt.coil3:coil-network-ktor3", version.ref = "coil" }
|
||||
|
||||
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue