Refact UI dialog network (#143)

* refact: [NETWORK] dialogs ui

* fix window size

---------

Co-authored-by: Florent Champigny <florent@bere.al>
This commit is contained in:
Florent CHAMPIGNY 2025-08-23 10:38:01 +02:00 committed by GitHub
parent ee8d0a0255
commit f4f815d53d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 400 additions and 330 deletions

View file

@ -1,8 +1,11 @@
package io.github.openflocon.flocondesktop.common.ui.window
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.DpSize
actual fun createFloconWindowState(): FloconWindowState {
actual fun createFloconWindowState(
size: DpSize?,
): FloconWindowState {
TODO("Not yet implemented")
}

View file

@ -1,10 +1,16 @@
package io.github.openflocon.flocondesktop.common.ui.window
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
interface FloconWindowState
expect fun createFloconWindowState(): FloconWindowState
val defaultWindowSize = DpSize(800.dp, 600.dp)
expect fun createFloconWindowState(
size: DpSize? = null,
): FloconWindowState
@Composable
expect fun FloconWindow(

View file

@ -1,72 +1,91 @@
package io.github.openflocon.flocondesktop.features.network.badquality.edition.view
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
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.draw.clip
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import io.github.openflocon.flocondesktop.features.network.badquality.edition.model.BadQualityConfigUiModel
import io.github.openflocon.flocondesktop.features.network.badquality.edition.model.possibleExceptions
import io.github.openflocon.library.designsystem.FloconTheme
import io.github.openflocon.library.designsystem.components.DefaultLabel
import io.github.openflocon.library.designsystem.components.FloconDialogButtons
import io.github.openflocon.library.designsystem.components.FloconDialogHeader
import io.github.openflocon.library.designsystem.components.FloconTextField
import io.github.openflocon.library.designsystem.components.defaultLabel
import io.github.openflocon.library.designsystem.components.defaultPlaceHolder
@Composable
fun BadQuaityErrorExceptionEditor(
error: BadQualityConfigUiModel.Error,
errorException: BadQualityConfigUiModel.Error.Type.Exception,
onErrorsChange: (BadQualityConfigUiModel.Error) -> Unit,
save: (BadQualityConfigUiModel.Error) -> Unit,
cancel: () -> Unit,
) {
var weight by remember(error) { mutableStateOf<String>(error.weight.toString()) }
var errorClassPath by remember(error) { mutableStateOf<String>(errorException.classPath) }
Column(
modifier = Modifier.padding(all = 8.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
modifier = Modifier.fillMaxWidth()
) {
possibleExceptions.fastForEach { exception ->
val (backgroundColor, textColor) = if (exception.classPath == errorException.classPath) {
FloconTheme.colorPalette.onSurface to FloconTheme.colorPalette.panel
} else {
FloconTheme.colorPalette.panel to FloconTheme.colorPalette.onSurface
}
FloconDialogHeader(
title = "Exception",
modifier = Modifier.fillMaxWidth(),
)
Column(
modifier = Modifier.padding(all = 8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
FloconTextField(
label = defaultLabel("Weight"),
placeholder = defaultPlaceHolder("eg: 1.0"),
value = weight,
onValueChange = {
if (it.isEmpty() || it.toFloatOrNull() != null) {
weight = it
}
},
containerColor = FloconTheme.colorPalette.panel
)
DefaultLabel("Select the exception")
Column(
modifier = Modifier
.clip(RoundedCornerShape(8.dp))
.background(
color = backgroundColor,
)
.then(
Modifier.clickable(
onClick = {
onErrorsChange(
error.copy(
weight = 1f,
type = errorException.copy(
classPath = exception.classPath
)
)
)
},
)
).padding(all = 8.dp)
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = exception.description,
style = FloconTheme.typography.bodySmall,
color = textColor,
)
Text(
text = exception.classPath,
style = FloconTheme.typography.bodyMedium.copy(
fontWeight = FontWeight.Bold,
),
color = textColor,
NetworkExceptionSelector(
selected = errorClassPath,
onSelected = {
errorClassPath = it
}
)
}
FloconDialogButtons(
onCancel = cancel,
onValidate = {
save(
error.copy(
weight = weight.toFloatOrNull() ?: error.weight,
type = errorException.copy(
classPath = errorClassPath
)
)
)
},
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 4.dp),
)
}
}
}

View file

@ -5,8 +5,6 @@ package io.github.openflocon.flocondesktop.features.network.badquality.edition.v
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
@ -23,6 +21,7 @@ import io.github.openflocon.flocondesktop.features.network.badquality.edition.mo
import io.github.openflocon.library.designsystem.FloconTheme
import io.github.openflocon.library.designsystem.components.FloconDialog
import io.github.openflocon.library.designsystem.components.FloconDialogButtons
import io.github.openflocon.library.designsystem.components.FloconDialogHeader
import io.github.openflocon.library.designsystem.components.FloconSurface
import io.github.openflocon.library.designsystem.components.FloconTextField
import io.github.openflocon.library.designsystem.components.defaultLabel
@ -46,6 +45,13 @@ fun BadQualityEditionWindow(
}
}
fun SelectedBadQualityUiModel.title(): String {
return when (this) {
SelectedBadQualityUiModel.Creation -> "Creation"
is SelectedBadQualityUiModel.Edition -> "Edition"
}
}
@Composable
fun BadNetworkQualityEditionContent(
state: SelectedBadQualityUiModel,
@ -86,13 +92,18 @@ fun BadNetworkQualityEditionContent(
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = modifier
.fillMaxSize(),
.fillMaxWidth(),
) {
FloconDialogHeader(
title = state.title(),
modifier = Modifier.fillMaxWidth(),
)
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = modifier
modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
.padding(horizontal = 12.dp),
) {
FloconTextField(
label = defaultLabel("Name"),
@ -156,7 +167,8 @@ fun BadNetworkQualityEditionContent(
)
}
BadQualityErrorsListView(
modifier = Modifier.weight(1f).padding(top = 16.dp),
modifier = Modifier
.padding(top = 16.dp),
errors = errors,
onErrorslicked = { error ->
selectedErrorToEdit = error
@ -174,10 +186,13 @@ fun BadNetworkQualityEditionContent(
FloconSurface {
when (val t = selectedError.type) {
is BadQualityConfigUiModel.Error.Type.Body -> {
BadQualityErrorsEditor(
BadQualityHttpErrorEditor(
error = selectedError,
httpType = t,
onErrorsChange = { error ->
cancel = {
selectedErrorToEdit = null
},
save = { error ->
errors = if (errors.any { it.uuid == selectedError.uuid }) {
errors.map {
if (it.uuid == selectedError.uuid) {
@ -196,7 +211,10 @@ fun BadNetworkQualityEditionContent(
BadQuaityErrorExceptionEditor(
error = selectedError,
errorException = t,
onErrorsChange = { error ->
cancel = {
selectedErrorToEdit = null
},
save = { error ->
errors = if (errors.any { it.uuid == selectedError.uuid }) {
errors.map {
if (it.uuid == selectedError.uuid) {
@ -223,7 +241,7 @@ fun BadNetworkQualityEditionContent(
save(
BadQualityConfigUiModel(
id = config?.id ?: UUID.randomUUID().toString(), // generate a new one
name = name,
name = name.takeIf { it.isNotBlank() } ?: UUID.randomUUID().toString(), // generate if empty, TODO : fail
isEnabled = config?.isEnabled ?: false, // disabled by default
createdAt = config?.createdAt
?: System.currentTimeMillis(), // generate a new date

View file

@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
@ -63,7 +64,7 @@ fun BadQualityErrorsListView(
weight = 1f,
type = BadQualityConfigUiModel.Error.Type.Body(
httpCode = 500,
body = "{\"error\":\"...\"}",
body = "",
contentType = "application/json",
),
),
@ -93,54 +94,58 @@ fun BadQualityErrorsListView(
}
}
val lazyListState = rememberLazyListState()
val scrollAdapter = rememberFloconScrollbarAdapter(lazyListState)
Box(modifier = Modifier.fillMaxSize()) {
val contentPadding = PaddingValues(horizontal = 12.dp)
LazyColumn(
state = lazyListState,
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(vertical = 12.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
if(errors.isNotEmpty()) {
item {
Row(
modifier = Modifier.fillMaxWidth()
.padding(horizontal = 6.dp)
) {
Text(
"Weight",
style = FloconTheme.typography.bodySmall.copy(
fontWeight = FontWeight.Thin,
fontSize = 10.sp,
),
)
Spacer(modifier = Modifier.width(28.dp))
Text(
"Type",
style = FloconTheme.typography.bodySmall.copy(
fontWeight = FontWeight.Thin,
fontSize = 10.sp,
),
)
if (errors.isNotEmpty()) {
val lazyListState = rememberLazyListState()
val scrollAdapter = rememberFloconScrollbarAdapter(lazyListState)
Box(modifier = Modifier.height(160.dp)) {
val contentPadding = PaddingValues(horizontal = 12.dp)
LazyColumn(
state = lazyListState,
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(vertical = 12.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
if (errors.isNotEmpty()) {
item {
Row(
modifier = Modifier.fillMaxWidth()
.padding(horizontal = 6.dp)
) {
Text(
"Weight",
style = FloconTheme.typography.bodySmall.copy(
fontWeight = FontWeight.Thin,
fontSize = 10.sp,
),
)
Spacer(modifier = Modifier.width(28.dp))
Text(
"Type",
style = FloconTheme.typography.bodySmall.copy(
fontWeight = FontWeight.Thin,
fontSize = 10.sp,
),
)
}
}
}
items(errors) { error ->
BadQualityErrorItemView(
modifier = Modifier,
error = error,
deleteError = deleteError,
clickedError = onErrorslicked,
contentPadding = contentPadding,
)
}
}
items(errors) { error ->
BadQualityErrorItemView(
modifier = Modifier.fillMaxWidth(),
error = error,
deleteError = deleteError,
clickedError = onErrorslicked,
contentPadding = contentPadding,
)
}
FloconVerticalScrollbar(
adapter = scrollAdapter,
modifier = Modifier
.align(Alignment.CenterEnd)
.fillMaxHeight(),
)
}
FloconVerticalScrollbar(
adapter = scrollAdapter,
modifier = Modifier.fillMaxHeight(),
)
}
}
}

View file

@ -0,0 +1,66 @@
package io.github.openflocon.flocondesktop.features.network.badquality.edition.view
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import io.github.openflocon.flocondesktop.features.network.badquality.edition.model.possibleExceptions
import io.github.openflocon.library.designsystem.FloconTheme
@Composable
fun NetworkExceptionSelector(
selected: String,
onSelected: (errorClassPath: String) -> Unit,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier.padding(horizontal = 4.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
possibleExceptions.fastForEach { exception ->
val (backgroundColor, textColor) = if (exception.classPath == selected) {
FloconTheme.colorPalette.onSurface to FloconTheme.colorPalette.panel
} else {
FloconTheme.colorPalette.panel to FloconTheme.colorPalette.onSurface
}
Column(
modifier = Modifier
.clip(RoundedCornerShape(8.dp))
.background(
color = backgroundColor,
)
.then(
Modifier.clickable(
onClick = {
onSelected(exception.classPath)
},
)
)
.padding(horizontal = 10.dp, vertical = 4.dp)
) {
Text(
text = exception.description,
style = FloconTheme.typography.bodySmall,
color = textColor,
)
Text(
text = exception.classPath,
style = FloconTheme.typography.bodySmall.copy(
fontWeight = FontWeight.Bold,
),
color = textColor,
)
}
}
}
}

View file

@ -1,70 +1,47 @@
package io.github.openflocon.flocondesktop.features.network.badquality.edition.view
import androidx.compose.foundation.background
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.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
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.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import io.github.openflocon.flocondesktop.features.network.badquality.edition.model.BadQualityConfigUiModel
import io.github.openflocon.library.designsystem.FloconTheme
import io.github.openflocon.library.designsystem.components.FloconDialogButtons
import io.github.openflocon.library.designsystem.components.FloconDialogHeader
import io.github.openflocon.library.designsystem.components.FloconTextField
import io.github.openflocon.library.designsystem.components.defaultLabel
import io.github.openflocon.library.designsystem.components.defaultPlaceHolder
@Composable
internal fun BadQualityErrorsEditor(
internal fun BadQualityHttpErrorEditor(
error: BadQualityConfigUiModel.Error,
httpType: BadQualityConfigUiModel.Error.Type.Body,
onErrorsChange: (BadQualityConfigUiModel.Error) -> Unit,
cancel: () -> Unit,
save: (BadQualityConfigUiModel.Error) -> Unit,
) {
var weight by remember(error) { mutableStateOf<String>(error.weight.toString()) }
var httpCode by remember(error) { mutableStateOf<String>(httpType.httpCode.toString()) }
var contentType by remember { mutableStateOf<String>(httpType.contentType) }
var body by remember(error) { mutableStateOf<String>(httpType.body) }
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
// bouton ajouter
Box(
modifier = Modifier
.clip(RoundedCornerShape(12.dp))
.background(FloconTheme.colorPalette.onSurface)
.clickable {
onErrorsChange(
error.copy(
weight = weight.toFloatOrNull() ?: error.weight,
type = httpType.copy(
httpCode = httpCode.toIntOrNull() ?: httpType.httpCode,
contentType = contentType,
body = body,
)
),
)
}
.padding(horizontal = 8.dp, vertical = 4.dp),
) {
Text(
"Save",
style = FloconTheme.typography.titleSmall,
color = FloconTheme.colorPalette.panel,
)
}
Column(
modifier = Modifier.fillMaxWidth(),
) {
FloconDialogHeader(
title = "Http error",
modifier = Modifier.fillMaxWidth(),
)
Column(
modifier = Modifier
.clip(RoundedCornerShape(8.dp))
.background(Color(0xFFEFEFEF).copy(alpha = 0.2f))
.padding(8.dp),
.padding(vertical = 8.dp, horizontal = 12.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
FloconTextField(
label = defaultLabel("Weight"),
@ -75,6 +52,7 @@ internal fun BadQualityErrorsEditor(
weight = it
}
},
containerColor = FloconTheme.colorPalette.panel
)
FloconTextField(
@ -86,21 +64,44 @@ internal fun BadQualityErrorsEditor(
httpCode = it
}
},
containerColor = FloconTheme.colorPalette.panel
)
FloconTextField(
label = defaultLabel("Content-Type"),
placeholder = defaultPlaceHolder("application/json"),
value = contentType,
onValueChange = { contentType = it }
onValueChange = { contentType = it },
containerColor = FloconTheme.colorPalette.panel
)
FloconTextField(
label = defaultLabel("Body"),
placeholder = defaultPlaceHolder("{\"error\":\"...\"}"),
placeholder = defaultPlaceHolder("{\n\t\"error\" : \"...\"\n}"),
value = body,
minLines = 5,
onValueChange = { body = it },
containerColor = FloconTheme.colorPalette.panel
)
}
FloconDialogButtons(
onCancel = cancel,
onValidate = {
save(
error.copy(
weight = weight.toFloatOrNull() ?: error.weight,
type = httpType.copy(
httpCode = httpCode.toIntOrNull() ?: httpType.httpCode,
contentType = contentType,
body = body,
)
),
)
},
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 4.dp),
)
}
}

View file

@ -3,6 +3,7 @@
package io.github.openflocon.flocondesktop.features.network.badquality.list.view
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@ -44,7 +45,7 @@ private fun BadNetworkQualityContent(
NetworkBadQualityContent(
lines = items,
modifier = Modifier.fillMaxSize(),
modifier = Modifier.fillMaxWidth(),
onItemClicked = viewModel::select,
onAddItemClicked = viewModel::create,
onDeleteClicked = viewModel::delete,

View file

@ -1,23 +1,18 @@
package io.github.openflocon.flocondesktop.features.network.badquality.list.view
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
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.badquality.list.model.NetworkBadQualityLineUiModel
import io.github.openflocon.library.designsystem.FloconTheme
import io.github.openflocon.library.designsystem.components.FloconButton
import io.github.openflocon.library.designsystem.components.FloconDialogHeader
@Composable
fun NetworkBadQualityContent(
@ -29,36 +24,21 @@ fun NetworkBadQualityContent(
modifier: Modifier = Modifier,
) {
Column(modifier = modifier) {
Box(
Modifier
.fillMaxWidth()
.background(FloconTheme.colorPalette.panel)
.padding(horizontal = 12.dp, vertical = 4.dp),
) {
Text(
text = "Bad network configs",
modifier = Modifier
.background(FloconTheme.colorPalette.panel)
.padding(all = 12.dp),
style = FloconTheme.typography.titleMedium,
color = FloconTheme.colorPalette.onSurface,
)
Box(
modifier = Modifier.align(Alignment.CenterEnd)
.clip(RoundedCornerShape(12.dp))
.background(FloconTheme.colorPalette.onSurface)
.clickable(onClick = onAddItemClicked)
.padding(horizontal = 8.dp, vertical = 4.dp),
) {
Text(
"Create",
style = FloconTheme.typography.titleSmall,
color = FloconTheme.colorPalette.panel,
FloconDialogHeader(
title = "Network bad quality",
modifier = Modifier.fillMaxWidth(),
trailingContent = {
FloconButton(
onClick = onAddItemClicked,
content = {
Text("Create")
}
)
}
}
},
)
LazyColumn(
modifier = Modifier.fillMaxSize(),
modifier = Modifier.fillMaxWidth()
.height(400.dp),
) {
items(lines) {
BadNetworkLineView(

View file

@ -34,12 +34,15 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import io.github.openflocon.flocondesktop.common.ui.window.FloconWindow
import io.github.openflocon.flocondesktop.common.ui.window.FloconWindowState
import io.github.openflocon.flocondesktop.common.ui.window.createFloconWindowState
import io.github.openflocon.flocondesktop.features.network.badquality.edition.model.BadQualityConfigUiModel
import io.github.openflocon.flocondesktop.features.network.badquality.edition.model.possibleExceptions
import io.github.openflocon.flocondesktop.features.network.badquality.edition.view.NetworkExceptionSelector
import io.github.openflocon.flocondesktop.features.network.mock.edition.mapper.createEditable
import io.github.openflocon.flocondesktop.features.network.mock.edition.mapper.editableToUi
import io.github.openflocon.flocondesktop.features.network.mock.edition.model.EditableMockNetworkUiModel
@ -47,10 +50,13 @@ import io.github.openflocon.flocondesktop.features.network.mock.edition.model.He
import io.github.openflocon.flocondesktop.features.network.mock.edition.model.MockNetworkUiModel
import io.github.openflocon.flocondesktop.features.network.mock.edition.model.SelectedMockUiModel
import io.github.openflocon.library.designsystem.FloconTheme
import io.github.openflocon.library.designsystem.components.FloconDialogButtons
import io.github.openflocon.library.designsystem.components.FloconDialogHeader
import io.github.openflocon.library.designsystem.components.FloconSurface
import io.github.openflocon.library.designsystem.components.FloconTextField
import io.github.openflocon.library.designsystem.components.defaultLabel
import io.github.openflocon.library.designsystem.components.defaultPlaceHolder
import java.util.UUID
@Composable
fun NetworkEditionWindow(
@ -61,7 +67,9 @@ fun NetworkEditionWindow(
onSave: (MockNetworkUiModel) -> Unit,
) {
val windowState: FloconWindowState = remember(instanceId) {
createFloconWindowState()
createFloconWindowState(
size = DpSize(900.dp, 700.dp)
)
}
key(windowState, instanceId) {
FloconWindow(
@ -109,49 +117,11 @@ fun MockEditorScreen(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
Box(
Modifier
.fillMaxWidth()
.background(FloconTheme.colorPalette.panel)
.padding(horizontal = 12.dp, vertical = 8.dp),
) {
Box(
modifier = Modifier.align(Alignment.CenterStart)
.clip(RoundedCornerShape(12.dp))
.background(FloconTheme.colorPalette.onSurface)
.clickable(onClick = onCancel)
.padding(horizontal = 8.dp, vertical = 4.dp),
) {
Text(
"Cancel",
style = FloconTheme.typography.titleSmall,
color = FloconTheme.colorPalette.panel,
)
}
Box(
modifier = Modifier.align(Alignment.CenterEnd)
.clip(RoundedCornerShape(12.dp))
.background(FloconTheme.colorPalette.onSurface)
.clickable(onClick = {
editableToUi(mock).fold(
doOnFailure = {
error = "Some fields are required"
},
doOnSuccess = {
onSave(it)
error = null
},
)
})
.padding(horizontal = 8.dp, vertical = 4.dp),
) {
Text(
"Save",
style = FloconTheme.typography.titleSmall,
color = FloconTheme.colorPalette.panel,
)
}
}
FloconDialogHeader(
modifier = Modifier
.fillMaxWidth(),
title = "Mock Edition"
)
error?.let {
Text(
@ -161,7 +131,8 @@ fun MockEditorScreen(
}
Row(
modifier = Modifier.fillMaxSize()
modifier = Modifier.fillMaxWidth()
.weight(1f)
.verticalScroll(scrollState),
) {
Column(
@ -251,9 +222,9 @@ fun MockEditorScreen(
}
when (mock.responseType) {
EditableMockNetworkUiModel.ResponseType.EXCEPTION -> {
MockNetworkErrorSelection(
NetworkExceptionSelector(
selected = mock.exceptionResponse.classPath,
onChanged = { new ->
onSelected = { new ->
mock = mock.copy(
exceptionResponse = mock.exceptionResponse.copy(
classPath = new,
@ -409,53 +380,23 @@ fun MockEditorScreen(
}
}
}
}
}
@Composable
private fun MockNetworkErrorSelection(
selected: String,
onChanged: (String) -> Unit,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
possibleExceptions.fastForEach { exception ->
val (backgroundColor, textColor) = if (exception.classPath == selected) {
FloconTheme.colorPalette.onSurface to FloconTheme.colorPalette.panel
} else {
FloconTheme.colorPalette.panel to FloconTheme.colorPalette.onSurface
}
Column(
modifier = Modifier
.clip(RoundedCornerShape(8.dp))
.background(
color = backgroundColor,
)
.then(
Modifier.clickable(
onClick = {
onChanged(exception.classPath)
},
)
).padding(all = 8.dp)
) {
Text(
text = exception.description,
style = FloconTheme.typography.bodySmall,
color = textColor,
FloconDialogButtons(
onCancel = onCancel,
onValidate = {
editableToUi(mock).fold(
doOnFailure = {
error = "Some fields are required"
},
doOnSuccess = {
onSave(it)
error = null
},
)
Text(
text = exception.classPath,
style = FloconTheme.typography.bodyMedium.copy(
fontWeight = FontWeight.Bold,
),
color = textColor,
)
}
}
},
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 4.dp),
)
}
}

View file

@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
@ -26,8 +27,9 @@ import io.github.openflocon.flocondesktop.features.network.mock.edition.view.Net
import io.github.openflocon.flocondesktop.features.network.mock.list.model.MockNetworkLineUiModel
import io.github.openflocon.flocondesktop.features.network.mock.list.model.previewMockNetworkLineUiModel
import io.github.openflocon.library.designsystem.FloconTheme
import io.github.openflocon.library.designsystem.components.FloconButton
import io.github.openflocon.library.designsystem.components.FloconDialog
import io.github.openflocon.library.designsystem.components.FloconSurface
import io.github.openflocon.library.designsystem.components.FloconDialogHeader
import org.jetbrains.compose.ui.tooling.preview.Preview
import org.koin.compose.viewmodel.koinViewModel
@ -50,7 +52,7 @@ fun NetworkMocksWindow(
) {
NetworkMocksContent(
mocks = mocks,
modifier = Modifier.fillMaxSize(),
modifier = Modifier.fillMaxWidth(),
onItemClicked = viewModel::clickOnMock,
onAddItemClicked = viewModel::createNewMock,
onDeleteClicked = viewModel::deleteMock,
@ -79,50 +81,30 @@ private fun NetworkMocksContent(
onAddItemClicked: () -> Unit,
modifier: Modifier = Modifier,
) {
FloconSurface(
modifier = modifier,
) {
Column(modifier = Modifier.fillMaxSize()) {
Box(
Modifier
.fillMaxWidth()
.background(FloconTheme.colorPalette.panel)
.padding(horizontal = 12.dp, vertical = 4.dp),
) {
Text(
text = "Mocks",
modifier = Modifier
.background(FloconTheme.colorPalette.panel)
.padding(all = 12.dp),
style = FloconTheme.typography.titleMedium,
color = FloconTheme.colorPalette.onSurface,
)
Box(
modifier = Modifier.align(Alignment.CenterEnd)
.clip(RoundedCornerShape(12.dp))
.background(FloconTheme.colorPalette.onSurface)
.clickable(onClick = onAddItemClicked)
.padding(horizontal = 8.dp, vertical = 4.dp),
Column(modifier = modifier) {
FloconDialogHeader(
title = "Mocks",
modifier = Modifier.fillMaxWidth(),
trailingContent = {
FloconButton(
onClick = onAddItemClicked,
) {
Text(
"Create",
style = FloconTheme.typography.titleSmall,
color = FloconTheme.colorPalette.panel,
)
Text("Create",)
}
}
LazyColumn(
modifier = Modifier.fillMaxSize(),
) {
items(mocks) {
MockLineView(
item = it,
onClicked = onItemClicked,
onDeleteClicked = onDeleteClicked,
changeIsEnabled = changeIsEnabled,
modifier = Modifier.fillMaxWidth(),
)
}
)
LazyColumn(
modifier = Modifier.fillMaxWidth().height(400.dp),
) {
items(mocks) {
MockLineView(
item = it,
onClicked = onItemClicked,
onDeleteClicked = onDeleteClicked,
changeIsEnabled = changeIsEnabled,
modifier = Modifier.fillMaxWidth(),
)
}
}
}

View file

@ -2,6 +2,8 @@ package io.github.openflocon.flocondesktop.common.ui.window
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowPlacement
import androidx.compose.ui.window.WindowPosition
@ -14,12 +16,15 @@ data class FloconWindowStateDesktop(
val windowState: WindowState,
) : FloconWindowState
actual fun createFloconWindowState(): FloconWindowState = FloconWindowStateDesktop(
WindowState(
placement = WindowPlacement.Floating,
position = WindowPosition(Alignment.Center),
),
)
actual fun createFloconWindowState(size: DpSize?): FloconWindowState {
return FloconWindowStateDesktop(
WindowState(
placement = WindowPlacement.Floating,
position = WindowPosition(Alignment.Center),
size = size ?: defaultWindowSize
)
)
}
@Composable
actual fun FloconWindow(

View file

@ -129,12 +129,13 @@ class BadQualityConfigLocalDataSourceImpl(
private fun defaultConfig(deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel): List<BadQualityConfigDomainModel> {
// to be sure we don't have doubles
val idPrefix = deviceIdAndPackageName.deviceId + deviceIdAndPackageName.packageName
val now = System.currentTimeMillis()
return listOf(
BadQualityConfigDomainModel(
id = idPrefix + "low_latency",
isEnabled = false,
name = "Low latency",
createdAt = Instant.fromEpochMilliseconds(System.currentTimeMillis()),
createdAt = Instant.fromEpochMilliseconds(now + 1) ,
latency = BadQualityConfigDomainModel.LatencyConfig(
minLatencyMs = 100,
maxLatencyMs = 400,
@ -147,7 +148,7 @@ class BadQualityConfigLocalDataSourceImpl(
id = idPrefix + "medium_latency",
isEnabled = false,
name = "Medium latency",
createdAt = Instant.fromEpochMilliseconds(System.currentTimeMillis()),
createdAt = Instant.fromEpochMilliseconds(now + 2),
latency = BadQualityConfigDomainModel.LatencyConfig(
minLatencyMs = 2000,
maxLatencyMs = 4000,
@ -160,7 +161,7 @@ class BadQualityConfigLocalDataSourceImpl(
id = idPrefix + "high_latency",
isEnabled = false,
name = "High latency",
createdAt = Instant.fromEpochMilliseconds(System.currentTimeMillis()),
createdAt = Instant.fromEpochMilliseconds(now + 3),
latency = BadQualityConfigDomainModel.LatencyConfig(
minLatencyMs = 10_000,
maxLatencyMs = 30_000,
@ -173,7 +174,7 @@ class BadQualityConfigLocalDataSourceImpl(
id = idPrefix + "timeout",
isEnabled = false,
name = "100% Timeout",
createdAt = Instant.fromEpochMilliseconds(System.currentTimeMillis()),
createdAt = Instant.fromEpochMilliseconds(now + 4),
latency = BadQualityConfigDomainModel.LatencyConfig(
minLatencyMs = 0,
maxLatencyMs = 0,
@ -193,7 +194,7 @@ class BadQualityConfigLocalDataSourceImpl(
id = idPrefix + "server_down",
isEnabled = false,
name = "Server Down",
createdAt = Instant.fromEpochMilliseconds(System.currentTimeMillis()),
createdAt = Instant.fromEpochMilliseconds(now + 5),
latency = BadQualityConfigDomainModel.LatencyConfig(
minLatencyMs = 0,
maxLatencyMs = 0,

View file

@ -2,19 +2,27 @@
package io.github.openflocon.library.designsystem.components
import androidx.compose.foundation.background
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.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Text
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 androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import io.github.openflocon.library.designsystem.FloconTheme
@Composable
fun FloconDialog(
@ -30,13 +38,42 @@ fun FloconDialog(
shape = RoundedCornerShape(10.dp),
modifier = modifier
.fillMaxWidth()
.height(500.dp)
.wrapContentHeight()
) {
content()
}
}
}
@Composable
fun FloconDialogHeader(
title: String,
modifier: Modifier = Modifier,
trailingContent: @Composable RowScope.() -> Unit = {},
) {
Row(
modifier
.background(FloconTheme.colorPalette.panel)
.padding(horizontal = 12.dp, vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = title,
modifier = Modifier
.padding(vertical = 12.dp)
.padding(start = 4.dp)
.weight(1f),
style = FloconTheme.typography.titleMedium,
color = FloconTheme.colorPalette.onSurface,
)
Row(
verticalAlignment = Alignment.CenterVertically,
) {
trailingContent()
}
}
}
@Composable
fun FloconDialogButtons(
onCancel: () -> Unit,
@ -59,7 +96,7 @@ fun FloconDialogButtons(
onClick = onValidate
) {
Text(
text = "Validate"
text = "Save"
)
}
}

View file

@ -211,6 +211,11 @@ private fun ContainerBox(
@Composable
fun defaultLabel(text: String): @Composable () -> Unit = {
DefaultLabel(text)
}
@Composable
fun DefaultLabel(text: String) {
Text(
text = text,
style = FloconTheme.typography.bodyMedium.copy(fontWeight = FontWeight.Thin),