diff --git a/.gitignore b/.gitignore index b63da45..be65145 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,7 @@ bin/ .vscode/ ### Mac OS ### -.DS_Store \ No newline at end of file +.DS_Store + +## Seems to me a bug to unzip into the project root dir +/wix** \ No newline at end of file diff --git a/.idea/artifacts/koalageddon_jvm_2_0_0.xml b/.idea/artifacts/koalageddon_jvm_2_0_1.xml similarity index 63% rename from .idea/artifacts/koalageddon_jvm_2_0_0.xml rename to .idea/artifacts/koalageddon_jvm_2_0_1.xml index 6cdd2de..df76e91 100644 --- a/.idea/artifacts/koalageddon_jvm_2_0_0.xml +++ b/.idea/artifacts/koalageddon_jvm_2_0_1.xml @@ -1,7 +1,7 @@ - + $PROJECT_DIR$/build/libs - + diff --git a/build.gradle.kts b/build.gradle.kts index c1151e3..ad0d8bb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,7 @@ plugins { val author = "acidicoala" val projectName = "Koalageddon" -val appVersion = "2.0.0" +val appVersion = "2.0.1" group = author version = appVersion @@ -48,7 +48,7 @@ kotlin { implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1") // https://github.com/kosi-libs/Kodein - implementation("org.kodein.di:kodein-di-framework-compose:7.16.0") + implementation("org.kodein.di:kodein-di-framework-compose:7.18.0") // https://github.com/harawata/appdirs implementation("net.harawata:appdirs:1.2.1") @@ -57,7 +57,7 @@ kotlin { implementation("org.slf4j:slf4j-api:2.0.6") // https://github.com/tinylog-org/tinylog - val tinylogVersion = "2.5.0" + val tinylogVersion = "2.6.0" implementation("org.tinylog:tinylog-impl:$tinylogVersion") implementation("org.tinylog:slf4j-tinylog:$tinylogVersion") @@ -65,7 +65,7 @@ kotlin { implementation("com.dorkbox:PeParser:3.1") // https://github.com/ktorio/ktor - val ktorVersion = "2.2.2" + val ktorVersion = "2.2.3" implementation("io.ktor:ktor-client-core:$ktorVersion") implementation("io.ktor:ktor-client-cio:$ktorVersion") implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion") diff --git a/gradle.properties b/gradle.properties index 9a50be0..fc8ae35 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ kotlin.code.style=official -kotlin.version=1.7.20 -compose.version=1.2.2 +kotlin.version=1.8.0 +compose.version=1.3.0 kotlin.mpp.stability.nowarn=true \ No newline at end of file diff --git a/rules.pro b/rules.pro index b62f971..bbad899 100644 --- a/rules.pro +++ b/rules.pro @@ -10,6 +10,7 @@ # Tinylog <-> SLF4J bridge -keep class org.tinylog.** { *; } +-keep class org.slf4j.** { *; } # Java native access # Source: https://github.com/java-native-access/jna/issues/1187#issuecomment-626251894 diff --git a/src/jvmMain/kotlin/acidicoala/koalageddon/core/model/AppPaths.kt b/src/jvmMain/kotlin/acidicoala/koalageddon/core/model/AppPaths.kt index 29fb1b6..bfb018b 100644 --- a/src/jvmMain/kotlin/acidicoala/koalageddon/core/model/AppPaths.kt +++ b/src/jvmMain/kotlin/acidicoala/koalageddon/core/model/AppPaths.kt @@ -34,6 +34,8 @@ class AppPaths { fun getUnlockerConfig(unlocker: KoalaTool) = getUnlockerDir(unlocker) / unlocker.configName + fun getUnlockerLog(unlocker: KoalaTool) = getUnlockerDir(unlocker) / unlocker.logName + fun getUnlockerDll(unlocker: KoalaTool) = getUnlockerDir(unlocker) / "${unlocker.name}.dll" fun getCacheAsset(filename: String) = cacheDir / filename diff --git a/src/jvmMain/kotlin/acidicoala/koalageddon/core/model/KoalaTool.kt b/src/jvmMain/kotlin/acidicoala/koalageddon/core/model/KoalaTool.kt index 0dcb0d1..daf324b 100644 --- a/src/jvmMain/kotlin/acidicoala/koalageddon/core/model/KoalaTool.kt +++ b/src/jvmMain/kotlin/acidicoala/koalageddon/core/model/KoalaTool.kt @@ -20,6 +20,7 @@ sealed class KoalaTool( val majorVersion: Int ) { val configName = "$name.config.json" + val logName = "$name.log.log" val homePage = "https://github.com/acidicoala/$name#readme" val gitHubReleaseUrl = "https://api.github.com/repos/acidicoala/$name/releases" diff --git a/src/jvmMain/kotlin/acidicoala/koalageddon/core/model/Settings.kt b/src/jvmMain/kotlin/acidicoala/koalageddon/core/model/Settings.kt index a60f84d..6935187 100644 --- a/src/jvmMain/kotlin/acidicoala/koalageddon/core/model/Settings.kt +++ b/src/jvmMain/kotlin/acidicoala/koalageddon/core/model/Settings.kt @@ -20,7 +20,7 @@ data class Settings constructor( langTag.startsWith("de", ignoreCase = true) -> Language.German langTag.startsWith("it", ignoreCase = true) -> Language.Italian langTag.startsWith("ru", ignoreCase = true) -> Language.Russian - langTag.startsWith("pt", ignoreCase = true) -> Language.Brazilian_Portuguese + langTag.startsWith("pt", ignoreCase = true) -> Language.BrazilianPortuguese langTag.startsWith("zh", ignoreCase = true) -> Language.SimplifiedChinese langTag.startsWith("tr", ignoreCase = true) -> Language.Turkish else -> Language.English @@ -38,39 +38,41 @@ data class Settings constructor( } } + // Languages are sorted according to their ISO 639-1 code + @Serializable enum class Language(val locale: Locale) : ILangString { - English(Locale.ENGLISH) { - override fun text(strings: Strings) = strings.languageEn - }, - German(Locale.GERMAN) { + German(Locale("de")) { override fun text(strings: Strings) = strings.languageDe }, - Italian(Locale.ITALIAN) { + English(Locale("en")) { + override fun text(strings: Strings) = strings.languageEn + }, + Italian(Locale("it")) { override fun text(strings: Strings) = strings.languageIt }, + BrazilianPortuguese(Locale("pt", "BR")) { + override fun text(strings: Strings) = strings.languagePtBr + }, Russian(Locale("ru")) { override fun text(strings: Strings) = strings.languageRu }, - Brazilian_Portuguese(Locale("pt")) { - override fun text(strings: Strings) = strings.languagePt_BR - }, - SimplifiedChinese(Locale.SIMPLIFIED_CHINESE) { - override fun text(strings: Strings) = strings.languageCHS - }, Turkish(Locale("tr")) { override fun text(strings: Strings) = strings.languageTr - } + }, + SimplifiedChinese(Locale("zh", "CN")) { + override fun text(strings: Strings) = strings.languageZhCn + }, } @Transient val strings = when (language) { - Language.English -> Strings.English Language.German -> Strings.German + Language.English -> Strings.English Language.Italian -> Strings.Italian + Language.BrazilianPortuguese -> Strings.BrazilianPortuguese Language.Russian -> Strings.Russian - Language.Brazilian_Portuguese -> Strings.Brazilian_Portuguese - Language.SimplifiedChinese -> Strings.SimplifiedChinese Language.Turkish -> Strings.Turkish + Language.SimplifiedChinese -> Strings.SimplifiedChinese } } diff --git a/src/jvmMain/kotlin/acidicoala/koalageddon/core/ui/composable/DropdownButton.kt b/src/jvmMain/kotlin/acidicoala/koalageddon/core/ui/composable/DropdownButton.kt index c1ab83b..f5833f0 100644 --- a/src/jvmMain/kotlin/acidicoala/koalageddon/core/ui/composable/DropdownButton.kt +++ b/src/jvmMain/kotlin/acidicoala/koalageddon/core/ui/composable/DropdownButton.kt @@ -2,19 +2,24 @@ package acidicoala.koalageddon.core.ui.composable import acidicoala.koalageddon.core.model.ILangString import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.width import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.unit.dp @Composable fun DropdownButton( selected: T, items: Array, onSelect: (T) -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + icon: ImageVector? = null, ) { var dropdownExpanded by remember { mutableStateOf(false) } @@ -22,6 +27,15 @@ fun DropdownButton( Button( onClick = { dropdownExpanded = !dropdownExpanded }, ) { + if (icon != null) { + Icon( + imageVector = icon, + contentDescription = null, + modifier = Modifier.align(Alignment.CenterVertically) + ) + + Spacer(Modifier.width(8.dp)) + } Text(text = selected.text) diff --git a/src/jvmMain/kotlin/acidicoala/koalageddon/core/ui/composable/DropdownOption.kt b/src/jvmMain/kotlin/acidicoala/koalageddon/core/ui/composable/DropdownOption.kt index 6f36eeb..8bd42fb 100644 --- a/src/jvmMain/kotlin/acidicoala/koalageddon/core/ui/composable/DropdownOption.kt +++ b/src/jvmMain/kotlin/acidicoala/koalageddon/core/ui/composable/DropdownOption.kt @@ -2,14 +2,22 @@ package acidicoala.koalageddon.core.ui.composable import acidicoala.koalageddon.core.model.ILangString import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.vector.ImageVector @Composable -fun DropdownOption(label: String, items: Array, selected: T, onSelect: (T) -> Unit) { +fun DropdownOption( + label: String, + items: Array, + selected: T, + onSelect: (T) -> Unit, + icon: ImageVector? = null, +) { ControlOption(label) { DropdownButton( selected = selected, items = items, onSelect = onSelect, + icon = icon, ) } } diff --git a/src/jvmMain/kotlin/acidicoala/koalageddon/core/use_case/ModifyInstallationStatus.kt b/src/jvmMain/kotlin/acidicoala/koalageddon/core/use_case/ModifyInstallationStatus.kt index fd36801..fba6c36 100644 --- a/src/jvmMain/kotlin/acidicoala/koalageddon/core/use_case/ModifyInstallationStatus.kt +++ b/src/jvmMain/kotlin/acidicoala/koalageddon/core/use_case/ModifyInstallationStatus.kt @@ -63,7 +63,12 @@ class ModifyInstallationStatus(override val di: DI) : DIAware { destination = paths.getUnlockerDll(unlocker), ) - unlocker.writeConfig(path = paths.getUnlockerConfig(unlocker), unlocker.defaultConfig) + // Ensure that config file exists + try { + unlocker.parseConfig(paths.getUnlockerConfig(unlocker)) + } catch (e: Exception) { + unlocker.writeConfig(path = paths.getUnlockerConfig(unlocker), unlocker.defaultConfig) + } } private fun uninstall(store: Store) = channelFlow { diff --git a/src/jvmMain/kotlin/acidicoala/koalageddon/core/use_case/OpenResourceLink.kt b/src/jvmMain/kotlin/acidicoala/koalageddon/core/use_case/OpenResourceLink.kt index e0de329..7a25526 100644 --- a/src/jvmMain/kotlin/acidicoala/koalageddon/core/use_case/OpenResourceLink.kt +++ b/src/jvmMain/kotlin/acidicoala/koalageddon/core/use_case/OpenResourceLink.kt @@ -1,6 +1,9 @@ package acidicoala.koalageddon.core.use_case import acidicoala.koalageddon.core.logging.AppLogger +import acidicoala.koalageddon.core.model.LangString +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch import org.kodein.di.DI import org.kodein.di.DIAware import org.kodein.di.instance @@ -10,16 +13,34 @@ import java.nio.file.Path class OpenResourceLink(override val di: DI) : DIAware { private val logger: AppLogger by instance() + private val showSnackbar: ShowSnackbar by instance() + private val scope: CoroutineScope by instance() operator fun invoke(path: Path) { - logger.debug("Opening directory in explorer: $path") + try { + logger.debug("Opening file/directory in explorer: $path") - Desktop.getDesktop().open(path.toFile()) + Desktop.getDesktop().open(path.toFile()) + } catch (e: Exception) { + logger.error(e, "Error opening file/directory in explorer") + + scope.launch { + showSnackbar(LangString { error }) + } + } } operator fun invoke(uri: URI) { - logger.debug("Opening uri: $uri") + try { + logger.debug("Opening URI: $uri") + + Desktop.getDesktop().browse(uri) + } catch (e: Exception) { + logger.error(e, "Error opening URI") - Desktop.getDesktop().browse(uri) + scope.launch { + showSnackbar(LangString { error }) + } + } } } \ No newline at end of file diff --git a/src/jvmMain/kotlin/acidicoala/koalageddon/core/values/Strings.kt b/src/jvmMain/kotlin/acidicoala/koalageddon/core/values/Strings.kt index f8d13ed..8649d07 100644 --- a/src/jvmMain/kotlin/acidicoala/koalageddon/core/values/Strings.kt +++ b/src/jvmMain/kotlin/acidicoala/koalageddon/core/values/Strings.kt @@ -21,6 +21,7 @@ sealed class Strings( val dlcId: String = "DLC ID", val downloadingRelease: String, val downloadPreReleaseVersions: String, + val error: String, val extraInventoryItems: String, val fetchingToolInfo: String, val gameMode: String, @@ -38,14 +39,15 @@ sealed class Strings( val languageDe: String = "Deutsch", val languageIt: String = "Italiano", val languageRu: String = "Русский", - val languagePt_BR: String = "Português (Brasil)", - val languageCHS: String = "简体中文", + val languagePtBr: String = "Português (Brasil)", + val languageZhCn: String = "简体中文", val languageTr: String = "Türkçe", val logging: String, val modifyInstallation: String, val ok: String = "OK", val openDataDirectory: String, val openLatestReleasePage: String, + val openLogFile: String, val openOfficialForumTopic: String, val overrideAppStatus: String, val overrideDlcStatus: String, @@ -78,70 +80,6 @@ sealed class Strings( val unlockFamilySharing: String, val version: String, ) { - object English : Strings( - autoInjectInventory = "Automatically inject items into an in-game Steam inventory", - appStatusOriginal = "Original", - appStatusUnlocked = "Unlocked", - appStatusLocked = "Locked", - buildTimestamp = "Build timestamp", - cacheSize = "Cache size: %0", - cancel = "Cancel", - checkForUpdates = "Check for updates", - clearCache = "Clear cache", - computing = "Computing", - configuration = "Configuration", - confirmForceModifyInstallationMessage = "Process %0 has been found currently running on your system. " + - "To modify installation status, it is necessary to close this process. " + - "Do you wish to proceed with installation by forcibly closing it?", - confirmForceModifyInstallationTitle = "Proceed with installation / removal?", - defaultAppStatus = "Default game status", - downloadingRelease = "Downloading %0 release: %1 out of %2", - downloadPreReleaseVersions = "Download pre-release versions of tools", - extraInventoryItems = "Inject extra items into an in-game Steam inventory", - fetchingToolInfo = "Fetching %0 info", - gameMode = "Game mode", - inDevelopment = "In development", - information = "Information", - install = "Install", - installation = "Installation", - installationError = "❌ Installation error", - installationStatus = "Installation status", - installationSuccess = "✅ Installation was successful", - installed = "Installed %0", - language = "Language", - logging = "Logging", - modifyInstallation = "Modify installation", - openDataDirectory = "Open data directory", - openLatestReleasePage = "Open latest release page", - openOfficialForumTopic = "Open official forum topic", - overrideAppStatus = "Override game status", - overrideDlcStatus = "Override DLC status", - notInstalled = "Not installed", - processStatusRunning = "Running", - processStatusNotRunning = "Not running", - refreshStatus = "Refresh status", - reloadConfig = "Reload configuration", - reloadConfigTooltip = "Reloads configuration in a running Steam process", - reloadConfigSuccess = "✅ Configuration was successfully reloaded", - reloadConfigError = "❌ Error reloading configuration", - restoreDefaultConfiguration = "Restore default configuration", - settings = "Settings", - startPage = "Start page", - startPageWelcome = "Welcome to Koalageddon v%0", - storeMode = "Store mode", - storeProcessStatus = "%0 process status", - theme = "Theme", - themeDark = "Dark", - themeLight = "Light", - toolConfig = "%0 config", - removalError = "❌ Removal error", - removalSuccess = "✅ Removal success", - remove = "Remove", - unlocker = "Unlocker", - unlockFamilySharing = "Unlock Family Sharing", - version = "Version v${BuildConfig.APP_VERSION}", - ) - object German : Strings( autoInjectInventory = "Gegenstände automatisch zum Steam Inventar unterstützter Spiele hinzufügen", appStatusOriginal = "Original", @@ -161,6 +99,7 @@ sealed class Strings( defaultAppStatus = "Standard Spielstatus", downloadingRelease = "Lade %0 Veröffentlichung herunter: %1 von %2", downloadPreReleaseVersions = "Vorveröffentlichungen der Tools herunterladen", + error = "❌ Fehler", extraInventoryItems = "Zusätzliche Gegenstände zum Steam Inventar unterstützter Spiele hinzufügen", fetchingToolInfo = "Rufe %0 Daten ab", gameMode = "Spielmodus", @@ -177,6 +116,7 @@ sealed class Strings( modifyInstallation = "Installation modifizieren", openDataDirectory = "Datenordner öffnen", openLatestReleasePage = "Seite der neuesten Veröffentlichung öffnen", + openLogFile = "Protokolldatei öffnen", openOfficialForumTopic = "Seite des offiziellen Forum-Themas öffnen", overrideAppStatus = "Spielstatus überschreiben", overrideDlcStatus = "DLC Status überschreiben", @@ -206,6 +146,72 @@ sealed class Strings( version = "Version v${BuildConfig.APP_VERSION}", ) + object English : Strings( + autoInjectInventory = "Automatically inject items into an in-game Steam inventory", + appStatusOriginal = "Original", + appStatusUnlocked = "Unlocked", + appStatusLocked = "Locked", + buildTimestamp = "Build timestamp", + cacheSize = "Cache size: %0", + cancel = "Cancel", + checkForUpdates = "Check for updates", + clearCache = "Clear cache", + computing = "Computing", + configuration = "Configuration", + confirmForceModifyInstallationMessage = "Process %0 has been found currently running on your system. " + + "To modify installation status, it is necessary to close this process. " + + "Do you wish to proceed with installation by forcibly closing it?", + confirmForceModifyInstallationTitle = "Proceed with installation / removal?", + defaultAppStatus = "Default game status", + downloadingRelease = "Downloading %0 release: %1 out of %2", + downloadPreReleaseVersions = "Download pre-release versions of tools", + error = "❌ Error", + extraInventoryItems = "Inject extra items into an in-game Steam inventory", + fetchingToolInfo = "Fetching %0 info", + gameMode = "Game mode", + inDevelopment = "In development", + information = "Information", + install = "Install", + installation = "Installation", + installationError = "❌ Installation error", + installationStatus = "Installation status", + installationSuccess = "✅ Installation was successful", + installed = "Installed %0", + language = "Language", + logging = "Logging", + modifyInstallation = "Modify installation", + openDataDirectory = "Open data directory", + openLatestReleasePage = "Open latest release page", + openLogFile = "Open log file", + openOfficialForumTopic = "Open official forum topic", + overrideAppStatus = "Override game status", + overrideDlcStatus = "Override DLC status", + notInstalled = "Not installed", + processStatusRunning = "Running", + processStatusNotRunning = "Not running", + refreshStatus = "Refresh status", + reloadConfig = "Reload configuration", + reloadConfigTooltip = "Reloads configuration in a running Steam process", + reloadConfigSuccess = "✅ Configuration was successfully reloaded", + reloadConfigError = "❌ Error reloading configuration", + restoreDefaultConfiguration = "Restore default configuration", + settings = "Settings", + startPage = "Start page", + startPageWelcome = "Welcome to Koalageddon v%0", + storeMode = "Store mode", + storeProcessStatus = "%0 process status", + theme = "Theme", + themeDark = "Dark", + themeLight = "Light", + toolConfig = "%0 config", + removalError = "❌ Removal error", + removalSuccess = "✅ Removal success", + remove = "Remove", + unlocker = "Unlocker", + unlockFamilySharing = "Unlock Family Sharing", + version = "Version v${BuildConfig.APP_VERSION}", + ) + object Italian : Strings( autoInjectInventory = "Inserisci automaticamente oggetti in un inventario di Steam in-game", appStatusOriginal = "Originale", @@ -225,6 +231,7 @@ sealed class Strings( defaultAppStatus = "Stato di gioco predefinito", downloadingRelease = "Scaricando %0: %1 su %2", downloadPreReleaseVersions = "Scarica la versione pre-release degli strumenti", + error = "❌ Errore", extraInventoryItems = "Inserisci oggetti extra in un inventario Steam in-game", fetchingToolInfo = "Acquisizione dati %0", gameMode = "Modalità gioco", @@ -241,6 +248,7 @@ sealed class Strings( modifyInstallation = "Modifica installazione", openDataDirectory = "Apri cartella dati", openLatestReleasePage = "Apri la pagina dell'ultima versione", + openLogFile = "Apri il file di registro", openOfficialForumTopic = "Apri la pagina dell'argomento sul forum ufficiale", overrideAppStatus = "Sovrascrivi lo stato del gioco", overrideDlcStatus = "Sovrascrivi lo stato dei DLC", @@ -270,6 +278,72 @@ sealed class Strings( version = "Versione v${BuildConfig.APP_VERSION}", ) + object BrazilianPortuguese : Strings( + autoInjectInventory = "Injetar automaticamente itens no inventário steam dentro do jogo", + appStatusOriginal = "Original", + appStatusUnlocked = "Desbloqueado", + appStatusLocked = "Bloqueado", + buildTimestamp = "Data/Hora da Build", + cacheSize = "Tamanho do cache: %0", + cancel = "Cancelar", + checkForUpdates = "Verificar se há atualizações", + clearCache = "Limpar cache", + computing = "Computando", + configuration = "Configurações", + confirmForceModifyInstallationMessage = "Processo %0 foi encontrado execução no seu sistema. " + + "Para modificar o status da instalação, é necessário encerrar este processo. " + + "Você deseja continuar com a instalação encerrando o processo?", + confirmForceModifyInstallationTitle = "Continuar com a instalação / desinstalação?", + defaultAppStatus = "Status padrão do jogo", + downloadingRelease = "Baixando a versão %0: %1 de %2", + downloadPreReleaseVersions = "Download de versões pré-lançamento das ferramentas", + error = "❌ Erro", + extraInventoryItems = "Injetar itens extras no inventário steam dentro do jogo", + fetchingToolInfo = "Recebendo informações %0", + gameMode = "Modo de jogo", + inDevelopment = "Em desenvolvimento", + information = "Informação", + install = "Instalar", + installation = "Instalação", + installationError = "❌ Erro de instalação", + installationStatus = "Status de instalação", + installationSuccess = "✅ A instalação foi concluida", + installed = "Instalado %0", + language = "Linguagem", + logging = "Manter Log", + modifyInstallation = "Modificar instalação", + openDataDirectory = "Abrir diretório dos dados", + openLatestReleasePage = "Abrir a página da versão mais recente", + openLogFile = "Abrir arquivo de registro", + openOfficialForumTopic = "Abrir tópico do fórum oficial", + overrideAppStatus = "Substituir status do jogo", + overrideDlcStatus = "Substituir status da DLC", + notInstalled = "Não instalado", + processStatusRunning = "Em execução", + processStatusNotRunning = "Não está em execução", + refreshStatus = "Atualizar status", + reloadConfig = "Recarregar configuração", + reloadConfigTooltip = "Recarregar a configuração em um processo Steam em execução", + reloadConfigSuccess = "✅ A configuração foi recarregada com sucesso", + reloadConfigError = "❌ Erro ao recarregar a configuração", + restoreDefaultConfiguration = "Restaurar configuração padrão", + settings = "Configurações", + startPage = "Página inicial", + startPageWelcome = "Bem-vindo ao Koalageddon v%0", + storeMode = "Modo loja", + storeProcessStatus = "Status do processo %0", + theme = "Tema", + themeDark = "Escuro", + themeLight = "Claro", + toolConfig = "Configuração do(a) %0", + removalError = "❌ Erro na Remoção", + removalSuccess = "✅ Removido com sucesso", + remove = "Remover", + unlocker = "Desbloquear", + unlockFamilySharing = "Desbloquear o Compartilhamento de Biblioteca (Family Sharing)", + version = "Versão v${BuildConfig.APP_VERSION}", + ) + object Russian : Strings( autoInjectInventory = "Автоматически внедрять предметы во внутриигровой Steam инвентарь", appStatusOriginal = "Оригинальный", @@ -289,6 +363,7 @@ sealed class Strings( defaultAppStatus = "Статус игр по умолчанию", downloadingRelease = "Загрузка %0 релиза: %1 из %2", downloadPreReleaseVersions = "Загружать предварительные версии инструментов", + error = "❌ Ошибка", extraInventoryItems = "Внедрить дополнительные предметы во внутриигровой Steam инвентарь", fetchingToolInfo = "Получение данных %0", gameMode = "Режим игры", @@ -305,6 +380,7 @@ sealed class Strings( modifyInstallation = "Изменить установку", openDataDirectory = "Открыть директорию данных", openLatestReleasePage = "Открыть страницу последнего релиза", + openLogFile = "Открыть файл журнала", openOfficialForumTopic = "Открыть официальный топик форума", overrideAppStatus = "Заменить статус игры", overrideDlcStatus = "Заменить статус DLC", @@ -334,72 +410,74 @@ sealed class Strings( version = "Версия v${BuildConfig.APP_VERSION}", ) - object Brazilian_Portuguese : Strings( - autoInjectInventory = "Injetar automaticamente itens no inventário steam dentro do jogo", - appStatusOriginal = "Original", - appStatusUnlocked = "Desbloqueado", - appStatusLocked = "Bloqueado", - buildTimestamp = "Data/Hora da Build", - cacheSize = "Tamanho do cache: %0", - cancel = "Cancelar", - checkForUpdates = "Verificar se há atualizações", - clearCache = "Limpar cache", - computing = "Computando", - configuration = "Configurações", - confirmForceModifyInstallationMessage = "Processo %0 foi encontrado execução no seu sistema. " + - "Para modificar o status da instalação, é necessário encerrar este processo. " + - "Você deseja continuar com a instalação encerrando o processo?", - confirmForceModifyInstallationTitle = "Continuar com a instalação / desinstalação?", - defaultAppStatus = "Status padrão do jogo", - downloadingRelease = "Baixando a versão %0: %1 de %2", - downloadPreReleaseVersions = "Download de versões pré-lançamento das ferramentas", - extraInventoryItems = "Injetar itens extras no inventário steam dentro do jogo", - fetchingToolInfo = "Recebendo informações %0", - gameMode = "Modo de jogo", - inDevelopment = "Em desenvolvimento", - information = "Informação", - install = "Instalar", - installation = "Instalação", - installationError = "❌ Erro de instalação", - installationStatus = "Status de instalação", - installationSuccess = "✅ A instalação foi concluida", - installed = "Instalado %0", - language = "Linguagem", - logging = "Manter Log", - modifyInstallation = "Modificar instalação", - openDataDirectory = "Abrir diretório dos dados", - openLatestReleasePage = "Abrir a página da versão mais recente", - openOfficialForumTopic = "Abrir tópico do fórum oficial", - overrideAppStatus = "Substituir status do jogo", - overrideDlcStatus = "Substituir status da DLC", - notInstalled = "Não instalado", - processStatusRunning = "Em execução", - processStatusNotRunning = "Não está em execução", - refreshStatus = "Atualizar status", - reloadConfig = "Recarregar configuração", - reloadConfigTooltip = "Recarregar a configuração em um processo Steam em execução", - reloadConfigSuccess = "✅ A configuração foi recarregada com sucesso", - reloadConfigError = "❌ Erro ao recarregar a configuração", - restoreDefaultConfiguration = "Restaurar configuração padrão", - settings = "Configurações", - startPage = "Página inicial", - startPageWelcome = "Bem-vindo ao Koalageddon v%0", - storeMode = "Modo loja", - storeProcessStatus = "Status do processo %0", + object Turkish : Strings( + autoInjectInventory = "Öğeleri oyun içi bir Steam envanterine otomatik olarak enjekte edin", + appStatusOriginal = "Orijinal", + appStatusUnlocked = "Kilidi Açıldı", + appStatusLocked = "Kilitlendi", + buildTimestamp = "Zaman damgası oluştur", + cacheSize = "Önbellek boyutu: %0", + cancel = "İptal", + checkForUpdates = "Güncelleştirmeleri denetle", + clearCache = "Önbelleği temizle", + computing = "Bilgi İşlemi", + configuration = "Yapılandırma", + confirmForceModifyInstallationMessage = "%0 işlemi şu anda sisteminizde çalışmakta olduğu bulundu. " + + "Yükleme durumunu değiştirmek için bu işlemi kapatmak gerekir. " + + "Zorla kapatarak yüklemeye devam etmek istiyor musunuz?", + confirmForceModifyInstallationTitle = "Kurulum / kaldırma işlemine devam edilsin mi?", + defaultAppStatus = "Varsayılan oyun durumu", + downloadingRelease = "%0 sürümü indiriliyor: %2'den %1", + downloadPreReleaseVersions = "Araçların yayınlanma öncesi sürümlerini indirin", + error = "❌ Hata", + extraInventoryItems = "Oyun içi bir Steam envanterine fazladan öğeler ekleyin", + fetchingToolInfo = "%0 bilgisi getiriliyor", + gameMode = "Oyun Modu", + inDevelopment = "Geliştirme aşamasında", + information = "Bilgi", + install = "Yükle", + installation = "Yükleme", + installationError = "❌ Yükleme hatası", + installationStatus = "Yükleme durumu", + installationSuccess = "✅ Yükleme başarıyla gerçekleşti", + installed = "%0 Yüklendi", + language = "Dil", + logging = "Günlük dosyasını yazmak", + modifyInstallation = "Kurulumu değiştir", + openDataDirectory = "Veri dizinini aç", + openLatestReleasePage = "En son sürüm sayfasını aç", + openLogFile = "Günlük dosyasını aç", + openOfficialForumTopic = "Resmi forum konusunu aç", + overrideAppStatus = "Oyun durumunu geçersiz kıl", + overrideDlcStatus = "DLC durumunu geçersiz kıl", + notInstalled = "Yüklü değil", + processStatusRunning = "Çalışıyor", + processStatusNotRunning = "Çalışmıyor", + refreshStatus = "Durumu yenile", + reloadConfig = "Yapılandırmayı yeniden yükle", + reloadConfigTooltip = "Çalışan bir Steam işleminde yapılandırmayı yeniden yükler", + reloadConfigSuccess = "✅ Yapılandırma başarıyla yeniden yüklendi", + reloadConfigError = "❌ Yapılandırma yeniden yüklenirken hata oluştu", + restoreDefaultConfiguration = "Varsayılan yapılandırmayı geri yükle", + settings = "Ayarlar", + startPage = "Başlangıç sayfası", + startPageWelcome = "Koalageddon v%0'a hoş geldiniz! Çeviri: @caner__karaca", + storeMode = "Mağaza modu", + storeProcessStatus = "%0 işlem durumu", theme = "Tema", - themeDark = "Escuro", - themeLight = "Claro", - toolConfig = "Configuração do(a) %0", - removalError = "❌ Erro na Remoção", - removalSuccess = "✅ Removido com sucesso", - remove = "Remover", - unlocker = "Desbloquear", - unlockFamilySharing = "Desbloquear o Compartilhamento de Biblioteca (Family Sharing)", - version = "Versão v${BuildConfig.APP_VERSION}", + themeDark = "Karanlık", + themeLight = "Aydınlık", + toolConfig = "%0 yapılandırma", + removalError = "❌ Kaldırma hatası", + removalSuccess = "✅ Kaldırma başarılı", + remove = "Kaldır", + unlocker = "Kilit açıcı", + unlockFamilySharing = "Aile Paylaşımının Kilidini Aç", + version = "Sürüm v${BuildConfig.APP_VERSION}", ) object SimplifiedChinese : Strings( - autoInjectInventory = "Automatically inject items into an in-game Steam inventory", + autoInjectInventory = "自动将物品注入游戏内 Steam 库存", appStatusOriginal = "原生", appStatusUnlocked = "已解锁", appStatusLocked = "未解锁", @@ -417,7 +495,8 @@ sealed class Strings( defaultAppStatus = "默认游戏状态", downloadingRelease = "正在下载 %0: %1 共 %2", downloadPreReleaseVersions = "下载预览版工具", - extraInventoryItems = "Inject extra items into an in-game Steam inventory", + error = "❌ 错误", + extraInventoryItems = "将额外的物品注入游戏内的 Steam 库存", fetchingToolInfo = "正在获取 %0 信息", gameMode = "游戏模式", inDevelopment = "开发中", @@ -433,6 +512,7 @@ sealed class Strings( modifyInstallation = "配置安装", openDataDirectory = "打开数据目录", openLatestReleasePage = "最新发布页面", + openLogFile = "打开日志文件", openOfficialForumTopic = "官方论坛主题", overrideAppStatus = "覆写游戏状态", overrideDlcStatus = "覆写DLC状态", @@ -461,68 +541,4 @@ sealed class Strings( unlockFamilySharing = "解锁家庭共享", version = "版本 v${BuildConfig.APP_VERSION}", ) - - object Turkish : Strings( - autoInjectInventory = "Öğeleri oyun içi bir Steam envanterine otomatik olarak enjekte edin", - appStatusOriginal = "Orijinal", - appStatusUnlocked = "Kilidi Açıldı", - appStatusLocked = "Kilitlendi", - buildTimestamp = "Zaman damgası oluştur", - cacheSize = "Önbellek boyutu: %0", - cancel = "İptal", - checkForUpdates = "Güncelleştirmeleri denetle", - clearCache = "Önbelleği temizle", - computing = "Bilgi İşlemi", - configuration = "Yapılandırma", - confirmForceModifyInstallationMessage = "%0 işlemi şu anda sisteminizde çalışmakta olduğu bulundu. " + - "Yükleme durumunu değiştirmek için bu işlemi kapatmak gerekir. " + - "Zorla kapatarak yüklemeye devam etmek istiyor musunuz?", - confirmForceModifyInstallationTitle = "Kurulum / kaldırma işlemine devam edilsin mi?", - defaultAppStatus = "Varsayılan oyun durumu", - downloadingRelease = "%0 sürümü indiriliyor: %2'den %1", - downloadPreReleaseVersions = "Araçların yayınlanma öncesi sürümlerini indirin", - extraInventoryItems = "Oyun içi bir Steam envanterine fazladan öğeler ekleyin", - fetchingToolInfo = "%0 bilgisi getiriliyor", - gameMode = "Oyun Modu", - inDevelopment = "Geliştirme aşamasında", - information = "Bilgi", - install = "Yükle", - installation = "Yükleme", - installationError = "❌ Yükleme hatası", - installationStatus = "Yükleme durumu", - installationSuccess = "✅ Yükleme başarıyla gerçekleşti", - installed = "%0 Yüklendi", - language = "Dil", - logging = "Giriş yapılıyor", - modifyInstallation = "Kurulumu değiştir", - openDataDirectory = "Veri dizinini aç", - openLatestReleasePage = "En son sürüm sayfasını aç", - openOfficialForumTopic = "Resmi forum konusunu aç", - overrideAppStatus = "Oyun durumunu geçersiz kıl", - overrideDlcStatus = "DLC durumunu geçersiz kıl", - notInstalled = "Yüklü değil", - processStatusRunning = "Çalışıyor", - processStatusNotRunning = "Çalışmıyor", - refreshStatus = "Durumu yenile", - reloadConfig = "Yapılandırmayı yeniden yükle", - reloadConfigTooltip = "Çalışan bir Steam işleminde yapılandırmayı yeniden yükler", - reloadConfigSuccess = "✅ Yapılandırma başarıyla yeniden yüklendi", - reloadConfigError = "❌ Yapılandırma yeniden yüklenirken hata oluştu", - restoreDefaultConfiguration = "Varsayılan yapılandırmayı geri yükle", - settings = "Ayarlar", - startPage = "Başlangıç sayfası", - startPageWelcome = "Koalageddon v%0'a hoş geldiniz! Çeviri: @caner__karaca", - storeMode = "Mağaza modu", - storeProcessStatus = "%0 işlem durumu", - theme = "Tema", - themeDark = "Karanlık", - themeLight = "Aydınlık", - toolConfig = "%0 yapılandırma", - removalError = "❌ Kaldırma hatası", - removalSuccess = "✅ Kaldırma başarılı", - remove = "Kaldır", - unlocker = "Kilit açıcı", - unlockFamilySharing = "Aile Paylaşımının Kilidini Aç", - version = "Sürüm v${BuildConfig.APP_VERSION}", - ) } diff --git a/src/jvmMain/kotlin/acidicoala/koalageddon/home/ui/HomeScreen.kt b/src/jvmMain/kotlin/acidicoala/koalageddon/home/ui/HomeScreen.kt index a6f417d..2a24f39 100644 --- a/src/jvmMain/kotlin/acidicoala/koalageddon/home/ui/HomeScreen.kt +++ b/src/jvmMain/kotlin/acidicoala/koalageddon/home/ui/HomeScreen.kt @@ -52,7 +52,10 @@ fun HomeScreen() { var selectedTab: HomeTab by remember { mutableStateOf(HomeTab.Start) } val homeTabs = remember { - HomeTab.values().filter { it.store?.installed ?: true }.sortedBy(HomeTab::priority).map(VerticalTabElement::Tab) + HomeTab.values() + .filter { it.store?.installed ?: true } + .sortedBy(HomeTab::priority) + .map(VerticalTabElement::Tab) .toMutableList().apply { add(2, VerticalTabElement.Spacer) } diff --git a/src/jvmMain/kotlin/acidicoala/koalageddon/settings/ui/SettingsScreen.kt b/src/jvmMain/kotlin/acidicoala/koalageddon/settings/ui/SettingsScreen.kt index af2f03d..bda4ea5 100644 --- a/src/jvmMain/kotlin/acidicoala/koalageddon/settings/ui/SettingsScreen.kt +++ b/src/jvmMain/kotlin/acidicoala/koalageddon/settings/ui/SettingsScreen.kt @@ -1,16 +1,20 @@ package acidicoala.koalageddon.settings.ui import acidicoala.koalageddon.core.model.LangString +import acidicoala.koalageddon.core.model.Settings +import acidicoala.koalageddon.core.ui.composable.* import acidicoala.koalageddon.core.ui.composition.LocalSettings import acidicoala.koalageddon.core.ui.composition.LocalStrings import acidicoala.koalageddon.core.ui.theme.DefaultContentPadding import acidicoala.koalageddon.core.ui.theme.DefaultMaxWidth -import acidicoala.koalageddon.core.model.Settings -import acidicoala.koalageddon.core.ui.composable.* import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.widthIn import androidx.compose.material.Divider +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.DarkMode +import androidx.compose.material.icons.filled.LightMode +import androidx.compose.material.icons.filled.Translate import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -40,14 +44,19 @@ fun SettingsScreen() { label = strings.theme, items = Settings.Theme.values(), selected = settings.theme, - onSelect = screenModel::onThemeChanged + onSelect = screenModel::onThemeChanged, + icon = when (settings.theme) { + Settings.Theme.Dark -> Icons.Default.DarkMode + Settings.Theme.Light -> Icons.Default.LightMode + } ) DropdownOption( label = strings.language, items = Settings.Language.values(), selected = settings.language, - onSelect = screenModel::onLanguageChanged + onSelect = screenModel::onLanguageChanged, + icon = Icons.Default.Translate ) SwitchOption( diff --git a/src/jvmMain/kotlin/acidicoala/koalageddon/steam/ui/SteamScreenModel.kt b/src/jvmMain/kotlin/acidicoala/koalageddon/steam/ui/SteamScreenModel.kt index 0bb69ec..441336a 100644 --- a/src/jvmMain/kotlin/acidicoala/koalageddon/steam/ui/SteamScreenModel.kt +++ b/src/jvmMain/kotlin/acidicoala/koalageddon/steam/ui/SteamScreenModel.kt @@ -19,6 +19,7 @@ import org.kodein.di.DI import org.kodein.di.DIAware import org.kodein.di.instance import java.net.URI +import kotlin.io.path.exists import kotlin.time.Duration.Companion.milliseconds class SteamScreenModel( @@ -30,6 +31,7 @@ class SteamScreenModel( val installationChecklist: InstallationChecklist? = null, val config: Config? = null, val isSteamRunning: Boolean = false, + val logFileExists: Boolean = false, ) private val paths: AppPaths by instance() @@ -49,6 +51,10 @@ class SteamScreenModel( openResourceLink(URI.create(SmokeAPI.homePage)) } + fun onOpenLogs() { + openResourceLink(paths.getUnlockerLog(SmokeAPI)) + } + fun onRefreshState() { scope.launch { stateFlow.update { @@ -60,6 +66,7 @@ class SteamScreenModel( stateFlow.update { it.copy( + logFileExists = paths.getUnlockerLog(SmokeAPI).exists(), installationChecklist = getInstallationChecklist(store = Store.Steam), config = try { SmokeAPI.parseConfig(paths.getUnlockerConfig(SmokeAPI)) diff --git a/src/jvmMain/kotlin/acidicoala/koalageddon/steam/ui/SteamStoreScreen.kt b/src/jvmMain/kotlin/acidicoala/koalageddon/steam/ui/SteamStoreScreen.kt index 34a15c6..024cfd5 100644 --- a/src/jvmMain/kotlin/acidicoala/koalageddon/steam/ui/SteamStoreScreen.kt +++ b/src/jvmMain/kotlin/acidicoala/koalageddon/steam/ui/SteamStoreScreen.kt @@ -58,6 +58,15 @@ fun SteamStoreScreen() { onClick = screenModel::onUnlockerClick ) + ButtonOption( + label = "", + buttonIcon = Icons.Default.Summarize, + buttonLabel = strings.openLogFile, + enabled = state.logFileExists, + outlined = true, + onClick = screenModel::onOpenLogs + ) + Divider(Modifier.padding(vertical = 8.dp)) SectionLabel(