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
+132 -56

No files matched your search

+1
View File
@@ -19,6 +19,7 @@ repositories {
dependencies {
implementation(compose.desktop.currentOs)
implementation(compose.materialIconsExtended)
// Global hotkeys
implementation("com.github.kwhat:jnativehook:2.2.2")
-14
View File
@@ -65,19 +65,6 @@ fun main() {
onSettings = { isSettingsVisible = true },
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) {
@@ -129,7 +116,6 @@ fun main() {
}
SettingsWindow(
config = configResult.config,
client = client,
onSave = { newConfig ->
saveConfig(newConfig, configResult.sourceFile)
configResult = ConfigResult(newConfig, configResult.sourceFile)
@@ -1,32 +1,19 @@
package ch.ergon.anytask.api
import ch.ergon.anytask.api.dto.CreateObjectRequest
import ch.ergon.anytask.api.dto.CreateObjectResponse
import ch.ergon.anytask.api.dto.ObjectType
import ch.ergon.anytask.api.dto.SearchObject
import ch.ergon.anytask.api.dto.SearchObjectsResponse
import ch.ergon.anytask.api.dto.Space
import ch.ergon.anytask.api.dto.SpacesResponse
import ch.ergon.anytask.api.dto.TypesResponse
import kotlinx.serialization.json.JsonElement
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.engine.cio.CIO
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 ch.ergon.anytask.api.dto.*
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.engine.cio.*
import io.ktor.client.plugins.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.http.content.OutgoingContent
import io.ktor.http.content.TextContent
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import org.apache.logging.log4j.LogManager
import kotlin.time.DurationUnit.MILLISECONDS
import kotlin.time.toDuration
@@ -52,6 +39,14 @@ class AnyTypeApi(
val method = request.method.value
val url = request.url.buildString()
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 call = execute(request)
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.filled.Add
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -40,12 +41,14 @@ 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.onFocusChanged
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import ch.ergon.anytask.api.AnyTypeApi
import ch.ergon.anytask.api.dto.ObjectType
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.Connection
import ch.ergon.anytask.config.PropertyConfig
@@ -54,16 +57,19 @@ import kotlinx.coroutines.launch
@Composable
fun SettingsWindow(
config: Configuration,
client: AnyTypeApi,
onSave: (Configuration) -> Unit,
onCancel: () -> Unit,
) {
val scope = rememberCoroutineScope()
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) }
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 isLoadingTypes by remember { mutableStateOf(false) }
var errorMessage by remember { mutableStateOf<String?>(null) }
@@ -71,21 +77,48 @@ fun SettingsWindow(
// Property edit state
var editingIndex by remember { mutableStateOf<Int?>(null) } // null=none, -1=new
// Load types on start
LaunchedEffect(Unit) {
suspend fun loadSpaces(key: String) {
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
client.getTypes()
.onSuccess { types = it }
.onFailure { errorMessage = "Failed to load types: ${it.message}" }
val tempClient = AnyTypeApi(baseUrl = config.connection.baseUrl, apiKey = key, spaceId = spaceId)
try {
tempClient.getTypes()
.onSuccess { types = it }
.onFailure { errorMessage = "Failed to load types: ${it.message}" }
} finally {
tempClient.close()
}
isLoadingTypes = false
}
// Initial load
LaunchedEffect(Unit) {
loadSpaces(apiKey)
if (selectedSpaceId.isNotEmpty()) {
loadTypes(apiKey, selectedSpaceId)
}
}
val selectedType = types.find { it.key == selectedTypeKey }
val availableProperties = selectedType?.properties.orEmpty()
fun buildConfig(): Configuration = Configuration(
connection = Connection(apiKey = apiKey, baseUrl = config.connection.baseUrl),
spaceId = config.spaceId,
spaceId = selectedSpaceId,
taskTypeKey = selectedTypeKey,
properties = propertyConfigs.toList(),
)
@@ -96,13 +129,56 @@ fun SettingsWindow(
Text("Settings", style = MaterialTheme.typography.h6)
Spacer(modifier = Modifier.height(16.dp))
// ApiKey
OutlinedTextField(
value = apiKey,
onValueChange = { apiKey = it },
label = { Text("API Key") },
singleLine = true,
modifier = Modifier.fillMaxWidth(),
// ApiKey + reload button
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
OutlinedTextField(
value = apiKey,
onValueChange = { apiKey = it },
label = { Text("API Key") },
singleLine = true,
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))
+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"?>
<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>
<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>
</Appenders>
<Loggers>
<Root level="info">
<Root level="trace">
<AppenderRef ref="Console"/>
</Root>
</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.