feat: improve home screen data fetching

This commit is contained in:
rushiiMachine 2025-05-25 02:57:38 -07:00
parent 46fed98175
commit 5edc39f289
No known key found for this signature in database
GPG key ID: DCBE5952BB3B6420
15 changed files with 98 additions and 93 deletions

View file

@ -5,6 +5,7 @@ import com.aliucord.manager.BuildConfig
import io.ktor.client.HttpClient
import io.ktor.client.call.HttpClientCall
import io.ktor.client.engine.okhttp.OkHttp
import io.ktor.client.plugins.HttpTimeout
import io.ktor.client.plugins.cache.HttpCache
import io.ktor.client.plugins.cache.storage.FileStorage
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
@ -62,6 +63,11 @@ fun Scope.provideHttpClient() = HttpClient(OkHttp) {
json(json)
}
install(HttpTimeout) {
connectTimeoutMillis = 20000
requestTimeoutMillis = 10000
}
install(HttpCache) {
val dir = application.cacheDir.resolve("ktor")
publicStorage(FileStorage(dir))

View file

@ -6,16 +6,22 @@
package com.aliucord.manager.network.models
import com.aliucord.manager.network.utils.SemVer
import com.aliucord.manager.util.serialization.IntAsStringSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class BuildInfo(
@Serializable(with = IntAsStringSerializer::class)
@SerialName("versionCode")
val discordVersionCode: String,
@SerialName("versionName")
val discordVersionName: String,
val discordVersionCode: Int,
// @SerialName("versionName")
// val discordVersionName: Int,
@SerialName("injectorVersion")
val injectorVersion: SemVer,
@SerialName("patchesVersion")
val patchesVersion: SemVer,
)

View file

@ -1,7 +1,7 @@
package com.aliucord.manager.network.models
import androidx.compose.runtime.Immutable
import com.aliucord.manager.util.ImmutableListSerializer
import com.aliucord.manager.util.serialization.ImmutableListSerializer
import kotlinx.collections.immutable.ImmutableList
import kotlinx.serialization.Serializable

View file

@ -43,8 +43,7 @@ class DowngradeCheckStep(private val options: PatchOptions) : Step(), KoinCompon
val targetVersion = container
.getStep<FetchInfoStep>()
.data.discordVersionCode.toIntOrNull()
?: throw IllegalArgumentException("Invalid remote target Discord version")
.data.discordVersionCode
container.log("Target discord version: $targetVersion")

View file

@ -8,7 +8,7 @@ import com.aliucord.manager.ui.components.ManagerTheme
import com.aliucord.manager.ui.screens.about.AboutScreenContent
import com.aliucord.manager.ui.screens.about.AboutScreenState
import com.aliucord.manager.ui.util.emptyImmutableList
import com.aliucord.manager.util.ImmutableListSerializer
import com.aliucord.manager.util.serialization.ImmutableListSerializer
import kotlinx.collections.immutable.*
import kotlinx.serialization.json.Json

View file

@ -21,15 +21,14 @@ import kotlinx.collections.immutable.persistentListOf
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
private fun HomeScreenLoadedPreview(
@PreviewParameter(HomeScreenParametersProvider::class)
parameters: HomeScreenParameters,
state: InstallsState.Fetched,
) {
ManagerTheme {
Scaffold(
topBar = { HomeAppBar() },
) { padding ->
HomeScreenLoadedContent(
state = parameters.state,
supportedVersion = parameters.supportedVersion,
state = state,
padding = padding,
onClickInstall = {},
onUpdate = {},
@ -41,12 +40,7 @@ private fun HomeScreenLoadedPreview(
}
}
private data class HomeScreenParameters(
val state: InstallsState.Fetched,
val supportedVersion: DiscordVersion,
)
private class HomeScreenParametersProvider : PreviewParameterProvider<HomeScreenParameters> {
private class HomeScreenParametersProvider : PreviewParameterProvider<InstallsState.Fetched> {
private val stableVersion = DiscordVersion.Existing(DiscordVersion.Type.STABLE, "126.21", 126021)
private val aliucordIconBytes =
"/9j/4AAQSkZJRgABAQAAAQABAAD/4gHYSUNDX1BST0ZJTEUAAQEAAAHIAAAAAAQwAABtbnRyUkdCIFhZWiAH4AABAAEAAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlkZXNjAAAA8AAAACRyWFlaAAABFAAAABRnWFlaAAABKAAAABRiWFlaAAABPAAAABR3dHB0AAABUAAAABRyVFJDAAABZAAAAChnVFJDAAABZAAAAChiVFJDAAABZAAAAChjcHJ0AAABjAAAADxtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEJYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAAAAAAACSgAAAPhAAAts9YWVogAAAAAAAA9tYAAQAAAADTLXBhcmEAAAAAAAQAAAACZmYAAPKnAAANWQAAE9AAAApbAAAAAAAAAABtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACAAAAAcAEcAbwBvAGcAbABlACAASQBuAGMALgAgADIAMAAxADb/2wBDAKBueIx4ZKCMgoy0qqC+8P//8Nzc8P//////////////////////////////////////////////////////////2wBDAaq0tPDS8P//////////////////////////////////////////////////////////////////////////////wAARCAC9AL0DASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIDAf/EACIQAQEAAgEFAAIDAAAAAAAAAAABAhEhAxIxQVETMiJhcf/EABcBAQEBAQAAAAAAAAAAAAAAAAABAgP/xAAZEQEBAQEBAQAAAAAAAAAAAAAAARECEjH/2gAMAwEAAhEDEQA/AMsce7/Gkkngk1NOoxboAIAAAAAAAAAAAAAAAAAAm4ys7NXVbJyx2LKoAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZLfRcbJug4AABJugDtxs9OAAAAAAAAAAAAAA7jN0HccN81cxk9OityBZuaAVP459OyKBMifxwxw1dqAyBrYCpuEvhFmq1TnNwZsZgIyAAAAAAAANcJqM8ZutRrkAVoAAAAAAAAABnnNVLTObjNGKACAAAAAAL6c9rcxmsY6rcALdTYrlyk8kzlZeRGPTYThdzSlbC2TyMsruiW4vvimK+nedIkqwFaGN4rZnnP5DPSQEZAAAAAJ5BtPACug5n+tdAYi8sL6cmFqOeO9P2sk1NCtwYtk5Yb5gljNWH7Odt+NMce2IkjoCtiOp6WjqeIJfiAEYAAAACeYANgllnAroA5lbJxAdEzOe+FblDQAAAAcuUntyZW3iCaoAUT1PEUjOy8CX4gBGAAAAAACWzw0mf1mBLjYZS2eFTqfVa9O9TWv7Zu27u3EZtd7r9O/L64Brvdfrm79AHZ55asVY56mhZWjlykRc7Ui3pWWdqQGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHMbuOsZbLw0xy2LYoAQAAAAAAAAAAAAAAAAAARnlzqGWdnEQNSP/2Q=="
@ -57,33 +51,27 @@ private class HomeScreenParametersProvider : PreviewParameterProvider<HomeScreen
.let(::BitmapPainter)
override val values = sequenceOf(
HomeScreenParameters(
supportedVersion = stableVersion,
state = InstallsState.Fetched(
persistentListOf(
InstallData(
name = "Aliucord",
packageName = "com.aliucord",
version = stableVersion,
icon = aliucordIcon,
isUpToDate = true,
)
InstallsState.Fetched(
persistentListOf(
InstallData(
name = "Aliucord",
packageName = "com.aliucord",
version = stableVersion,
icon = aliucordIcon,
isUpToDate = true,
)
),
)
),
HomeScreenParameters(
supportedVersion = stableVersion,
state = InstallsState.Fetched(
persistentListOf(
InstallData(
name = "Discord",
packageName = "com.discord",
version = stableVersion,
icon = aliucordIcon,
isUpToDate = false,
)
InstallsState.Fetched(
persistentListOf(
InstallData(
name = "Discord",
packageName = "com.discord",
version = stableVersion,
icon = aliucordIcon,
isUpToDate = false,
)
),
)
),
)
}

View file

@ -42,9 +42,6 @@ class HomeModel(
private val maven: AliucordMavenService,
private val json: Json,
) : ScreenModel {
var supportedVersion by mutableStateOf<DiscordVersion>(DiscordVersion.None)
private set
var installsState by mutableStateOf<InstallsState>(InstallsState.Fetching)
private set
@ -176,37 +173,25 @@ class HomeModel(
}
}
// FIXME: Properly show state when one of these fetches fail,
// to ensure the people know that the up-to-date check does not work
private suspend fun fetchRemoteData() {
github.getBuildData().fold(
success = {
val versionCode = it.discordVersionCode.toIntOrNull()
if (versionCode == null) {
supportedVersion = DiscordVersion.Error
return
}
remoteDataJson = it
supportedVersion = DiscordVersion.Existing(
type = DiscordVersion.parseVersionType(versionCode),
name = it.discordVersionName.split("-")[0].trim(),
code = versionCode,
listOf(
screenModelScope.launch {
github.getBuildData().fold(
success = { remoteDataJson = it },
fail = { Log.w(BuildConfig.TAG, "Failed to fetch remote build data", it) },
)
},
fail = {
Log.w(BuildConfig.TAG, "Failed to fetch remote build data", it)
supportedVersion = DiscordVersion.Error
screenModelScope.launch {
maven.getAliuhookVersion().fold(
success = { latestAliuhookVersion = it },
fail = { Log.w(BuildConfig.TAG, "Failed to fetch latest Aliuhook version", it) },
)
},
)
).joinAll()
maven.getAliuhookVersion().fold(
success = { latestAliuhookVersion = it },
fail = {
Log.w(BuildConfig.TAG, "Failed to fetch latest Aliuhook version", it)
supportedVersion = DiscordVersion.Error
},
)
if (remoteDataJson == null || latestAliuhookVersion == null) {
application.showToast(R.string.home_network_fail)
}
}
/**
@ -238,7 +223,7 @@ class HomeModel(
val versionCode = pkg.versionCode
// Check if the base APK version is a mismatch
if (remoteBuildData.discordVersionCode.toIntOrNull() != versionCode) return false
if (remoteBuildData.discordVersionCode != versionCode) return false
// Try to parse install metadata. If none present, install was made via legacy installer.
val apkPath = pkg.applicationInfo?.publicSourceDir ?: return false

View file

@ -32,7 +32,6 @@ import com.aliucord.manager.ui.components.ProjectHeader
import com.aliucord.manager.ui.screens.home.components.*
import com.aliucord.manager.ui.screens.patchopts.PatchOptionsScreen
import com.aliucord.manager.ui.screens.plugins.PluginsScreen
import com.aliucord.manager.ui.util.DiscordVersion
import com.aliucord.manager.ui.util.paddings.PaddingValuesSides
import com.aliucord.manager.ui.util.paddings.exclude
import com.aliucord.manager.util.pushOnce
@ -64,7 +63,6 @@ class HomeScreen : Screen, Parcelable {
when (val state = model.installsState) {
is InstallsState.Fetched -> HomeScreenLoadedContent(
state = state,
supportedVersion = model.supportedVersion,
padding = padding,
onClickInstall = { navigator.pushOnce(PatchOptionsScreen()) },
onUpdate = {
@ -119,7 +117,6 @@ fun HomeScreenLoadingContent(padding: PaddingValues) {
@Composable
fun HomeScreenLoadedContent(
state: InstallsState.Fetched,
supportedVersion: DiscordVersion,
padding: PaddingValues,
onClickInstall: () -> Unit,
onUpdate: (packageName: String) -> Unit,
@ -153,20 +150,14 @@ fun HomeScreenLoadedContent(
}
items(state.data, key = { it.packageName }) { item ->
AnimatedVisibility(
enter = fadeIn() + slideInHorizontally { it * -2 },
exit = fadeOut() + slideOutHorizontally { it * 2 },
visible = supportedVersion !is DiscordVersion.None,
) {
InstalledItemCard(
data = item,
onUpdate = { onUpdate(item.packageName) },
onOpenApp = { onOpenApp(item.packageName) },
onOpenInfo = { onOpenAppInfo(item.packageName) },
onOpenPlugins = { onOpenPlugins(item.packageName) },
modifier = Modifier.fillMaxWidth()
)
}
InstalledItemCard(
data = item,
onUpdate = { onUpdate(item.packageName) },
onOpenApp = { onOpenApp(item.packageName) },
onOpenInfo = { onOpenAppInfo(item.packageName) },
onOpenPlugins = { onOpenPlugins(item.packageName) },
modifier = Modifier.fillMaxWidth()
)
}
}
}

View file

@ -3,8 +3,8 @@ package com.aliucord.manager.ui.screens.patchopts
import android.os.Parcelable
import androidx.compose.runtime.Immutable
import androidx.compose.ui.graphics.Color
import com.aliucord.manager.util.ColorParceler
import com.aliucord.manager.util.ColorSerializer
import com.aliucord.manager.util.serialization.ColorParceler
import com.aliucord.manager.util.serialization.ColorSerializer
import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.TypeParceler
import kotlinx.serialization.SerialName

View file

@ -1,7 +1,7 @@
package com.aliucord.manager.ui.screens.plugins.model
import androidx.compose.runtime.Immutable
import com.aliucord.manager.util.ImmutableListSerializer
import com.aliucord.manager.util.serialization.ImmutableListSerializer
import kotlinx.collections.immutable.ImmutableList
import kotlinx.serialization.Serializable

View file

@ -1,4 +1,4 @@
package com.aliucord.manager.util
package com.aliucord.manager.util.serialization
import android.os.Parcel
import androidx.compose.ui.graphics.Color

View file

@ -1,4 +1,4 @@
package com.aliucord.manager.util
package com.aliucord.manager.util.serialization
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb

View file

@ -1,4 +1,4 @@
package com.aliucord.manager.util
package com.aliucord.manager.util.serialization
import com.aliucord.manager.ui.util.toUnsafeImmutable
import kotlinx.collections.immutable.ImmutableList

View file

@ -0,0 +1,28 @@
package com.aliucord.manager.util.serialization
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerializationException
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
/**
* (De)serializes an integer as a string.
*/
object IntAsStringSerializer : KSerializer<Int> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Int", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Int) {
encoder.encodeString(value.toString())
}
override fun deserialize(decoder: Decoder): Int {
val stringValue = decoder.decodeString()
return try {
stringValue.toInt()
} catch (e: NumberFormatException) {
throw SerializationException(e)
}
}
}

View file

@ -151,6 +151,8 @@
<string name="downloader_err_file_exists">Download failed (File exists)</string>
<string name="downloader_err_storage_space">Download failed (Insufficient space)</string>
<string name="home_network_fail">Failed to fetch data!</string>
<string name="patchopts_title">Aliucord provides several customizations at installation time that are not able to be changed once installed.</string>
<string name="patchopts_pkgname_title">Package Name</string>
<string name="patchopts_pkgname_desc">The package name is a unique identifier for all apps. Using different ones can allow for multiple installations of Aliucord.</string>