mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2026-03-13 08:41:57 +08:00
feat: migrate to API v5
This commit is contained in:
@@ -2,7 +2,6 @@ package app.revanced.manager.domain.repository
|
||||
|
||||
import app.revanced.manager.network.api.ReVancedAPI
|
||||
import app.revanced.manager.network.dto.ReVancedAnnouncement
|
||||
import app.revanced.manager.network.dto.ReVancedAnnouncementTag
|
||||
import app.revanced.manager.network.utils.getOrNull
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
@@ -12,7 +11,6 @@ class AnnouncementRepository(
|
||||
) {
|
||||
private val mutex = Mutex()
|
||||
private var cachedAnnouncements: List<ReVancedAnnouncement>? = null
|
||||
private var cachedTags: List<ReVancedAnnouncementTag>? = null
|
||||
|
||||
suspend fun getAnnouncements(forceRefresh: Boolean = false): List<ReVancedAnnouncement>? {
|
||||
mutex.withLock {
|
||||
@@ -22,13 +20,4 @@ class AnnouncementRepository(
|
||||
return cachedAnnouncements
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getTags(forceRefresh: Boolean = false): List<ReVancedAnnouncementTag>? {
|
||||
mutex.withLock {
|
||||
if (cachedTags == null || forceRefresh) {
|
||||
cachedTags = api.getAnnouncementTags().getOrNull()
|
||||
}
|
||||
return cachedTags
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ package app.revanced.manager.network.api
|
||||
|
||||
import app.revanced.manager.BuildConfig
|
||||
import app.revanced.manager.domain.manager.PreferencesManager
|
||||
import app.revanced.manager.domain.manager.base.Preference
|
||||
import app.revanced.manager.network.dto.ReVancedAnnouncement
|
||||
import app.revanced.manager.network.dto.ReVancedAnnouncementTag
|
||||
import app.revanced.manager.network.dto.ReVancedAsset
|
||||
import app.revanced.manager.network.dto.ReVancedGitRepository
|
||||
import app.revanced.manager.network.dto.ReVancedInfo
|
||||
@@ -25,7 +25,7 @@ class ReVancedAPI(
|
||||
Dispatchers.IO
|
||||
) {
|
||||
client.request {
|
||||
url("$api/v4/$route")
|
||||
url("$api/v5/$route")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,17 +33,19 @@ class ReVancedAPI(
|
||||
|
||||
suspend fun getAnnouncements() = request<List<ReVancedAnnouncement>>("announcements")
|
||||
|
||||
suspend fun getAnnouncementTags() = request<List<ReVancedAnnouncementTag>>("announcements/tags")
|
||||
|
||||
suspend fun getAppUpdate() =
|
||||
getLatestAppInfo().getOrThrow().takeIf { it.version.removePrefix("v") != BuildConfig.VERSION_NAME }
|
||||
|
||||
suspend fun getLatestAppInfo() =
|
||||
request<ReVancedAsset>("manager?prerelease=${prefs.useManagerPrereleases.get()}")
|
||||
request<ReVancedAsset>("manager${prefs.useManagerPrereleases.prereleaseString()}")
|
||||
|
||||
suspend fun getPatchesUpdate() = request<ReVancedAsset>("patches?prerelease=${prefs.usePatchesPrereleases.get()}")
|
||||
suspend fun getPatchesUpdate() = request<ReVancedAsset>("patches${prefs.usePatchesPrereleases.prereleaseString()}")
|
||||
|
||||
suspend fun getContributors() = request<List<ReVancedGitRepository>>("contributors")
|
||||
|
||||
suspend fun getInfo(api: String? = null) = request<ReVancedInfo>(api ?: apiUrl(), "about")
|
||||
|
||||
private companion object {
|
||||
suspend fun Preference<Boolean>.prereleaseString() = if (get()) "/prerelease" else ""
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,12 @@ package app.revanced.manager.network.dto
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.toInstant
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.time.Clock
|
||||
|
||||
@Parcelize
|
||||
@Serializable
|
||||
@@ -14,10 +17,11 @@ data class ReVancedAnnouncement(
|
||||
val title: String,
|
||||
val content: String,
|
||||
val tags: List<String>,
|
||||
val attachments: List<String>,
|
||||
@SerialName("created_at")
|
||||
val createdAt: LocalDateTime,
|
||||
@SerialName("archived_at")
|
||||
val archivedAt: LocalDateTime,
|
||||
val archivedAt: LocalDateTime?,
|
||||
val level: Int,
|
||||
) : Parcelable
|
||||
) : Parcelable {
|
||||
val isArchived get() = archivedAt?.toInstant(TimeZone.UTC)?.let { it < Clock.System.now() } ?: false
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package app.revanced.manager.network.dto
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ReVancedAnnouncementTag(
|
||||
val name: String
|
||||
)
|
||||
@@ -50,10 +50,7 @@ import app.revanced.manager.ui.component.LoadingIndicator
|
||||
import app.revanced.manager.ui.viewmodel.AnnouncementsViewModel
|
||||
import app.revanced.manager.util.relativeTime
|
||||
import app.revanced.manager.util.transparentListItemColors
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.toInstant
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import kotlin.time.Clock
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -64,7 +61,7 @@ fun AnnouncementsScreen(
|
||||
) {
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
var showFilterSheet by rememberSaveable { mutableStateOf(false) }
|
||||
val tags by vm.tags.collectAsStateWithLifecycle()
|
||||
val tags by vm.tags.collectAsStateWithLifecycle(null)
|
||||
val selectedTags by vm.selectedTags.getAsState()
|
||||
val showArchived by vm.showArchived.collectAsStateWithLifecycle()
|
||||
val announcements by vm.announcements.collectAsStateWithLifecycle(emptyList())
|
||||
@@ -129,6 +126,7 @@ fun AnnouncementsScreen(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
|
||||
AnnouncementCard(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
@@ -141,7 +139,7 @@ fun AnnouncementsScreen(
|
||||
author = announcement.author,
|
||||
content = announcement.content,
|
||||
unread = announcement.id !in readAnnouncements,
|
||||
archived = announcement.archivedAt.toInstant(TimeZone.UTC) < Clock.System.now()
|
||||
archived = announcement.isArchived
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -161,7 +159,7 @@ fun AnnouncementsScreen(
|
||||
@Composable
|
||||
private fun FilterBottomSheet(
|
||||
onDismissRequest: () -> Unit,
|
||||
tags: List<String>,
|
||||
tags: Set<String>,
|
||||
selectedTags: Set<String>,
|
||||
showArchived: Boolean,
|
||||
onShowArchivedChange: (Boolean) -> Unit,
|
||||
|
||||
@@ -6,15 +6,12 @@ import app.revanced.manager.data.platform.NetworkInfo
|
||||
import app.revanced.manager.domain.manager.PreferencesManager
|
||||
import app.revanced.manager.domain.repository.AnnouncementRepository
|
||||
import app.revanced.manager.network.dto.ReVancedAnnouncement
|
||||
import kotlin.time.Clock
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.toInstant
|
||||
|
||||
class AnnouncementsViewModel(
|
||||
private val announcementRepository: AnnouncementRepository,
|
||||
@@ -23,37 +20,26 @@ class AnnouncementsViewModel(
|
||||
) : ViewModel() {
|
||||
private val allAnnouncements = MutableStateFlow<List<ReVancedAnnouncement>?>(null)
|
||||
|
||||
private val _tags = MutableStateFlow<List<String>?>(null)
|
||||
val tags get() = _tags.asStateFlow()
|
||||
val tags = allAnnouncements.map { it?.tags }
|
||||
val selectedTags = preferences.selectedAnnouncementTags
|
||||
val readAnnouncements = preferences.readAnnouncements
|
||||
val showArchived = MutableStateFlow(false)
|
||||
|
||||
val announcements = combine(
|
||||
allAnnouncements,
|
||||
_tags,
|
||||
selectedTags.flow,
|
||||
showArchived
|
||||
) { source, tags, selectedTags, showArchived ->
|
||||
) { source, selectedTags, showArchived ->
|
||||
if (source == null) return@combine null
|
||||
// Only filter by tags that actually exist
|
||||
val availableTags = tags.orEmpty().toSet()
|
||||
val availableTags = source.tags
|
||||
val validSelected = selectedTags.intersect(availableTags)
|
||||
|
||||
val visibleAnnouncements = if (showArchived) {
|
||||
source
|
||||
} else {
|
||||
source.filter { announcement ->
|
||||
announcement.archivedAt.toInstant(TimeZone.UTC) > Clock.System.now()
|
||||
}
|
||||
}
|
||||
source.filter { announcement ->
|
||||
if (!showArchived && announcement.isArchived) return@filter false
|
||||
|
||||
if (validSelected.isEmpty()) {
|
||||
visibleAnnouncements
|
||||
} else {
|
||||
visibleAnnouncements.filter { announcement ->
|
||||
announcement.tags.any(validSelected::contains)
|
||||
}
|
||||
if (!validSelected.isEmpty()) announcement.tags.any(validSelected::contains)
|
||||
else true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +70,6 @@ class AnnouncementsViewModel(
|
||||
viewModelScope.launch {
|
||||
if (!network.isConnected()) {
|
||||
allAnnouncements.value = emptyList()
|
||||
_tags.value = emptyList()
|
||||
return@launch
|
||||
}
|
||||
|
||||
@@ -92,10 +77,14 @@ class AnnouncementsViewModel(
|
||||
announcementRepository.getAnnouncements()?.let {
|
||||
allAnnouncements.value = it
|
||||
}
|
||||
announcementRepository.getTags()?.let {
|
||||
_tags.value = it.map { tag -> tag.name }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val List<ReVancedAnnouncement>.tags: Set<String>
|
||||
get() = flatMapTo(
|
||||
mutableSetOf()
|
||||
) { it.tags }
|
||||
}
|
||||
}
|
||||
@@ -100,15 +100,12 @@ class DashboardViewModel(
|
||||
val readAnnouncements = prefs.readAnnouncements.get()
|
||||
|
||||
unreadAnnouncement = announcements.firstOrNull { announcement ->
|
||||
val isNotArchived =
|
||||
announcement.archivedAt.toInstant(TimeZone.UTC) > Clock.System.now()
|
||||
|
||||
val hasRelevantTag = announcement.tags.any {
|
||||
it == "✨ ReVanced" || it == "💊 Manager"
|
||||
}
|
||||
val isUnread = announcement.id !in readAnnouncements
|
||||
|
||||
isNotArchived && hasRelevantTag && isUnread
|
||||
!announcement.isArchived && hasRelevantTag && isUnread
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user