From ed2481a427bfc4c5512a92aab1a5e1a061506067 Mon Sep 17 00:00:00 2001 From: "Elie G." Date: Sun, 14 Jun 2026 16:42:49 +0300 Subject: [PATCH] feat(settings): add manual update re-check action in settings banner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a "בדוק שוב" link action on the UpToDate and Error update banners so the user can trigger a fresh update check without restarting the app. Introduces AppUpdateService.recheck() which resets state to Idle and re-runs checkOnStartup(), no-op while a check/download/install is active. --- SeforimApp/build.gradle.kts | 2 +- .../composeResources/values/strings.xml | 1 + .../settings/ui/GeneralSettingsScreen.kt | 32 ++++++++++++++++--- .../framework/update/AppUpdateService.kt | 18 +++++++++++ gradle/libs.versions.toml | 2 +- 5 files changed, 49 insertions(+), 6 deletions(-) diff --git a/SeforimApp/build.gradle.kts b/SeforimApp/build.gradle.kts index 0f951e81..fb19cb95 100644 --- a/SeforimApp/build.gradle.kts +++ b/SeforimApp/build.gradle.kts @@ -307,7 +307,7 @@ nucleus.application { // Register the custom URL scheme so shareable deep links (zayit://book/..., // zayit://search/...) are routed to the app by the OS on macOS, Windows and Linux. - protocol("Zayit", "zayit") + protocol("זית", "zayit") linux { iconFile.set(project.file("desktopAppIcons/LinuxIcon.png")) diff --git a/SeforimApp/src/commonMain/composeResources/values/strings.xml b/SeforimApp/src/commonMain/composeResources/values/strings.xml index 6c219677..ffc05e54 100644 --- a/SeforimApp/src/commonMain/composeResources/values/strings.xml +++ b/SeforimApp/src/commonMain/composeResources/values/strings.xml @@ -520,6 +520,7 @@ התקן והפעל מחדש מאוחר יותר האפליקציה מעודכנת לגרסה האחרונה + בדוק שוב בודק עדכונים… לא ניתן לבדוק עדכונים — ודא שקיים חיבור לאינטרנט diff --git a/SeforimApp/src/jvmMain/kotlin/io/github/kdroidfilter/seforimapp/features/settings/ui/GeneralSettingsScreen.kt b/SeforimApp/src/jvmMain/kotlin/io/github/kdroidfilter/seforimapp/features/settings/ui/GeneralSettingsScreen.kt index 6d86e8e5..41fc3ecf 100644 --- a/SeforimApp/src/jvmMain/kotlin/io/github/kdroidfilter/seforimapp/features/settings/ui/GeneralSettingsScreen.kt +++ b/SeforimApp/src/jvmMain/kotlin/io/github/kdroidfilter/seforimapp/features/settings/ui/GeneralSettingsScreen.kt @@ -17,6 +17,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -37,6 +38,7 @@ import io.github.kdroidfilter.seforimapp.framework.di.LocalAppGraph import io.github.kdroidfilter.seforimapp.framework.update.UpdateMode import io.github.kdroidfilter.seforimapp.framework.update.UpdateUiState import io.github.kdroidfilter.seforimapp.theme.PreviewContainer +import kotlinx.coroutines.launch import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource import org.jetbrains.jewel.foundation.theme.JewelTheme @@ -62,6 +64,7 @@ import seforimapp.seforimapp.generated.resources.settings_keep_screen_awake_desc import seforimapp.seforimapp.generated.resources.settings_persist_session import seforimapp.seforimapp.generated.resources.settings_persist_session_description import seforimapp.seforimapp.generated.resources.update_available_banner +import seforimapp.seforimapp.generated.resources.update_check_action import seforimapp.seforimapp.generated.resources.update_check_failed import seforimapp.seforimapp.generated.resources.update_checking import seforimapp.seforimapp.generated.resources.update_download_action @@ -77,6 +80,7 @@ fun GeneralSettingsScreen() { val version = UpdaterConfig().currentVersion val updateService = LocalAppGraph.current.appUpdateService val updateState by updateService.state.collectAsState() + val scope = rememberCoroutineScope() GeneralSettingsView( state = state, version = version, @@ -84,6 +88,7 @@ fun GeneralSettingsScreen() { onEvent = viewModel::onEvent, onDownloadUpdate = updateService::startDownload, onInstallUpdate = updateService::installAndRestart, + onCheckForUpdate = { scope.launch { updateService.recheck() } }, ) } @@ -95,6 +100,7 @@ private fun GeneralSettingsView( onEvent: (GeneralSettingsEvents) -> Unit, onDownloadUpdate: () -> Unit = {}, onInstallUpdate: () -> Unit = {}, + onCheckForUpdate: () -> Unit = {}, ) { VerticallyScrollableContainer(modifier = Modifier.fillMaxSize()) { Column( @@ -109,6 +115,7 @@ private fun GeneralSettingsView( updateState = updateState, onDownloadUpdate = onDownloadUpdate, onInstallUpdate = onInstallUpdate, + onCheckForUpdate = onCheckForUpdate, ) SettingCard( @@ -141,6 +148,7 @@ private fun AppHeader( updateState: UpdateUiState, onDownloadUpdate: () -> Unit, onInstallUpdate: () -> Unit, + onCheckForUpdate: () -> Unit, ) { val shape = RoundedCornerShape(8.dp) @@ -159,6 +167,7 @@ private fun AppHeader( updateState = updateState, onDownloadUpdate = onDownloadUpdate, onInstallUpdate = onInstallUpdate, + onCheckForUpdate = onCheckForUpdate, ) Row( @@ -235,6 +244,7 @@ private fun UpdateStatusBanner( updateState: UpdateUiState, onDownloadUpdate: () -> Unit, onInstallUpdate: () -> Unit, + onCheckForUpdate: () -> Unit, ) { when (updateState) { is UpdateUiState.Available -> { @@ -269,11 +279,25 @@ private fun UpdateStatusBanner( ) } - UpdateUiState.UpToDate -> - InlineSuccessBanner(text = stringResource(Res.string.update_up_to_date)) + UpdateUiState.UpToDate -> { + val checkLabel = stringResource(Res.string.update_check_action) + InlineSuccessBanner( + text = stringResource(Res.string.update_up_to_date), + linkActions = { + action(checkLabel, onClick = onCheckForUpdate) + }, + ) + } - is UpdateUiState.Error -> - InlineWarningBanner(text = stringResource(Res.string.update_check_failed)) + is UpdateUiState.Error -> { + val checkLabel = stringResource(Res.string.update_check_action) + InlineWarningBanner( + text = stringResource(Res.string.update_check_failed), + linkActions = { + action(checkLabel, onClick = onCheckForUpdate) + }, + ) + } UpdateUiState.Idle, UpdateUiState.Checking -> InlineInformationBanner(text = stringResource(Res.string.update_checking)) diff --git a/SeforimApp/src/jvmMain/kotlin/io/github/kdroidfilter/seforimapp/framework/update/AppUpdateService.kt b/SeforimApp/src/jvmMain/kotlin/io/github/kdroidfilter/seforimapp/framework/update/AppUpdateService.kt index e0d27055..0fbf0362 100644 --- a/SeforimApp/src/jvmMain/kotlin/io/github/kdroidfilter/seforimapp/framework/update/AppUpdateService.kt +++ b/SeforimApp/src/jvmMain/kotlin/io/github/kdroidfilter/seforimapp/framework/update/AppUpdateService.kt @@ -244,6 +244,24 @@ class AppUpdateService( } } + /** + * Re-runs the update check on user request (from the settings screen). Resets to [UpdateUiState.Idle] + * first so [checkOnStartup] runs again; no-op while a check/download/install is already in progress. + */ + suspend fun recheck() { + if (config.fakeState != null) return + when (_state.value) { + is UpdateUiState.Checking, + is UpdateUiState.Downloading, + is UpdateUiState.ReadyToInstall, + -> return + else -> { + _state.value = UpdateUiState.Idle + checkOnStartup() + } + } + } + /** * Triggers the download for a prompt update (MINOR/MAJOR) when the user confirms in the dialog. * Runs on the service scope (not the dialog's), so the download continues in the background even diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1c9df8ae..4f2e9e55 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ filekitCore = "0.14.1" hebrewNumerals = "0.2.6" jsoup = "1.22.2" jvmToolchain = "25" -nucleus = "2.0.0-alpha-202606140317" +nucleus = "2.0.0-alpha-202606140318" koalaplotCore = "0.11.2" kotlin = "2.3.21" compose = "1.10.3"