Project init by claude
This commit is contained in:
1 parent
0f63311c6a
commit
830765cb13
7 files changed
+345
No files matched your search
@@ -0,0 +1,100 @@
|
||||
package ch.ergon.anytask.ui
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
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.*
|
||||
import androidx.compose.ui.unit.dp
|
||||
import ch.ergon.anytask.api.AnyTypeClient
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun TaskWindow(client: AnyTypeClient, 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<String?>(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
|
||||
.padding(24.dp)
|
||||
.onKeyEvent { 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
|
||||
}
|
||||
},
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Text("Neue Aufgabe", style = MaterialTheme.typography.h6)
|
||||
|
||||
OutlinedTextField(
|
||||
value = title,
|
||||
onValueChange = { title = it },
|
||||
label = { Text("Titel") },
|
||||
singleLine = true,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(focusRequester),
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = details,
|
||||
onValueChange = { details = it },
|
||||
label = { Text("Details (optional)") },
|
||||
modifier = Modifier.fillMaxWidth().height(100.dp),
|
||||
maxLines = 4,
|
||||
)
|
||||
|
||||
if (errorMessage != null) {
|
||||
Text(errorMessage!!, color = MaterialTheme.colors.error, style = MaterialTheme.typography.caption)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.End),
|
||||
) {
|
||||
TextButton(onClick = onDismiss) { Text("Abbrechen") }
|
||||
Button(onClick = ::submit, enabled = title.isNotBlank() && !isLoading) {
|
||||
if (isLoading) CircularProgressIndicator(modifier = Modifier.size(16.dp), strokeWidth = 2.dp)
|
||||
else Text("Erstellen ⌘↵")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) { focusRequester.requestFocus() }
|
||||
}
|
||||