package ch.ergon.anytask.ui 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.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.Button import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.MaterialTheme import androidx.compose.material.OutlinedTextField import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.material.TextButton 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.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.input.key.Key import androidx.compose.ui.input.key.KeyEventType import androidx.compose.ui.input.key.isMetaPressed import androidx.compose.ui.input.key.key import androidx.compose.ui.input.key.onPreviewKeyEvent import androidx.compose.ui.input.key.type import androidx.compose.ui.unit.dp import ch.ergon.anytask.api.AnyTypeApi import kotlinx.coroutines.launch @Composable fun TaskWindow(client: AnyTypeApi, isVisible: Boolean, onDismiss: () -> Unit) { val scope = rememberCoroutineScope() var title by remember { mutableStateOf("") } var details by remember { mutableStateOf("") } var isLoading by remember { mutableStateOf(false) } var errorMessage by remember { mutableStateOf(null) } val focusRequester = remember { FocusRequester() } fun submit() { if (title.isBlank()) return scope.launch { isLoading = true errorMessage = null client.createTask(title = title.trim(), details = details.trim()) .onSuccess { title = "" details = "" onDismiss() } .onFailure { errorMessage = it.message } isLoading = false } } MaterialTheme { Surface(modifier = Modifier.fillMaxSize()) { Column( modifier = Modifier .fillMaxSize() .padding(24.dp) .onPreviewKeyEvent { event -> when { event.type == KeyEventType.KeyDown && event.key == Key.Escape -> { onDismiss(); true } event.type == KeyEventType.KeyDown && event.key == Key.Enter && event.isMetaPressed -> { submit(); true } else -> false } }, ) { Text("New Task", style = MaterialTheme.typography.h6) Spacer(modifier = Modifier.height(16.dp)) OutlinedTextField( value = title, onValueChange = { title = it }, label = { Text("Title") }, singleLine = true, modifier = Modifier .fillMaxWidth() .focusRequester(focusRequester), ) Spacer(modifier = Modifier.height(12.dp)) OutlinedTextField( value = details, onValueChange = { details = it }, label = { Text("Details (optional)") }, modifier = Modifier.fillMaxWidth().height(120.dp), maxLines = 4, ) if (errorMessage != null) { Spacer(modifier = Modifier.height(8.dp)) Text(errorMessage!!, color = MaterialTheme.colors.error, style = MaterialTheme.typography.caption) } Spacer(modifier = Modifier.weight(1f)) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.End), ) { TextButton(onClick = onDismiss) { Text("Cancel") } Button(onClick = ::submit, enabled = title.isNotBlank() && !isLoading) { if (isLoading) CircularProgressIndicator(modifier = Modifier.size(16.dp), strokeWidth = 2.dp) else Text("Create ⌘↵") } } } } } LaunchedEffect(isVisible) { if (isVisible) focusRequester.requestFocus() } }