mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Show toast & add option to fetch release notes when app updates (#1182)
## Description When Wholphin is updated, it will show a short toast message. This works for both app store and side loaded installs. You can now click on the "Installed version" in settings to fetch the release notes for the current version. ### Related issues Closes #1058 ### Testing Emulator ## Screenshots  ## AI or LLM usage None
This commit is contained in:
parent
46beee00bb
commit
feb8c31791
7 changed files with 207 additions and 85 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
package com.github.damontecres.wholphin
|
package com.github.damontecres.wholphin
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
|
@ -51,14 +52,15 @@ import com.github.damontecres.wholphin.ui.LocalImageUrlService
|
||||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||||
import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisodeIds
|
import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisodeIds
|
||||||
import com.github.damontecres.wholphin.ui.launchDefault
|
import com.github.damontecres.wholphin.ui.launchDefault
|
||||||
import com.github.damontecres.wholphin.ui.launchIO
|
|
||||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
|
import com.github.damontecres.wholphin.ui.showToast
|
||||||
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
||||||
import com.github.damontecres.wholphin.ui.util.ProvideLocalClock
|
import com.github.damontecres.wholphin.ui.util.ProvideLocalClock
|
||||||
import com.github.damontecres.wholphin.util.DebugLogTree
|
import com.github.damontecres.wholphin.util.DebugLogTree
|
||||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
|
|
@ -394,6 +396,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
class MainActivityViewModel
|
class MainActivityViewModel
|
||||||
@Inject
|
@Inject
|
||||||
constructor(
|
constructor(
|
||||||
|
@param:ApplicationContext private val context: Context,
|
||||||
private val preferences: DataStore<AppPreferences>,
|
private val preferences: DataStore<AppPreferences>,
|
||||||
val serverRepository: ServerRepository,
|
val serverRepository: ServerRepository,
|
||||||
private val navigationManager: SetupNavigationManager,
|
private val navigationManager: SetupNavigationManager,
|
||||||
|
|
@ -402,9 +405,19 @@ class MainActivityViewModel
|
||||||
private val appUpgradeHandler: AppUpgradeHandler,
|
private val appUpgradeHandler: AppUpgradeHandler,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
fun appStart() {
|
fun appStart() {
|
||||||
viewModelScope.launchIO {
|
viewModelScope.launchDefault {
|
||||||
try {
|
try {
|
||||||
|
val needUpgrade = appUpgradeHandler.needUpgrade()
|
||||||
|
if (needUpgrade) {
|
||||||
|
showToast(
|
||||||
|
context,
|
||||||
|
context.getString(
|
||||||
|
R.string.updated_toast,
|
||||||
|
appUpgradeHandler.currentVersion.toString(),
|
||||||
|
),
|
||||||
|
)
|
||||||
appUpgradeHandler.run()
|
appUpgradeHandler.run()
|
||||||
|
}
|
||||||
appUpgradeHandler.copySubfont(false)
|
appUpgradeHandler.copySubfont(false)
|
||||||
val prefs =
|
val prefs =
|
||||||
preferences.data.firstOrNull() ?: AppPreferences.getDefaultInstance()
|
preferences.data.firstOrNull() ?: AppPreferences.getDefaultInstance()
|
||||||
|
|
@ -447,7 +460,7 @@ class MainActivityViewModel
|
||||||
navigationManager.navigateTo(SetupDestination.ServerList)
|
navigationManager.navigateTo(SetupDestination.ServerList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
viewModelScope.launchIO {
|
viewModelScope.launchDefault {
|
||||||
// Create the mediaCodecCapabilitiesTest if needed
|
// Create the mediaCodecCapabilitiesTest if needed
|
||||||
deviceProfileService.mediaCodecCapabilitiesTest.supportsAVC()
|
deviceProfileService.mediaCodecCapabilitiesTest.supportsAVC()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
package com.github.damontecres.wholphin.services
|
package com.github.damontecres.wholphin.services
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageInfo
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import androidx.datastore.core.DataStore
|
import androidx.datastore.core.DataStore
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.github.damontecres.wholphin.WholphinApplication
|
|
||||||
import com.github.damontecres.wholphin.data.SeerrServerDao
|
import com.github.damontecres.wholphin.data.SeerrServerDao
|
||||||
import com.github.damontecres.wholphin.preferences.AppPreference
|
import com.github.damontecres.wholphin.preferences.AppPreference
|
||||||
import com.github.damontecres.wholphin.preferences.AppPreferences
|
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||||
|
|
@ -43,12 +43,10 @@ class AppUpgradeHandler
|
||||||
private val appPreferences: DataStore<AppPreferences>,
|
private val appPreferences: DataStore<AppPreferences>,
|
||||||
private val seerrServerDao: SeerrServerDao,
|
private val seerrServerDao: SeerrServerDao,
|
||||||
) {
|
) {
|
||||||
suspend fun run() {
|
val pkgInfo: PackageInfo get() = context.packageManager.getPackageInfo(context.packageName, 0)
|
||||||
val pkgInfo =
|
val currentVersion: Version get() = Version.fromString(pkgInfo.versionName!!)
|
||||||
WholphinApplication.instance.packageManager.getPackageInfo(
|
|
||||||
WholphinApplication.instance.packageName,
|
fun needUpgrade(): Boolean {
|
||||||
0,
|
|
||||||
)
|
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
val previousVersion = prefs.getString(VERSION_NAME_CURRENT_KEY, null)
|
val previousVersion = prefs.getString(VERSION_NAME_CURRENT_KEY, null)
|
||||||
val previousVersionCode = prefs.getLong(VERSION_CODE_CURRENT_KEY, -1)
|
val previousVersionCode = prefs.getLong(VERSION_CODE_CURRENT_KEY, -1)
|
||||||
|
|
@ -60,10 +58,25 @@ class AppUpgradeHandler
|
||||||
} else {
|
} else {
|
||||||
pkgInfo.versionCode.toLong()
|
pkgInfo.versionCode.toLong()
|
||||||
}
|
}
|
||||||
if (newVersion != previousVersion || newVersionCode != previousVersionCode) {
|
|
||||||
Timber.i(
|
Timber.i(
|
||||||
"App updated: $previousVersion=>$newVersion, $previousVersionCode=>$newVersionCode",
|
"App versions: $previousVersion=>$newVersion, $previousVersionCode=>$newVersionCode",
|
||||||
)
|
)
|
||||||
|
return newVersion != previousVersion || newVersionCode != previousVersionCode
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun run() {
|
||||||
|
Timber.i("App upgrade started")
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val previousVersion = prefs.getString(VERSION_NAME_CURRENT_KEY, null)
|
||||||
|
val previousVersionCode = prefs.getLong(VERSION_CODE_CURRENT_KEY, -1)
|
||||||
|
|
||||||
|
val newVersion = pkgInfo.versionName!!
|
||||||
|
val newVersionCode =
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
pkgInfo.longVersionCode
|
||||||
|
} else {
|
||||||
|
pkgInfo.versionCode.toLong()
|
||||||
|
}
|
||||||
// Store the previous and new version info
|
// Store the previous and new version info
|
||||||
prefs.edit(true) {
|
prefs.edit(true) {
|
||||||
putString(VERSION_NAME_PREVIOUS_KEY, previousVersion)
|
putString(VERSION_NAME_PREVIOUS_KEY, previousVersion)
|
||||||
|
|
@ -82,9 +95,6 @@ class AppUpgradeHandler
|
||||||
Timber.e(ex, "Exception during app upgrade")
|
Timber.e(ex, "Exception during app upgrade")
|
||||||
}
|
}
|
||||||
Timber.i("App upgrade complete")
|
Timber.i("App upgrade complete")
|
||||||
} else {
|
|
||||||
Timber.d("No app update needed")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -128,18 +128,36 @@ class UpdateChecker
|
||||||
return Version.fromString(pkgInfo.versionName!!)
|
return Version.fromString(pkgInfo.versionName!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getRelease(version: Version): Release? {
|
||||||
|
val url =
|
||||||
|
"https://api.github.com/repos/damontecres/Wholphin/releases/tags/v${version.major}.${version.minor}.${version.patch}"
|
||||||
|
return withContext(Dispatchers.IO) {
|
||||||
|
val request =
|
||||||
|
Request
|
||||||
|
.Builder()
|
||||||
|
.url(url)
|
||||||
|
.get()
|
||||||
|
.build()
|
||||||
|
getRelease(request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the latest released version
|
* Get the latest released version
|
||||||
*/
|
*/
|
||||||
suspend fun getLatestRelease(updateUrl: String): Release? {
|
suspend fun getLatestRelease(updateUrl: String): Release? =
|
||||||
return withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val request =
|
val request =
|
||||||
Request
|
Request
|
||||||
.Builder()
|
.Builder()
|
||||||
.url(updateUrl)
|
.url(updateUrl)
|
||||||
.get()
|
.get()
|
||||||
.build()
|
.build()
|
||||||
okHttpClient.newCall(request).execute().use {
|
getRelease(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getRelease(request: Request): Release? {
|
||||||
|
return okHttpClient.newCall(request).execute().use {
|
||||||
if (it.isSuccessful && it.body != null) {
|
if (it.isSuccessful && it.body != null) {
|
||||||
val result = Json.parseToJsonElement(it.body!!.string())
|
val result = Json.parseToJsonElement(it.body!!.string())
|
||||||
val name = result.jsonObject["name"]?.jsonPrimitive?.contentOrNull
|
val name = result.jsonObject["name"]?.jsonPrimitive?.contentOrNull
|
||||||
|
|
@ -161,19 +179,18 @@ class UpdateChecker
|
||||||
m.groupValues[1]
|
m.groupValues[1]
|
||||||
}.toList()
|
}.toList()
|
||||||
} else {
|
} else {
|
||||||
listOf()
|
emptyList()
|
||||||
}
|
}
|
||||||
return@use Release(version, downloadUrl, publishedAt, body, notes)
|
return@use Release(version, downloadUrl, publishedAt, body, notes)
|
||||||
} else {
|
} else {
|
||||||
Timber.w("Update version parsing failed. name=$name")
|
Timber.w("Update version parsing failed. name=$name")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Timber.w("Update check failed: ${it.message}")
|
Timber.w("Update check failed ${it.code}: ${it.message}")
|
||||||
}
|
}
|
||||||
return@use null
|
return@use null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Download and install an update
|
* Download and install an update
|
||||||
|
|
@ -347,7 +364,19 @@ data class Release(
|
||||||
val publishedAt: String?,
|
val publishedAt: String?,
|
||||||
val body: String?,
|
val body: String?,
|
||||||
val notes: List<String>,
|
val notes: List<String>,
|
||||||
)
|
) {
|
||||||
|
val content =
|
||||||
|
"# ${version}\n" +
|
||||||
|
(
|
||||||
|
(notes.joinToString("\n").takeIf { it.isNotNullOrBlank() } ?: "") +
|
||||||
|
(body ?: "")
|
||||||
|
).replace(
|
||||||
|
Regex("https://github.com/\\w*/\\w+/pull/(\\d+)"),
|
||||||
|
"#$1",
|
||||||
|
)
|
||||||
|
// Remove the last line for full changelog since its just a link
|
||||||
|
.replace(Regex("\\*\\*Full Changelog\\*\\*.*"), "")
|
||||||
|
}
|
||||||
|
|
||||||
interface DownloadCallback {
|
interface DownloadCallback {
|
||||||
fun contentLength(contentLength: Long)
|
fun contentLength(contentLength: Long)
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
|
@ -55,20 +54,26 @@ import com.github.damontecres.wholphin.preferences.advancedPreferences
|
||||||
import com.github.damontecres.wholphin.preferences.basicPreferences
|
import com.github.damontecres.wholphin.preferences.basicPreferences
|
||||||
import com.github.damontecres.wholphin.preferences.screensaverPreferences
|
import com.github.damontecres.wholphin.preferences.screensaverPreferences
|
||||||
import com.github.damontecres.wholphin.preferences.updatePlaybackPreferences
|
import com.github.damontecres.wholphin.preferences.updatePlaybackPreferences
|
||||||
|
import com.github.damontecres.wholphin.services.Release
|
||||||
import com.github.damontecres.wholphin.services.SeerrConnectionStatus
|
import com.github.damontecres.wholphin.services.SeerrConnectionStatus
|
||||||
import com.github.damontecres.wholphin.services.UpdateChecker
|
import com.github.damontecres.wholphin.services.UpdateChecker
|
||||||
import com.github.damontecres.wholphin.ui.components.ConfirmDialog
|
import com.github.damontecres.wholphin.ui.components.ConfirmDialog
|
||||||
|
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
|
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||||
|
import com.github.damontecres.wholphin.ui.components.ScrollableDialog
|
||||||
import com.github.damontecres.wholphin.ui.ifElse
|
import com.github.damontecres.wholphin.ui.ifElse
|
||||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
import com.github.damontecres.wholphin.ui.playOnClickSound
|
import com.github.damontecres.wholphin.ui.playOnClickSound
|
||||||
import com.github.damontecres.wholphin.ui.playSoundOnFocus
|
import com.github.damontecres.wholphin.ui.playSoundOnFocus
|
||||||
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings
|
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings
|
||||||
|
import com.github.damontecres.wholphin.ui.setup.ReleaseNotes
|
||||||
import com.github.damontecres.wholphin.ui.setup.UpdateViewModel
|
import com.github.damontecres.wholphin.ui.setup.UpdateViewModel
|
||||||
import com.github.damontecres.wholphin.ui.setup.seerr.AddSeerServerDialog
|
import com.github.damontecres.wholphin.ui.setup.seerr.AddSeerServerDialog
|
||||||
import com.github.damontecres.wholphin.ui.setup.seerr.SwitchSeerrViewModel
|
import com.github.damontecres.wholphin.ui.setup.seerr.SwitchSeerrViewModel
|
||||||
import com.github.damontecres.wholphin.ui.showToast
|
import com.github.damontecres.wholphin.ui.showToast
|
||||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||||
|
import com.github.damontecres.wholphin.util.DataLoadingState
|
||||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
import com.github.damontecres.wholphin.util.LoadingState
|
import com.github.damontecres.wholphin.util.LoadingState
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -93,6 +98,7 @@ fun PreferencesContent(
|
||||||
val currentUser by viewModel.currentUser.observeAsState()
|
val currentUser by viewModel.currentUser.observeAsState()
|
||||||
val currentServer by seerrVm.currentSeerrServer.collectAsState(null)
|
val currentServer by seerrVm.currentSeerrServer.collectAsState(null)
|
||||||
var showPinFlow by remember { mutableStateOf(false) }
|
var showPinFlow by remember { mutableStateOf(false) }
|
||||||
|
var showVersionDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
var cacheUsage by remember { mutableStateOf(CacheUsage(0, 0, 0)) }
|
var cacheUsage by remember { mutableStateOf(CacheUsage(0, 0, 0)) }
|
||||||
val seerrConnection by viewModel.seerrConnection.collectAsState()
|
val seerrConnection by viewModel.seerrConnection.collectAsState()
|
||||||
|
|
@ -252,15 +258,13 @@ fun PreferencesContent(
|
||||||
}
|
}
|
||||||
when (pref) {
|
when (pref) {
|
||||||
AppPreference.InstalledVersion -> {
|
AppPreference.InstalledVersion -> {
|
||||||
var clickCount by remember { mutableIntStateOf(0) }
|
|
||||||
ClickPreference(
|
ClickPreference(
|
||||||
title = stringResource(R.string.installed_version),
|
title = stringResource(R.string.installed_version),
|
||||||
onClick = {
|
onClick = {
|
||||||
if (movementSounds) playOnClickSound(context)
|
showVersionDialog = true
|
||||||
if (clickCount++ >= 2) {
|
},
|
||||||
clickCount = 0
|
onLongClick = {
|
||||||
viewModel.navigationManager.navigateTo(Destination.Debug)
|
viewModel.navigationManager.navigateTo(Destination.Debug)
|
||||||
}
|
|
||||||
},
|
},
|
||||||
summary = installedVersion.toString(),
|
summary = installedVersion.toString(),
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
|
|
@ -615,6 +619,33 @@ fun PreferencesContent(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (showVersionDialog) {
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
viewModel.fetchReleaseNotes()
|
||||||
|
}
|
||||||
|
val release by viewModel.releaseNotes.collectAsState()
|
||||||
|
ScrollableDialog(
|
||||||
|
onDismissRequest = { showVersionDialog = false },
|
||||||
|
) {
|
||||||
|
item {
|
||||||
|
when (val r = release) {
|
||||||
|
is DataLoadingState.Error -> {
|
||||||
|
ErrorMessage(message = "Error", exception = r.exception)
|
||||||
|
}
|
||||||
|
|
||||||
|
DataLoadingState.Pending,
|
||||||
|
DataLoadingState.Loading,
|
||||||
|
-> {
|
||||||
|
LoadingPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
is DataLoadingState.Success<Release> -> {
|
||||||
|
ReleaseNotes(r.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,13 @@ import com.github.damontecres.wholphin.preferences.resetSubtitles
|
||||||
import com.github.damontecres.wholphin.preferences.updateSubtitlePreferences
|
import com.github.damontecres.wholphin.preferences.updateSubtitlePreferences
|
||||||
import com.github.damontecres.wholphin.services.BackdropService
|
import com.github.damontecres.wholphin.services.BackdropService
|
||||||
import com.github.damontecres.wholphin.services.NavigationManager
|
import com.github.damontecres.wholphin.services.NavigationManager
|
||||||
|
import com.github.damontecres.wholphin.services.Release
|
||||||
import com.github.damontecres.wholphin.services.ScreensaverService
|
import com.github.damontecres.wholphin.services.ScreensaverService
|
||||||
import com.github.damontecres.wholphin.services.SeerrServerRepository
|
import com.github.damontecres.wholphin.services.SeerrServerRepository
|
||||||
|
import com.github.damontecres.wholphin.services.UpdateChecker
|
||||||
import com.github.damontecres.wholphin.ui.detail.DebugViewModel.Companion.sendAppLogs
|
import com.github.damontecres.wholphin.ui.detail.DebugViewModel.Companion.sendAppLogs
|
||||||
import com.github.damontecres.wholphin.ui.launchIO
|
import com.github.damontecres.wholphin.ui.launchIO
|
||||||
|
import com.github.damontecres.wholphin.util.DataLoadingState
|
||||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
import com.github.damontecres.wholphin.util.LoadingState
|
import com.github.damontecres.wholphin.util.LoadingState
|
||||||
import com.github.damontecres.wholphin.util.RememberTabManager
|
import com.github.damontecres.wholphin.util.RememberTabManager
|
||||||
|
|
@ -22,9 +25,11 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
import org.jellyfin.sdk.api.client.ApiClient
|
import org.jellyfin.sdk.api.client.ApiClient
|
||||||
import org.jellyfin.sdk.model.ClientInfo
|
import org.jellyfin.sdk.model.ClientInfo
|
||||||
import org.jellyfin.sdk.model.DeviceInfo
|
import org.jellyfin.sdk.model.DeviceInfo
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
|
|
@ -42,6 +47,7 @@ class PreferencesViewModel
|
||||||
private val seerrServerRepository: SeerrServerRepository,
|
private val seerrServerRepository: SeerrServerRepository,
|
||||||
private val deviceInfo: DeviceInfo,
|
private val deviceInfo: DeviceInfo,
|
||||||
private val clientInfo: ClientInfo,
|
private val clientInfo: ClientInfo,
|
||||||
|
private val updateChecker: UpdateChecker,
|
||||||
) : ViewModel(),
|
) : ViewModel(),
|
||||||
RememberTabManager by rememberTabManager {
|
RememberTabManager by rememberTabManager {
|
||||||
val currentUser get() = serverRepository.currentUser
|
val currentUser get() = serverRepository.currentUser
|
||||||
|
|
@ -51,6 +57,8 @@ class PreferencesViewModel
|
||||||
private val _quickConnectStatus = MutableStateFlow<LoadingState>(LoadingState.Pending)
|
private val _quickConnectStatus = MutableStateFlow<LoadingState>(LoadingState.Pending)
|
||||||
val quickConnectStatus: StateFlow<LoadingState> = _quickConnectStatus
|
val quickConnectStatus: StateFlow<LoadingState> = _quickConnectStatus
|
||||||
|
|
||||||
|
val releaseNotes = MutableStateFlow<DataLoadingState<Release>>(DataLoadingState.Pending)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launchIO {
|
viewModelScope.launchIO {
|
||||||
serverRepository.currentUser.value?.let { user ->
|
serverRepository.currentUser.value?.let { user ->
|
||||||
|
|
@ -99,6 +107,23 @@ class PreferencesViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun fetchReleaseNotes() {
|
||||||
|
viewModelScope.launchIO {
|
||||||
|
releaseNotes.update { DataLoadingState.Loading }
|
||||||
|
try {
|
||||||
|
val release = updateChecker.getRelease(updateChecker.getInstalledVersion())
|
||||||
|
if (release != null) {
|
||||||
|
releaseNotes.update { DataLoadingState.Success(release) }
|
||||||
|
} else {
|
||||||
|
releaseNotes.update { DataLoadingState.Error("Release not found") }
|
||||||
|
}
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Timber.e(ex, "Error fetching release")
|
||||||
|
releaseNotes.update { DataLoadingState.Error(ex) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
suspend fun resetSubtitleSettings(appPreferences: DataStore<AppPreferences>) {
|
suspend fun resetSubtitleSettings(appPreferences: DataStore<AppPreferences>) {
|
||||||
appPreferences.updateData {
|
appPreferences.updateData {
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,6 @@ import com.github.damontecres.wholphin.services.Release
|
||||||
import com.github.damontecres.wholphin.services.UpdateChecker
|
import com.github.damontecres.wholphin.services.UpdateChecker
|
||||||
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
||||||
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
||||||
import com.github.damontecres.wholphin.ui.components.Button
|
|
||||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||||
import com.github.damontecres.wholphin.ui.components.TextButton
|
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||||
|
|
@ -66,6 +65,7 @@ import com.github.damontecres.wholphin.util.LoadingExceptionHandler
|
||||||
import com.github.damontecres.wholphin.util.LoadingState
|
import com.github.damontecres.wholphin.util.LoadingState
|
||||||
import com.github.damontecres.wholphin.util.Version
|
import com.github.damontecres.wholphin.util.Version
|
||||||
import com.mikepenz.markdown.m3.Markdown
|
import com.mikepenz.markdown.m3.Markdown
|
||||||
|
import com.mikepenz.markdown.m3.markdownTypography
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
|
@ -272,15 +272,7 @@ fun InstallUpdatePageContent(
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
Markdown(
|
ReleaseNotes(release)
|
||||||
(release.notes.joinToString("\n\n") + (release.body ?: ""))
|
|
||||||
.replace(
|
|
||||||
Regex("https://github.com/damontecres/\\w+/pull/(\\d+)"),
|
|
||||||
"#$1",
|
|
||||||
)
|
|
||||||
// Remove the last line for full changelog since its just a link
|
|
||||||
.replace(Regex("\\*\\*Full Changelog\\*\\*.*"), ""),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Column(
|
Column(
|
||||||
|
|
@ -317,6 +309,25 @@ fun InstallUpdatePageContent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ReleaseNotes(
|
||||||
|
release: Release,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Markdown(
|
||||||
|
content = release.content,
|
||||||
|
typography =
|
||||||
|
markdownTypography(
|
||||||
|
h1 = MaterialTheme.typography.headlineLarge,
|
||||||
|
h2 = MaterialTheme.typography.headlineMedium,
|
||||||
|
h3 = MaterialTheme.typography.headlineSmall,
|
||||||
|
text = MaterialTheme.typography.bodySmall,
|
||||||
|
code = MaterialTheme.typography.bodySmall,
|
||||||
|
),
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DownloadDialog(
|
fun DownloadDialog(
|
||||||
contentLength: Long,
|
contentLength: Long,
|
||||||
|
|
@ -392,6 +403,7 @@ private fun InstallUpdatePageContentPreview() {
|
||||||
downloadUrl = "https://url",
|
downloadUrl = "https://url",
|
||||||
publishedAt = null,
|
publishedAt = null,
|
||||||
body =
|
body =
|
||||||
|
"## Header 2\n" +
|
||||||
"Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus " +
|
"Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus " +
|
||||||
"ex sapien vitae pellentesque sem placerat. In id cursus mi pretium " +
|
"ex sapien vitae pellentesque sem placerat. In id cursus mi pretium " +
|
||||||
"tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. " +
|
"tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. " +
|
||||||
|
|
@ -399,6 +411,7 @@ private fun InstallUpdatePageContentPreview() {
|
||||||
"Iaculis massa nisl malesuada lacinia integer nunc posuere. " +
|
"Iaculis massa nisl malesuada lacinia integer nunc posuere. " +
|
||||||
"Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora " +
|
"Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora " +
|
||||||
"torquent per conubia nostra inceptos himenaeos.\n\n" +
|
"torquent per conubia nostra inceptos himenaeos.\n\n" +
|
||||||
|
"### Header 3\n" +
|
||||||
"Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus " +
|
"Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus " +
|
||||||
"ex sapien vitae pellentesque sem placerat. In id cursus mi pretium " +
|
"ex sapien vitae pellentesque sem placerat. In id cursus mi pretium " +
|
||||||
"tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. " +
|
"tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. " +
|
||||||
|
|
|
||||||
|
|
@ -748,5 +748,6 @@
|
||||||
<string name="select_all">Select all</string>
|
<string name="select_all">Select all</string>
|
||||||
<string name="separate_types">Separate types</string>
|
<string name="separate_types">Separate types</string>
|
||||||
<string name="prefer_logos">Prefer showing logos for titles</string>
|
<string name="prefer_logos">Prefer showing logos for titles</string>
|
||||||
|
<string name="updated_toast">Wholphin updated to %s</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue