Add update checker

This commit is contained in:
Syer10
2021-12-04 22:05:10 -05:00
parent aee287ec31
commit 6891708df7
9 changed files with 148 additions and 1 deletions

View File

@@ -86,6 +86,7 @@ jobs:
-Pcompose.desktop.mac.notarization.appleID=${{ secrets.APPLE_ID }}
-Pcompose.desktop.mac.notarization.password=${{ secrets.APPLE_PASSWORD }}
-Pidentity="${{ secrets.APPLE_IDENTITY }}"
-Ppreview="${{ env.COMMIT_COUNT }}"
--stacktrace
# Upload runner package tar.gz/zip as artifact

View File

@@ -230,9 +230,11 @@ buildConfig {
buildConfigField("String", "NAME", project.name.wrap())
buildConfigField("String", "VERSION", project.version.toString().wrap())
buildConfigField("int", "MIGRATION_CODE", migrationCode.toString())
buildConfigField("boolean", "DEBUG", project.hasProperty("debugApp").toString())
buildConfigField("boolean", "IS_PREVIEW", project.hasProperty("preview").toString())
buildConfigField("int", "PREVIEW_BUILD", project.properties["preview"]?.toString() ?: 0.toString())
// Tachidesk
buildConfigField("boolean", "DEBUG", project.hasProperty("debugApp").toString())
buildConfigField("String", "TACHIDESK_SP_VERSION", tachideskVersion.wrap())
buildConfigField("int", "SERVER_CODE", serverCode.toString())
}

View File

