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"