mirror of
https://github.com/openflocon/Flocon.git
synced 2026-05-05 18:31:07 +00:00
feature: Add detail json (#61)
Co-authored-by: TEYSSANDIER Raphael <rteyssandier@sephora.fr>
This commit is contained in:
parent
46da1787cb
commit
46a4e00094
12 changed files with 177 additions and 39 deletions
|
|
@ -1,12 +1,15 @@
|
|||
package io.github.openflocon.flocondesktop.features.network.ui
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.NetworkJsonUi
|
||||
|
||||
@Immutable
|
||||
data class ContentUiState(
|
||||
val selectedRequestId: String?
|
||||
val selectedRequestId: String?,
|
||||
val detailJsons: Set<NetworkJsonUi>
|
||||
)
|
||||
|
||||
fun previewContentUiState() = ContentUiState(
|
||||
selectedRequestId = null
|
||||
selectedRequestId = null,
|
||||
detailJsons = emptySet()
|
||||
)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@ sealed interface NetworkAction {
|
|||
|
||||
data object Reset : NetworkAction
|
||||
|
||||
data class JsonDetail(val id: String, val json: String) : NetworkAction
|
||||
|
||||
data class CloseJsonDetail(val id: String) : NetworkAction
|
||||
|
||||
data class CopyUrl(val item: NetworkItemViewState) : NetworkAction
|
||||
|
||||
data class CopyCUrl(val item: NetworkItemViewState) : NetworkAction
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ 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.NetworkJsonUi
|
||||
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
|
||||
|
|
@ -44,7 +45,7 @@ class NetworkViewModel(
|
|||
|
||||
private val filterMethod = MethodFilter()
|
||||
|
||||
private val contentState = MutableStateFlow(ContentUiState(selectedRequestId = null))
|
||||
private val contentState = MutableStateFlow(ContentUiState(selectedRequestId = null, detailJsons = emptySet()))
|
||||
private val filterUiState = MutableStateFlow(FilterUiState(query = "", methods = NetworkMethodUi.all()))
|
||||
|
||||
private val detailState: StateFlow<NetworkDetailViewState?> =
|
||||
|
|
@ -105,6 +106,8 @@ class NetworkViewModel(
|
|||
is NetworkAction.RemoveLinesAbove -> onRemoveLinesAbove(action)
|
||||
is NetworkAction.FilterQuery -> onFilterQuery(action)
|
||||
is NetworkAction.FilterMethod -> onFilterMethod(action)
|
||||
is NetworkAction.CloseJsonDetail -> onCloseJsonDetail(action)
|
||||
is NetworkAction.JsonDetail -> onJsonDetail(action)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -129,6 +132,24 @@ class NetworkViewModel(
|
|||
feedbackDisplayer.displayMessage("copied")
|
||||
}
|
||||
|
||||
private fun onJsonDetail(action: NetworkAction.JsonDetail) {
|
||||
contentState.update { state ->
|
||||
if (state.detailJsons.any { it.id == action.id })
|
||||
return
|
||||
|
||||
state.copy(
|
||||
detailJsons = state.detailJsons + NetworkJsonUi(
|
||||
id = action.id,
|
||||
json = action.json
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onCloseJsonDetail(action: NetworkAction.CloseJsonDetail) {
|
||||
contentState.update { state -> state.copy(detailJsons = state.detailJsons.filterNot { it.id == action.id }.toSet()) }
|
||||
}
|
||||
|
||||
private fun onReset() {
|
||||
viewModelScope.launch(dispatcherProvider.viewModel) {
|
||||
resetCurrentDeviceHttpRequestsUseCase()
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import io.github.openflocon.flocondesktop.features.network.ui.model.NetworkDetai
|
|||
import io.github.openflocon.flocondesktop.features.network.ui.model.NetworkStatusUi
|
||||
|
||||
fun toDetailUi(request: FloconHttpRequestDomainModel): NetworkDetailViewState = NetworkDetailViewState(
|
||||
uuid = request.uuid,
|
||||
fullUrl = request.url,
|
||||
method = toDetailMethodUi(request),
|
||||
status = toDetailNetworkStatusUi(request.type),
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import androidx.compose.runtime.Immutable
|
|||
|
||||
@Immutable
|
||||
data class NetworkDetailViewState(
|
||||
val uuid: String,
|
||||
val fullUrl: String,
|
||||
val requestTimeFormatted: String,
|
||||
val durationFormatted: String,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
package io.github.openflocon.flocondesktop.features.network.ui.model
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
|
||||
@Immutable
|
||||
data class NetworkJsonUi(
|
||||
val id: String,
|
||||
val json: String
|
||||
)
|
||||
|
|
@ -310,6 +310,7 @@ private fun Response(
|
|||
onToggle = {
|
||||
isResponseBodyExpanded = it
|
||||
},
|
||||
onDetail = { onAction(NetworkAction.JsonDetail(state.requestTimeFormatted, state.responseBody)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
FloconSectionExpandable(
|
||||
|
|
@ -330,23 +331,23 @@ private fun Response(
|
|||
private fun NetworkDetailViewPreview() {
|
||||
FloconTheme {
|
||||
NetworkDetailView(
|
||||
state =
|
||||
NetworkDetailViewState(
|
||||
fullUrl = "http://www.google.com",
|
||||
method = NetworkDetailViewState.Method.Http(NetworkMethodUi.Http.GET),
|
||||
status =
|
||||
NetworkStatusUi(
|
||||
text = "200",
|
||||
isSuccess = true,
|
||||
),
|
||||
requestHeaders =
|
||||
listOf(
|
||||
previewNetworkDetailHeaderUi(),
|
||||
previewNetworkDetailHeaderUi(),
|
||||
previewNetworkDetailHeaderUi(),
|
||||
),
|
||||
requestBody =
|
||||
"""
|
||||
state = NetworkDetailViewState(
|
||||
uuid = "",
|
||||
fullUrl = "http://www.google.com",
|
||||
method = NetworkDetailViewState.Method.Http(NetworkMethodUi.Http.GET),
|
||||
status =
|
||||
NetworkStatusUi(
|
||||
text = "200",
|
||||
isSuccess = true,
|
||||
),
|
||||
requestHeaders =
|
||||
listOf(
|
||||
previewNetworkDetailHeaderUi(),
|
||||
previewNetworkDetailHeaderUi(),
|
||||
previewNetworkDetailHeaderUi(),
|
||||
),
|
||||
requestBody =
|
||||
"""
|
||||
{
|
||||
"id": "123",
|
||||
"name": "Flocon App",
|
||||
|
|
@ -359,18 +360,18 @@ private fun NetworkDetailViewPreview() {
|
|||
}
|
||||
}
|
||||
""".trimIndent(),
|
||||
responseHeaders =
|
||||
listOf(
|
||||
previewNetworkDetailHeaderUi(),
|
||||
previewNetworkDetailHeaderUi(),
|
||||
previewNetworkDetailHeaderUi(),
|
||||
previewNetworkDetailHeaderUi(),
|
||||
previewNetworkDetailHeaderUi(),
|
||||
),
|
||||
requestTimeFormatted = "00:00:00.000",
|
||||
durationFormatted = "300ms",
|
||||
responseBody =
|
||||
"""
|
||||
responseHeaders =
|
||||
listOf(
|
||||
previewNetworkDetailHeaderUi(),
|
||||
previewNetworkDetailHeaderUi(),
|
||||
previewNetworkDetailHeaderUi(),
|
||||
previewNetworkDetailHeaderUi(),
|
||||
previewNetworkDetailHeaderUi(),
|
||||
),
|
||||
requestTimeFormatted = "00:00:00.000",
|
||||
durationFormatted = "300ms",
|
||||
responseBody =
|
||||
"""
|
||||
{
|
||||
"networkStatusUi": "success",
|
||||
"message": "Data received and processed.",
|
||||
|
|
@ -380,10 +381,10 @@ private fun NetworkDetailViewPreview() {
|
|||
}
|
||||
}
|
||||
""".trimIndent(),
|
||||
requestSize = "0kb",
|
||||
responseSize = "0kb",
|
||||
graphQlSection = null,
|
||||
),
|
||||
requestSize = "0kb",
|
||||
responseSize = "0kb",
|
||||
graphQlSection = null,
|
||||
),
|
||||
modifier = Modifier.padding(16.dp), // Padding pour la preview
|
||||
onAction = {}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -16,22 +16,33 @@ import androidx.compose.foundation.lazy.LazyColumn
|
|||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Window
|
||||
import androidx.compose.ui.window.WindowPlacement
|
||||
import androidx.compose.ui.window.WindowPosition
|
||||
import androidx.compose.ui.window.WindowState
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import flocondesktop.composeapp.generated.resources.Res
|
||||
import flocondesktop.composeapp.generated.resources.app_icon
|
||||
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.NetworkItemViewState
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.model.NetworkJsonUi
|
||||
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.previewNetworkUiState
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.view.components.NetworkItemHeaderView
|
||||
import io.github.openflocon.flocondesktop.features.network.ui.view.header.NetworkFilter
|
||||
import io.github.openflocon.library.designsystem.FloconTheme
|
||||
import io.github.openflocon.library.designsystem.components.FloconJsonTree
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
import io.github.openflocon.library.designsystem.components.FloconSurface
|
||||
import org.jetbrains.compose.ui.tooling.preview.Preview
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
|
|
@ -138,6 +149,44 @@ fun NetworkScreen(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
val states = remember { mutableStateMapOf<NetworkJsonUi, WindowState>() }
|
||||
|
||||
|
||||
LaunchedEffect(uiState.contentState.detailJsons) {
|
||||
val deletedJson = states.keys.filter { key -> uiState.contentState.detailJsons.none { key.id == it.id } }
|
||||
val addedJson = uiState.contentState.detailJsons.filter { key -> states.keys.none { key.id == it.id } }
|
||||
|
||||
deletedJson.forEach { states.remove(it) }
|
||||
addedJson.forEach {
|
||||
states.put(
|
||||
it, WindowState(
|
||||
placement = WindowPlacement.Floating,
|
||||
position = WindowPosition(Alignment.Center)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
uiState.contentState
|
||||
.detailJsons
|
||||
.forEach { item ->
|
||||
val state = states[item]
|
||||
|
||||
if (state != null) {
|
||||
Window(
|
||||
title = "Json",
|
||||
icon = painterResource(Res.drawable.app_icon),
|
||||
state = state,
|
||||
onCloseRequest = { onAction(NetworkAction.CloseJsonDetail(item.id)) }
|
||||
) {
|
||||
FloconJsonTree(
|
||||
json = item.json,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -5,7 +5,11 @@ import androidx.compose.foundation.layout.Arrangement
|
|||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.CopyAll
|
||||
import androidx.compose.material.icons.outlined.Details
|
||||
import androidx.compose.material.icons.outlined.ExpandMore
|
||||
import androidx.compose.material.icons.outlined.OpenInFull
|
||||
import androidx.compose.material.icons.outlined.Share
|
||||
import androidx.compose.material.icons.outlined.Visibility
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -22,9 +26,10 @@ import org.jetbrains.compose.ui.tooling.preview.Preview
|
|||
fun DetailSectionTitleView(
|
||||
isExpanded: Boolean,
|
||||
title: String,
|
||||
onCopy: (() -> Unit)?,
|
||||
onToggle: ((isExpanded: Boolean) -> Unit)?,
|
||||
modifier: Modifier = Modifier
|
||||
modifier: Modifier = Modifier,
|
||||
onDetail: (() -> Unit)? = null,
|
||||
onCopy: (() -> Unit)? = null,
|
||||
onToggle: ((isExpanded: Boolean) -> Unit)? = null,
|
||||
) {
|
||||
val rotate by animateFloatAsState(
|
||||
targetValue = if (isExpanded) 180f else 0f,
|
||||
|
|
@ -55,6 +60,13 @@ fun DetailSectionTitleView(
|
|||
color = FloconTheme.colorPalette.onBackground,
|
||||
modifier = Modifier.weight(1f), // Takes remaining space
|
||||
)
|
||||
|
||||
if (onDetail != null) {
|
||||
FloconIconButton(
|
||||
onClick = onDetail,
|
||||
imageVector = Icons.Outlined.OpenInFull
|
||||
)
|
||||
}
|
||||
if (onCopy != null) {
|
||||
FloconIconButton(
|
||||
imageVector = Icons.Outlined.CopyAll,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue