feat(komga-tray): display dialog with error detail on application startup failure

Closes: #1336
This commit is contained in:
Gauthier Roebroeck 2023-12-11 16:52:23 +08:00
parent 7a8d50ce7d
commit 0fdcb2a754
4 changed files with 125 additions and 4 deletions

View file

@ -32,7 +32,7 @@ tasks {
dependencies {
implementation(project(":komga"))
implementation(compose.desktop.common)
implementation(compose.desktop.currentOs)
linuxAmd64(compose.desktop.linux_x64)
macAmd64(compose.desktop.macos_x64)

View file

@ -1,7 +1,10 @@
package org.gotson.komga
import org.gotson.komga.application.gui.showErrorDialog
import org.gotson.komga.infrastructure.util.checkTempDirectory
import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.boot.web.server.PortInUseException
import org.springframework.context.ApplicationContextException
import org.springframework.scheduling.annotation.EnableAsync
@EnableAsync
@ -14,8 +17,17 @@ fun main(args: Array<String>) {
System.setProperty("org.jooq.no-logo", "true")
System.setProperty("org.jooq.no-tips", "true")
SpringApplicationBuilder(DesktopApplication::class.java).apply {
headless(false)
run(*args)
try {
SpringApplicationBuilder(DesktopApplication::class.java).apply {
headless(false)
run(*args)
}
} catch (e: ApplicationContextException) {
val (message, stackTrace) = when (e.cause) {
is PortInUseException -> RB.getString("error_message.port_in_use", (e.cause as PortInUseException).port) to null
else -> RB.getString("error_message.unexpected") to e.stackTraceToString()
}
showErrorDialog(message, stackTrace)
}
}

View file

@ -0,0 +1,104 @@
package org.gotson.komga.application.gui
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.TextField
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.loadSvgPainter
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowPlacement
import androidx.compose.ui.window.WindowPosition
import androidx.compose.ui.window.WindowState
import androidx.compose.ui.window.application
import org.gotson.komga.RB
import org.springframework.core.io.ClassPathResource
@Preview
fun showErrorDialog(text: String, stackTrace: String? = null) {
application {
Window(
title = RB.getString("dialog_error.title"),
onCloseRequest = ::exitApplication,
visible = true,
resizable = false,
state = WindowState(
placement = WindowPlacement.Floating,
position = WindowPosition(alignment = Alignment.Center),
size = DpSize(
if (stackTrace != null) 800.dp else Dp.Unspecified,
Dp.Unspecified,
),
),
icon = loadSvgPainter(ClassPathResource("icons/komga-color.svg").inputStream, LocalDensity.current),
) {
Column(
modifier = Modifier.padding(16.dp),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(bottom = 16.dp),
) {
Image(
painter = loadSvgPainter(ClassPathResource("icons/komga-color.svg").inputStream, LocalDensity.current),
contentDescription = "Komga logo",
modifier = Modifier
.size(96.dp)
.align(Alignment.Top),
)
Text(
text,
modifier = Modifier.padding(start = 32.dp),
)
}
if (stackTrace != null)
TextField(
value = stackTrace,
onValueChange = {},
singleLine = false,
maxLines = 15,
modifier = Modifier.fillMaxWidth(),
)
Row(
horizontalArrangement = if (stackTrace != null) Arrangement.SpaceBetween else Arrangement.End,
modifier = if (stackTrace != null)
Modifier.align(Alignment.End).fillMaxWidth()
else
Modifier.align(Alignment.End),
) {
if (stackTrace != null) {
val clipboardManager = LocalClipboardManager.current
TextButton(
onClick = {
clipboardManager.setText(AnnotatedString(stackTrace))
},
) {
Text(RB.getString("dialog_error.copy_clipboard"))
}
}
Button(
onClick = { exitApplication() },
) {
Text(RB.getString("dialog_error.close"))
}
}
}
}
}
}

View file

@ -1,3 +1,8 @@
dialog_error.close=Close
dialog_error.copy_clipboard=Copy to clipboard
dialog_error.title=Komga failed to start
error_message.port_in_use=Port {} is already in use.\nKomga is probably already running.\nCheck the tray icon or menu bar for the Komga icon.
error_message.unexpected=An unexpected error occurred.
menu.open_komga=Open Komga
menu.quit=Quit Komga
menu.show_conf_dir=Open configuration directory