Добавлен индикатор загрузки и подключен снэкбар

Этот коммит содержится в:
Глеб Иваницкий 2024-08-28 11:53:32 +03:00
родитель d7aefcb03d
Коммит 7d6531af1f
3 изменённых файлов: 149 добавлений и 97 удалений

Просмотреть файл

@ -1,7 +1,10 @@
package ru.csasq.cit_is_bot package ru.csasq.cit_is_bot
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.NavigationBarItem
@ -12,7 +15,9 @@ import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
@ -65,22 +70,33 @@ fun App() {
colorScheme = darkScheme, colorScheme = darkScheme,
) { ) {
val navController = rememberNavController() val navController = rememberNavController()
val snackbarHostState = remember { val progressIndicatorState = remember {
SnackbarHostState() mutableStateOf(false)
} }
val snackbarHostState = SnackbarHostState()
val floatingActionButtonState = remember { val floatingActionButtonState = remember {
mutableStateOf<(@Composable () -> Unit)?>(null) mutableStateOf<(@Composable () -> Unit)?>(null)
} }
Scaffold( Scaffold(
topBar = { topBar = {
TopAppBar( Column {
title = { TopAppBar(
Text( title = {
text = "Сообщения", Text(
text = "Сообщения",
overflow = TextOverflow.Ellipsis,
softWrap = false,
)
},
)
when {
progressIndicatorState.value -> LinearProgressIndicator(
modifier = Modifier
.fillMaxWidth(),
) )
}, }
) }
}, },
bottomBar = { bottomBar = {
NavigationBar { NavigationBar {
@ -119,12 +135,12 @@ fun App() {
snackbarHost = { snackbarHost = {
SnackbarHost( SnackbarHost(
hostState = snackbarHostState, hostState = snackbarHostState,
) { ) { snackbarData ->
Snackbar( Snackbar(
snackbarData = it, snackbarData = snackbarData,
containerColor = MaterialTheme.colorScheme.errorContainer, // containerColor = MaterialTheme.colorScheme.errorContainer,
contentColor = MaterialTheme.colorScheme.error, // contentColor = MaterialTheme.colorScheme.error,
actionColor = MaterialTheme.colorScheme.onErrorContainer, // actionColor = MaterialTheme.colorScheme.onErrorContainer,
) )
} }
}, },
@ -145,6 +161,8 @@ fun App() {
ScenariosScreen( ScenariosScreen(
navController, navController,
paddingValues, paddingValues,
progressIndicatorState,
snackbarHostState,
floatingActionButtonState, floatingActionButtonState,
) )
} }

Просмотреть файл

@ -21,6 +21,7 @@ import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.ExtendedFloatingActionButton import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
@ -84,11 +85,15 @@ enum class MessageType(
fun ScenarioDialog( fun ScenarioDialog(
scenarioDialogState: MutableState<Scenario?>, scenarioDialogState: MutableState<Scenario?>,
client: HttpClient, client: HttpClient,
onSuccess: () -> Unit,
properties: DialogProperties = DialogProperties( properties: DialogProperties = DialogProperties(
usePlatformDefaultWidth = false, usePlatformDefaultWidth = false,
), ),
) { ) {
val coroutine = rememberCoroutineScope() val coroutine = rememberCoroutineScope()
val progressIndicatorState = remember {
mutableStateOf(false)
}
val nameState = remember { val nameState = remember {
mutableStateOf(scenarioDialogState.value?.name) mutableStateOf(scenarioDialogState.value?.name)
} }
@ -119,71 +124,98 @@ fun ScenarioDialog(
modifier = Modifier modifier = Modifier
.fillMaxSize(), .fillMaxSize(),
topBar = { topBar = {
TopAppBar( Column {
title = { TopAppBar(
Text( title = {
text = scenarioDialogState.value?.id?.let {
"Изменить сценарий"
} ?: "Добавить сценарий",
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
},
navigationIcon = {
IconButton(
onClick = {
scenarioDialogState.value = null
},
) {
Icon(
painter = painterResource(Res.drawable.baseline_close_24),
contentDescription = null,
)
}
},
actions = {
Button(
onClick = {
scenarioDialogState.value?.name = nameState.value!!
coroutine.launch {
val response = client.put("https://cit.csasq.ru/api/scripts") {
contentType(ContentType.Application.Json)
setBody(scenarioDialogState.value)
}
when(response.status) {
HttpStatusCode.Created -> scenarioDialogState.value = null
}
}
},
) {
Text( Text(
text = "Сохранить", text = scenarioDialogState.value?.id?.let {
"Изменить сценарий"
} ?: "Добавить сценарий",
overflow = TextOverflow.Ellipsis,
softWrap = false,
) )
} },
val dropdownMenuState = remember { navigationIcon = {
mutableStateOf(false) IconButton(
} onClick = {
IconButton( scenarioDialogState.value = null
onClick = { },
dropdownMenuState.value = true ) {
}, Icon(
) { painter = painterResource(Res.drawable.baseline_close_24),
Icon( contentDescription = null,
painter = painterResource(Res.drawable.baseline_more_vert_24), )
contentDescription = null, }
) },
} actions = {
DropdownMenu( Button(
expanded = dropdownMenuState.value, onClick = {
onDismissRequest = { scenarioDialogState.value?.name = nameState.value!!
dropdownMenuState.value = false coroutine.launch {
}, progressIndicatorState.value = true
) { val response = client.put("https://cit.csasq.ru/api/scripts") {
scenarioDialogState.value?.let { contentType(ContentType.Application.Json)
setBody(scenarioDialogState.value)
}
progressIndicatorState.value = false
when (response.status) {
HttpStatusCode.Created -> {
onSuccess()
scenarioDialogState.value = null
}
}
}
},
) {
Text(
text = "Сохранить",
)
}
val dropdownMenuState = remember {
mutableStateOf(false)
}
IconButton(
onClick = {
dropdownMenuState.value = true
},
) {
Icon(
painter = painterResource(Res.drawable.baseline_more_vert_24),
contentDescription = null,
)
}
DropdownMenu(
expanded = dropdownMenuState.value,
onDismissRequest = {
dropdownMenuState.value = false
},
) {
scenarioDialogState.value?.let {
DropdownMenuItem(
text = {
Text(
text = "Удалить",
)
},
onClick = {
dropdownMenuState.value = false
},
)
}
DropdownMenuItem( DropdownMenuItem(
text = { text = {
Text( Text(
text = "Удалить", text = "Сообщить о проблеме",
)
},
onClick = {
dropdownMenuState.value = false
},
)
DropdownMenuItem(
text = {
Text(
text = "Справка",
) )
}, },
onClick = { onClick = {
@ -191,29 +223,15 @@ fun ScenarioDialog(
}, },
) )
} }
DropdownMenuItem( },
text = { )
Text( when {
text = "Сообщить о проблеме", progressIndicatorState.value -> LinearProgressIndicator(
) modifier = Modifier
}, .fillMaxWidth(),
onClick = { )
dropdownMenuState.value = false }
}, }
)
DropdownMenuItem(
text = {
Text(
text = "Справка",
)
},
onClick = {
dropdownMenuState.value = false
},
)
}
},
)
}, },
floatingActionButton = { floatingActionButton = {
ExtendedFloatingActionButton( ExtendedFloatingActionButton(

Просмотреть файл

@ -8,6 +8,8 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExtendedFloatingActionButton import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@ -15,6 +17,7 @@ import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.buildAnnotatedString
@ -46,8 +49,11 @@ data class Scenario(
fun ScenariosScreen( fun ScenariosScreen(
navController: NavController, navController: NavController,
paddingValues: PaddingValues, paddingValues: PaddingValues,
progressIndicatorState: MutableState<Boolean>,
snackbarHostState: SnackbarHostState,
floatingActionButtonState: MutableState<(@Composable () -> Unit)?>, floatingActionButtonState: MutableState<(@Composable () -> Unit)?>,
) { ) {
val snackbarCoroutineScope = rememberCoroutineScope()
val verticalScrollState = rememberScrollState() val verticalScrollState = rememberScrollState()
val floatingActionButtonExpandedState = remember { val floatingActionButtonExpandedState = remember {
mutableStateOf(true) mutableStateOf(true)
@ -74,7 +80,9 @@ fun ScenariosScreen(
} }
LaunchedEffect(false) { LaunchedEffect(false) {
client.launch { client.launch {
progressIndicatorState.value = true
val response = client.get("https://cit.csasq.ru/api/scripts") val response = client.get("https://cit.csasq.ru/api/scripts")
progressIndicatorState.value = false
when (response.status) { when (response.status) {
HttpStatusCode.OK -> scenarioList.addAll(response.body()) HttpStatusCode.OK -> scenarioList.addAll(response.body())
} }
@ -127,7 +135,7 @@ fun ScenariosScreen(
} }
val captionAnnotatedString = buildAnnotatedString { val captionAnnotatedString = buildAnnotatedString {
append(it.time) append(it.time)
append(" \u2022 ") append(" \u2022 ")
append(it.messageNumber) append(it.messageNumber)
} }
SwitchButton( SwitchButton(
@ -147,6 +155,14 @@ fun ScenariosScreen(
ScenarioDialog( ScenarioDialog(
scenarioDialogState = scenarioDialogState, scenarioDialogState = scenarioDialogState,
client = client, client = client,
onSuccess = {
snackbarCoroutineScope.launch {
snackbarHostState.showSnackbar(
message = "Сценарий сохранен",
duration = SnackbarDuration.Short,
)
}
},
) )
} }
} }