@@ -32,6 +32,8 @@ import ca.gosyer.data.server.interactions.SourceInteractionHandler
import ca.gosyer.data.translation.ResourceProvider
import ca.gosyer.data.translation.XmlResourceBundle
import ca.gosyer.data.ui.UiPreferences
import ca.gosyer.data.update.UpdateChecker
import ca.gosyer.data.update.UpdatePreferences
import io.kamel.core.config.KamelConfig
import toothpick.ktp.binding.bind
import toothpick.ktp.binding.module
@@ -71,6 +73,10 @@ val DataModule = module {
.toProviderInstance { MigrationPreferences(preferenceFactory.create("migration")) }
.providesSingleton()
bind<UpdatePreferences>()
.toProviderInstance { UpdatePreferences(preferenceFactory.create("update")) }
.providesSingleton()
bind<Http>()
.toProvider(HttpProvider::class)
.providesSingleton()
@@ -114,4 +120,7 @@ val DataModule = module {
bind<Migrations>()
.toClass<Migrations>()
.singleton()
bind<UpdateChecker>()
.toClass<UpdateChecker>()
.singleton()
}

View File

@@ -0,0 +1,61 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.data.update
import ca.gosyer.build.BuildConfig
import ca.gosyer.data.server.Http
import ca.gosyer.data.update.model.GithubRelease
import ca.gosyer.util.lang.launch
import ca.gosyer.util.lang.withIOContext
import io.ktor.client.request.get
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.flow.MutableSharedFlow
import javax.inject.Inject
class UpdateChecker @Inject constructor(
private val updatePreferences: UpdatePreferences,
private val client: Http
) {
val updateFound = MutableSharedFlow<GithubRelease>()
@OptIn(DelicateCoroutinesApi::class)
fun checkForUpdates() {
if (!updatePreferences.enabled().get()) return
launch {
val latestRelease = withIOContext {
client.get<GithubRelease>("https://api.github.com/repos/$GITHUB_REPO/releases/latest")
}
if (isNewVersion(latestRelease.version)) {
updateFound.emit(latestRelease)
}
}
}
// Thanks to Tachiyomi for inspiration
private fun isNewVersion(versionTag: String): Boolean {
// Removes prefixes like "r" or "v"
val newVersion = versionTag.replace("[^\\d.]".toRegex(), "")
return if (BuildConfig.IS_PREVIEW) {
// Preview builds: based on releases in "Suwayomi/Tachidesk-JUI-preview" repo
// tagged as something like "r123"
newVersion.toInt() > BuildConfig.PREVIEW_BUILD
} else {
// Release builds: based on releases in "Suwayomi/Tachidesk-JUI" repo
// tagged as something like "v1.1.2"
newVersion != BuildConfig.VERSION
}
}
companion object {
private val GITHUB_REPO = if (BuildConfig.IS_PREVIEW) {
"Suwayomi/Tachidesk-JUI-preview"
} else {
"Suwayomi/Tachidesk-JUI"
}
}
}

View File

@@ -0,0 +1,16 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.data.update
import ca.gosyer.common.prefs.Preference
import ca.gosyer.common.prefs.PreferenceStore
class UpdatePreferences(private val preferenceStore: PreferenceStore) {
fun enabled(): Preference<Boolean> {
return preferenceStore.getBoolean("enabled", true)
}
}

View File

@@ -0,0 +1,17 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.data.update.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class GithubRelease(
@SerialName("tag_name") val version: String,
@SerialName("body") val info: String,
@SerialName("html_url") val releaseLink: String
)

View File

@@ -12,6 +12,7 @@ import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
@@ -21,6 +22,7 @@ import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.type
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.window.Notification
import androidx.compose.ui.window.Tray
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.awaitApplication
@@ -35,6 +37,7 @@ import ca.gosyer.data.server.ServerService.ServerResult
import ca.gosyer.data.translation.XmlResourceBundle
import ca.gosyer.data.ui.UiPreferences
import ca.gosyer.data.ui.model.ThemeMode
import ca.gosyer.data.update.UpdateChecker
import ca.gosyer.ui.base.WindowDialog
import ca.gosyer.ui.base.components.LoadingScreen
import ca.gosyer.ui.base.prefs.asStateIn
@@ -55,7 +58,9 @@ import io.kamel.image.config.LocalKamelConfig
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.launch
import org.jetbrains.skiko.SystemTheme
import org.jetbrains.skiko.currentSystemTheme
import toothpick.configuration.Configuration
@@ -89,6 +94,7 @@ suspend fun main() {
val serverService = scope.getInstance<ServerService>()
val uiPreferences = scope.getInstance<UiPreferences>()
val updateChecker = scope.getInstance<UpdateChecker>()
// Call setDefault before getting a resource bundle
val language = uiPreferences.language().get()
@@ -165,6 +171,21 @@ suspend fun main() {
}
)
LaunchedEffect(Unit) {
launch {
updateChecker.checkForUpdates()
updateChecker.updateFound.collect {
trayState.sendNotification(
Notification(
resources.getStringA("new_update_title"),
resources.getString("new_update_message", it.version),
Notification.Type.Info
)
)
}
}
}
Window(
onCloseRequest = {
if (confirmExit.value) {

View File

@@ -9,15 +9,30 @@ package ca.gosyer.ui.settings
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import ca.gosyer.data.update.UpdatePreferences
import ca.gosyer.ui.base.components.MenuController
import ca.gosyer.ui.base.components.Toolbar
import ca.gosyer.ui.base.prefs.SwitchPreference
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.base.vm.ViewModel
import ca.gosyer.ui.base.vm.viewModel
import javax.inject.Inject
class SettingsAdvancedViewModel @Inject constructor(
updatePreferences: UpdatePreferences,
) : ViewModel() {
val updatesEnabled = updatePreferences.enabled().asStateFlow()
}
@Composable
fun SettingsAdvancedScreen(menuController: MenuController) {
val vm = viewModel<SettingsAdvancedViewModel>()
Column {
Toolbar(stringResource("settings_advanced_screen"), menuController, true)
LazyColumn {
item {
SwitchPreference(preference = vm.updatesEnabled, title = stringResource("update_checker"))
}
}
}
}

View File

@@ -12,6 +12,8 @@
<string name="unable_to_start_server">Cannot start server</string>
<string name="confirm_exit">Confirm exit</string>
<string name="confirm_exit_message">Are you sure you want to exit?</string>
<string name="new_update_title">Tachidesk-JUI update available</string>
<string name="new_update_message">%1$s is now available!</string>
<!-- Actions -->
<string name="action_yes">Yes</string>
@@ -219,4 +221,7 @@
<string name="digest_auth">Digest auth</string>
<string name="auth_username">Auth username</string>
<string name="auth_password">Auth password</string>
<!-- Advanced Settings -->
<string name="update_checker">Check for updates</string>
</resources>