fixed inital Config

This commit is contained in:
boxi committed 2026-04-04 00:00:37 +02:00
1 parent cd57e97cff
commit e8d2408c8e
6 files changed
+125 -49

No files matched your search

+1
View File
@@ -19,6 +19,7 @@ repositories {
dependencies { dependencies {
implementation(compose.desktop.currentOs) implementation(compose.desktop.currentOs)
implementation(compose.materialIconsExtended)
// Global hotkeys // Global hotkeys
implementation("com.github.kwhat:jnativehook:2.2.2") implementation("com.github.kwhat:jnativehook:2.2.2")
-14
View File
@@ -65,19 +65,6 @@ fun main() {
onSettings = { isSettingsVisible = true }, onSettings = { isSettingsVisible = true },
onExit = { exitApplication() }, onExit = { exitApplication() },
) )
client.getSpaces()
.onSuccess { spaces ->
logger.info("Available spaces:")
spaces.forEach { logger.info("Space: ${it.id} ${it.name}") }
val space = spaces.find { space -> space.id == configResult.config.spaceId }
if (space == null) {
logger.error("Selected space not found: ${configResult.config.spaceId}")
exitProcess(-1)
} else {
logger.info("Selected space: ${space.name} (${space.id})")
}
}
.onFailure { logger.error("Failed to load spaces: ${it.message}") }
} }
DisposableEffect(Unit) { DisposableEffect(Unit) {
@@ -129,7 +116,6 @@ fun main() {
} }
SettingsWindow( SettingsWindow(
config = configResult.config, config = configResult.config,
client = client,
onSave = { newConfig -> onSave = { newConfig ->
saveConfig(newConfig, configResult.sourceFile) saveConfig(newConfig, configResult.sourceFile)
configResult = ConfigResult(newConfig, configResult.sourceFile) configResult = ConfigResult(newConfig, configResult.sourceFile)
@@ -1,32 +1,19 @@
package ch.ergon.anytask.api package ch.ergon.anytask.api
import ch.ergon.anytask.api.dto.CreateObjectRequest import ch.ergon.anytask.api.dto.*
import ch.ergon.anytask.api.dto.CreateObjectResponse import io.ktor.client.*
import ch.ergon.anytask.api.dto.ObjectType import io.ktor.client.call.*
import ch.ergon.anytask.api.dto.SearchObject import io.ktor.client.engine.cio.*
import ch.ergon.anytask.api.dto.SearchObjectsResponse import io.ktor.client.plugins.*
import ch.ergon.anytask.api.dto.Space import io.ktor.client.plugins.contentnegotiation.*
import ch.ergon.anytask.api.dto.SpacesResponse import io.ktor.client.request.*
import ch.ergon.anytask.api.dto.TypesResponse import io.ktor.client.statement.*
import kotlinx.serialization.json.JsonElement import io.ktor.http.*
import io.ktor.client.HttpClient import io.ktor.http.content.OutgoingContent
import io.ktor.client.call.body import io.ktor.http.content.TextContent
import io.ktor.client.engine.cio.CIO import io.ktor.serialization.kotlinx.json.*
import io.ktor.client.plugins.HttpSend
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.plugin
import io.ktor.client.request.bearerAuth
import io.ktor.client.request.get
import io.ktor.client.request.header
import io.ktor.client.request.parameter
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.bodyAsText
import io.ktor.http.ContentType
import io.ktor.http.contentType
import io.ktor.http.isSuccess
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import kotlin.time.DurationUnit.MILLISECONDS import kotlin.time.DurationUnit.MILLISECONDS
import kotlin.time.toDuration import kotlin.time.toDuration
@@ -52,6 +39,14 @@ class AnyTypeApi(
val method = request.method.value val method = request.method.value
val url = request.url.buildString() val url = request.url.buildString()
logger.info("→ $method $url") logger.info("→ $method $url")
if (logger.isTraceEnabled) {
val bodyText = when (val body = request.body) {
is TextContent -> body.text
is OutgoingContent.ByteArrayContent -> body.bytes().decodeToString()
else -> null
}
if (bodyText != null) logger.trace("Request body: $bodyText")
}
val start = System.currentTimeMillis() val start = System.currentTimeMillis()
val call = execute(request) val call = execute(request)
val duration = (System.currentTimeMillis() - start).toDuration(MILLISECONDS) val duration = (System.currentTimeMillis() - start).toDuration(MILLISECONDS)
@@ -30,6 +30,7 @@ import androidx.compose.material.TextButton
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -40,12 +41,14 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ch.ergon.anytask.api.AnyTypeApi import ch.ergon.anytask.api.AnyTypeApi
import ch.ergon.anytask.api.dto.ObjectType import ch.ergon.anytask.api.dto.ObjectType
import ch.ergon.anytask.api.dto.Property import ch.ergon.anytask.api.dto.Property
import ch.ergon.anytask.api.dto.Space
import ch.ergon.anytask.config.Configuration import ch.ergon.anytask.config.Configuration
import ch.ergon.anytask.config.Connection import ch.ergon.anytask.config.Connection
import ch.ergon.anytask.config.PropertyConfig import ch.ergon.anytask.config.PropertyConfig
@@ -54,16 +57,19 @@ import kotlinx.coroutines.launch
@Composable @Composable
fun SettingsWindow( fun SettingsWindow(
config: Configuration, config: Configuration,
client: AnyTypeApi,
onSave: (Configuration) -> Unit, onSave: (Configuration) -> Unit,
onCancel: () -> Unit, onCancel: () -> Unit,
) { ) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
var apiKey by remember { mutableStateOf(config.connection.apiKey) } var apiKey by remember { mutableStateOf(config.connection.apiKey) }
var loadedApiKey by remember { mutableStateOf(config.connection.apiKey) }
var selectedSpaceId by remember { mutableStateOf(config.spaceId) }
var selectedTypeKey by remember { mutableStateOf(config.taskTypeKey) } var selectedTypeKey by remember { mutableStateOf(config.taskTypeKey) }
val propertyConfigs = remember { mutableStateListOf<PropertyConfig>().also { it.addAll(config.properties) } } val propertyConfigs = remember { mutableStateListOf<PropertyConfig>().also { it.addAll(config.properties) } }
var spaces by remember { mutableStateOf<List<Space>>(emptyList()) }
var isLoadingSpaces by remember { mutableStateOf(false) }
var types by remember { mutableStateOf<List<ObjectType>>(emptyList()) } var types by remember { mutableStateOf<List<ObjectType>>(emptyList()) }
var isLoadingTypes by remember { mutableStateOf(false) } var isLoadingTypes by remember { mutableStateOf(false) }
var errorMessage by remember { mutableStateOf<String?>(null) } var errorMessage by remember { mutableStateOf<String?>(null) }
@@ -71,21 +77,48 @@ fun SettingsWindow(
// Property edit state // Property edit state
var editingIndex by remember { mutableStateOf<Int?>(null) } // null=none, -1=new var editingIndex by remember { mutableStateOf<Int?>(null) } // null=none, -1=new
// Load types on start suspend fun loadSpaces(key: String) {
LaunchedEffect(Unit) { spaces = emptyList()
isLoadingSpaces = true
val tempClient = AnyTypeApi(baseUrl = config.connection.baseUrl, apiKey = key)
try {
tempClient.getSpaces()
.onSuccess { spaces = it }
.onFailure { errorMessage = "Failed to load spaces: ${it.message}" }
} finally {
tempClient.close()
}
isLoadingSpaces = false
}
suspend fun loadTypes(key: String, spaceId: String) {
types = emptyList()
isLoadingTypes = true isLoadingTypes = true
client.getTypes() val tempClient = AnyTypeApi(baseUrl = config.connection.baseUrl, apiKey = key, spaceId = spaceId)
try {
tempClient.getTypes()
.onSuccess { types = it } .onSuccess { types = it }
.onFailure { errorMessage = "Failed to load types: ${it.message}" } .onFailure { errorMessage = "Failed to load types: ${it.message}" }
} finally {
tempClient.close()
}
isLoadingTypes = false isLoadingTypes = false
} }
// Initial load
LaunchedEffect(Unit) {
loadSpaces(apiKey)
if (selectedSpaceId.isNotEmpty()) {
loadTypes(apiKey, selectedSpaceId)
}
}
val selectedType = types.find { it.key == selectedTypeKey } val selectedType = types.find { it.key == selectedTypeKey }
val availableProperties = selectedType?.properties.orEmpty() val availableProperties = selectedType?.properties.orEmpty()
fun buildConfig(): Configuration = Configuration( fun buildConfig(): Configuration = Configuration(
connection = Connection(apiKey = apiKey, baseUrl = config.connection.baseUrl), connection = Connection(apiKey = apiKey, baseUrl = config.connection.baseUrl),
spaceId = config.spaceId, spaceId = selectedSpaceId,
taskTypeKey = selectedTypeKey, taskTypeKey = selectedTypeKey,
properties = propertyConfigs.toList(), properties = propertyConfigs.toList(),
) )
@@ -96,13 +129,56 @@ fun SettingsWindow(
Text("Settings", style = MaterialTheme.typography.h6) Text("Settings", style = MaterialTheme.typography.h6)
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
// ApiKey // ApiKey + reload button
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
OutlinedTextField( OutlinedTextField(
value = apiKey, value = apiKey,
onValueChange = { apiKey = it }, onValueChange = { apiKey = it },
label = { Text("API Key") }, label = { Text("API Key") },
singleLine = true, singleLine = true,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.weight(1f).onFocusChanged { focusState ->
if (!focusState.isFocused && apiKey != loadedApiKey) {
scope.launch {
loadedApiKey = apiKey
selectedSpaceId = ""
selectedTypeKey = ""
propertyConfigs.clear()
editingIndex = null
types = emptyList()
loadSpaces(apiKey)
}
}
},
)
IconButton(onClick = {
scope.launch {
loadedApiKey = apiKey
selectedSpaceId = ""
selectedTypeKey = ""
propertyConfigs.clear()
editingIndex = null
types = emptyList()
loadSpaces(apiKey)
}
}) {
Icon(Icons.Default.Refresh, contentDescription = "Reload spaces")
}
}
Spacer(modifier = Modifier.height(12.dp))
// Space dropdown
DropdownSelector(
label = "Space",
selectedValue = spaces.find { it.id == selectedSpaceId }?.name ?: selectedSpaceId,
options = spaces.map { it.id to it.name },
onSelect = { id ->
selectedSpaceId = id
selectedTypeKey = ""
propertyConfigs.clear()
editingIndex = null
scope.launch { loadTypes(apiKey, id) }
},
isLoading = isLoadingSpaces,
) )
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
+7 -2
View File
@@ -1,12 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"> <Configuration status="WARN">
<Properties>
<Property name="CLR">{FATAL=bright red, ERROR=red, WARN=bright yellow, INFO=Normal, DEBUG=white, TRACE=white}</Property>
<Property name="MSG_CLR">{FATAL=bright red, ERROR=Normal, WARN=Normal, INFO=Normal, DEBUG=Normal, TRACE=white}</Property>
<Property name="LOG_PATTERN">%d{yy-MM-dd HH:mm:ss.SSS} %style{[%t]}{magenta} %highlight{%-5level}${CLR} %style{%logger{36}}{cyan} %highlight{: %msg%n}${MSG_CLR}</Property>
</Properties>
<Appenders> <Appenders>
<Console name="Console" target="SYSTEM_OUT"> <Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> <PatternLayout pattern="${LOG_PATTERN}" disableAnsi="false"/>
</Console> </Console>
</Appenders> </Appenders>
<Loggers> <Loggers>
<Root level="info"> <Root level="trace">
<AppenderRef ref="Console"/> <AppenderRef ref="Console"/>
</Root> </Root>
</Loggers> </Loggers>
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.