mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2026-03-13 08:41:57 +08:00
Compare commits
2 Commits
api@1.0.0-
...
feat/chang
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95cd097519 | ||
|
|
d685382277 |
@@ -20,16 +20,23 @@ class ReVancedAPI(
|
|||||||
) {
|
) {
|
||||||
private suspend fun apiUrl() = prefs.api.get()
|
private suspend fun apiUrl() = prefs.api.get()
|
||||||
|
|
||||||
private suspend inline fun <reified T> request(api: String, route: String): APIResponse<T> =
|
private suspend inline fun <reified T> request(
|
||||||
|
api: String,
|
||||||
|
route: String,
|
||||||
|
version: String? = "v4"
|
||||||
|
): APIResponse<T> =
|
||||||
withContext(
|
withContext(
|
||||||
Dispatchers.IO
|
Dispatchers.IO
|
||||||
) {
|
) {
|
||||||
client.request {
|
client.request {
|
||||||
url("$api/v4/$route")
|
url("$api/$version/$route")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend inline fun <reified T> request(route: String) = request<T>(apiUrl(), route)
|
private suspend inline fun <reified T> request(
|
||||||
|
route: String,
|
||||||
|
version: String = "v4"
|
||||||
|
) = request<T>(apiUrl(), route, version)
|
||||||
|
|
||||||
suspend fun getAnnouncements() = request<List<ReVancedAnnouncement>>("announcements")
|
suspend fun getAnnouncements() = request<List<ReVancedAnnouncement>>("announcements")
|
||||||
|
|
||||||
@@ -41,6 +48,8 @@ class ReVancedAPI(
|
|||||||
suspend fun getLatestAppInfo() =
|
suspend fun getLatestAppInfo() =
|
||||||
request<ReVancedAsset>("manager?prerelease=${prefs.useManagerPrereleases.get()}")
|
request<ReVancedAsset>("manager?prerelease=${prefs.useManagerPrereleases.get()}")
|
||||||
|
|
||||||
|
suspend fun getAppHistory() = request<ReVancedAsset>("patches/history", "dev")
|
||||||
|
|
||||||
suspend fun getPatchesUpdate() = request<ReVancedAsset>("patches?prerelease=${prefs.usePatchesPrereleases.get()}")
|
suspend fun getPatchesUpdate() = request<ReVancedAsset>("patches?prerelease=${prefs.usePatchesPrereleases.get()}")
|
||||||
|
|
||||||
suspend fun getContributors() = request<List<ReVancedGitRepository>>("contributors")
|
suspend fun getContributors() = request<List<ReVancedGitRepository>>("contributors")
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.NewReleases
|
import androidx.compose.material.icons.outlined.CalendarToday
|
||||||
|
import androidx.compose.material.icons.outlined.Campaign
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -21,7 +22,7 @@ import app.revanced.manager.ui.component.Markdown
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Changelog(
|
fun Changelog(
|
||||||
markdown: String,
|
description: String,
|
||||||
version: String,
|
version: String,
|
||||||
publishDate: String
|
publishDate: String
|
||||||
) {
|
) {
|
||||||
@@ -34,22 +35,32 @@ fun Changelog(
|
|||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Outlined.NewReleases,
|
imageVector = Icons.Outlined.Campaign,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(32.dp)
|
.size(32.dp)
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
"${version.removePrefix("v")} ($publishDate)",
|
version.removePrefix("v"),
|
||||||
style = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight(800)),
|
style = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight(800)),
|
||||||
color = MaterialTheme.colorScheme.primary,
|
color = MaterialTheme.colorScheme.primary,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Tag(
|
||||||
|
Icons.Outlined.CalendarToday,
|
||||||
|
publishDate
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Markdown(
|
||||||
|
description.removeVersionHeaderIfMatches(version),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Markdown(
|
|
||||||
markdown,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -70,4 +81,16 @@ private fun Tag(icon: ImageVector, text: String) {
|
|||||||
color = MaterialTheme.colorScheme.outline,
|
color = MaterialTheme.colorScheme.outline,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.removeVersionHeaderIfMatches(version: String): String {
|
||||||
|
val firstNewlineIndex = indexOf('\n')
|
||||||
|
if (firstNewlineIndex == -1) return this
|
||||||
|
|
||||||
|
val firstLine = substring(0, firstNewlineIndex).trim()
|
||||||
|
val versionWithoutPrefix = version.removePrefix("v")
|
||||||
|
|
||||||
|
if (!firstLine.contains(versionWithoutPrefix)) return this
|
||||||
|
|
||||||
|
return substring(firstNewlineIndex + 1).trimStart()
|
||||||
}
|
}
|
||||||
@@ -189,7 +189,7 @@ private fun ColumnScope.Changelog(releaseInfo: ReVancedAsset) {
|
|||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Changelog(
|
Changelog(
|
||||||
markdown = releaseInfo.description.replace("`", ""),
|
description = releaseInfo.description.replace("`", ""),
|
||||||
version = releaseInfo.version,
|
version = releaseInfo.version,
|
||||||
publishDate = releaseInfo.createdAt.relativeTime(LocalContext.current)
|
publishDate = releaseInfo.createdAt.relativeTime(LocalContext.current)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
package app.revanced.manager.ui.screen.settings.update
|
package app.revanced.manager.ui.screen.settings.update
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.material3.rememberTopAppBarState
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -17,10 +20,12 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
|
import app.revanced.manager.network.dto.ReVancedAsset
|
||||||
import app.revanced.manager.ui.component.AppTopBar
|
import app.revanced.manager.ui.component.AppTopBar
|
||||||
import app.revanced.manager.ui.component.ColumnWithScrollbar
|
import app.revanced.manager.ui.component.LazyColumnWithScrollbar
|
||||||
import app.revanced.manager.ui.component.LoadingIndicator
|
import app.revanced.manager.ui.component.LoadingIndicator
|
||||||
import app.revanced.manager.ui.component.settings.Changelog
|
import app.revanced.manager.ui.component.settings.Changelog
|
||||||
|
import app.revanced.manager.ui.viewmodel.ChangelogUiState
|
||||||
import app.revanced.manager.ui.viewmodel.ChangelogsViewModel
|
import app.revanced.manager.ui.viewmodel.ChangelogsViewModel
|
||||||
import app.revanced.manager.util.relativeTime
|
import app.revanced.manager.util.relativeTime
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
@@ -43,22 +48,61 @@ fun ChangelogsSettingsScreen(
|
|||||||
},
|
},
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
ColumnWithScrollbar(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(paddingValues)
|
.padding(paddingValues)
|
||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
contentAlignment = Alignment.Center
|
||||||
verticalArrangement = if (vm.releaseInfo == null) Arrangement.Center else Arrangement.Top
|
|
||||||
) {
|
) {
|
||||||
vm.releaseInfo?.let { info ->
|
when (val state = vm.state) {
|
||||||
Column(modifier = Modifier.padding(16.dp)) {
|
is ChangelogUiState.Loading -> LoadingIndicator()
|
||||||
Changelog(
|
|
||||||
markdown = info.description.replace("`", ""),
|
is ChangelogUiState.Error -> Text(
|
||||||
version = info.version,
|
text = state.message,
|
||||||
publishDate = info.createdAt.relativeTime(LocalContext.current)
|
style = MaterialTheme.typography.titleLarge
|
||||||
)
|
)
|
||||||
|
|
||||||
|
is ChangelogUiState.Success -> {
|
||||||
|
if (state.changelogs.isEmpty()) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.no_changelogs_found),
|
||||||
|
style = MaterialTheme.typography.titleLarge
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
LazyColumnWithScrollbar(modifier = Modifier.fillMaxSize()) {
|
||||||
|
items(
|
||||||
|
items = state.changelogs,
|
||||||
|
key = { it.version }
|
||||||
|
) { changelog ->
|
||||||
|
ChangelogItem(
|
||||||
|
changelog = changelog,
|
||||||
|
showDivider = changelog != state.changelogs.last()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} ?: LoadingIndicator()
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ChangelogItem(
|
||||||
|
changelog: ReVancedAsset,
|
||||||
|
showDivider: Boolean
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.padding(16.dp)) {
|
||||||
|
Changelog(
|
||||||
|
description = changelog.description,
|
||||||
|
version = changelog.version,
|
||||||
|
publishDate = changelog.createdAt.relativeTime(LocalContext.current)
|
||||||
|
)
|
||||||
|
if (showDivider) {
|
||||||
|
HorizontalDivider(
|
||||||
|
modifier = Modifier.padding(top = 32.dp),
|
||||||
|
color = MaterialTheme.colorScheme.outlineVariant
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,18 +12,57 @@ import app.revanced.manager.network.dto.ReVancedAsset
|
|||||||
import app.revanced.manager.network.utils.getOrThrow
|
import app.revanced.manager.network.utils.getOrThrow
|
||||||
import app.revanced.manager.util.uiSafe
|
import app.revanced.manager.util.uiSafe
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.datetime.LocalDateTime
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
sealed interface ChangelogUiState {
|
||||||
|
data object Loading : ChangelogUiState
|
||||||
|
data class Success(val changelogs: List<ReVancedAsset>) : ChangelogUiState
|
||||||
|
data class Error(val message: String) : ChangelogUiState
|
||||||
|
}
|
||||||
|
|
||||||
class ChangelogsViewModel(
|
class ChangelogsViewModel(
|
||||||
private val api: ReVancedAPI,
|
private val api: ReVancedAPI,
|
||||||
private val app: Application,
|
private val app: Application,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
var releaseInfo: ReVancedAsset? by mutableStateOf(null)
|
val mock = listOf(
|
||||||
|
ReVancedAsset(
|
||||||
|
version = "v1.25.2",
|
||||||
|
createdAt = LocalDateTime.parse("2025-07-07T13:01:12"),
|
||||||
|
description = """
|
||||||
|
## [1.25.2](https://github.com/ReVanced/revanced-manager/compare/v1.25.0...v1.25.1) (2025-07-07)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Disable Impeller Flutter render engine correctly to fix rendering issues
|
||||||
|
""".trimIndent(),
|
||||||
|
downloadUrl = "https://github.com/ReVanced/revanced-manager/releases/download/v1.25.1/revanced-manager-1.25.1.apk"
|
||||||
|
),
|
||||||
|
ReVancedAsset(
|
||||||
|
version = "v1.25.1",
|
||||||
|
createdAt = LocalDateTime.parse("2025-07-07T13:01:12"),
|
||||||
|
description = """
|
||||||
|
## [1.25.1](https://github.com/ReVanced/revanced-manager/compare/v1.25.0...v1.25.1) (2025-07-07)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Disable Impeller Flutter render engine correctly to fix rendering issues
|
||||||
|
""".trimIndent(),
|
||||||
|
downloadUrl = "https://github.com/ReVanced/revanced-manager/releases/download/v1.25.1/revanced-manager-1.25.1.apk"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
var state: ChangelogUiState by mutableStateOf(ChangelogUiState.Loading)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
uiSafe(app, R.string.changelog_download_fail, "Failed to download changelog") {
|
uiSafe(app, R.string.changelog_download_fail, "Failed to download changelog") {
|
||||||
releaseInfo = api.getLatestAppInfo().getOrThrow()
|
api.getLatestAppInfo().getOrThrow()
|
||||||
|
state = ChangelogUiState.Success(changelogs = mock)
|
||||||
|
}
|
||||||
|
if (state is ChangelogUiState.Loading) {
|
||||||
|
state = ChangelogUiState.Error(app.getString(R.string.changelog_download_fail))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user