Remove dependency on livedata libraries (#1453)

## Description
Refactors all usages of `LiveData` into flows in keeping with best
practices. This also includes refactoring several `ViewModel`s to
combine multiple `LiveData` into a new single `StateFlow`.

There are no user facing changes

### Related issues
None

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
This commit is contained in:
Damontecres 2026-05-25 08:27:19 -04:00 • committed by GitHub
parent 982d9e6fe8
commit 0b450fc0ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
90 changed files with 2170 additions and 2283 deletions

View file

@ -33,7 +33,7 @@ class ItemPlaybackRepository
item: BaseItem,
prefs: UserPreferences,
): ChosenStreams? =
serverRepository.currentUser.value?.let { user ->
serverRepository.currentUser?.let { user ->
val itemPlayback = itemPlaybackDao.getItem(user = user, itemId = itemId)
val plc = streamChoiceService.getPlaybackLanguageChoice(item.data)
Timber.v("For ${item.id}: itemPlayback=${itemPlayback != null}, plc=${plc != null}")
@ -91,7 +91,7 @@ class ItemPlaybackRepository
sourceId: UUID,
): ItemPlayback? =
withContext(Dispatchers.IO) {
serverRepository.currentUser.value?.let { user ->
serverRepository.currentUser?.let { user ->
val itemPlayback =
ItemPlayback(
userId = user.rowId,
@ -168,7 +168,7 @@ class ItemPlaybackRepository
val toSave =
if (itemPlayback.userId < 0) {
val userRowId =
serverRepository.currentUser.value
serverRepository.currentUser
?.rowId
?.takeIf { it >= 0 }
?: throw IllegalStateException("Trying to save an ItemPlayback without a user, but there is no current user")
@ -184,7 +184,7 @@ class ItemPlaybackRepository
itemId: UUID,
trackIndex: Int,
): ItemTrackModification? =
serverRepository.currentUser.value?.rowId?.let { userId ->
serverRepository.currentUser?.rowId?.let { userId ->
itemPlaybackDao.getTrackModifications(userId, itemId, trackIndex)
}
@ -193,7 +193,7 @@ class ItemPlaybackRepository
trackIndex: Int,
delay: Duration,
) {
serverRepository.currentUser.value?.rowId?.let { userId ->
serverRepository.currentUser?.rowId?.let { userId ->
Timber.v("Saving track mod item=%s, track=%s, delay=%s", itemId, trackIndex, delay)
itemPlaybackDao.saveItem(
ItemTrackModification(

View file

@ -4,18 +4,18 @@ import android.content.Context
import android.content.SharedPreferences
import androidx.core.content.edit
import androidx.datastore.core.DataStore
import androidx.lifecycle.LiveData
import androidx.lifecycle.map
import com.github.damontecres.wholphin.data.model.JellyfinServer
import com.github.damontecres.wholphin.data.model.JellyfinUser
import com.github.damontecres.wholphin.preferences.AppPreferences
import com.github.damontecres.wholphin.services.hilt.IoDispatcher
import com.github.damontecres.wholphin.ui.setValueOnMain
import com.github.damontecres.wholphin.ui.toServerString
import com.github.damontecres.wholphin.util.EqualityMutableLiveData
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
import org.jellyfin.sdk.Jellyfin
@ -46,14 +46,17 @@ class ServerRepository
val userPreferencesDataStore: DataStore<AppPreferences>,
@param:IoDispatcher private val ioDispatcher: CoroutineDispatcher,
) {
private var _current = EqualityMutableLiveData<CurrentUser?>(null)
val current: LiveData<CurrentUser?> = _current
private var _current = MutableStateFlow<CurrentUser?>(null)
val current: StateFlow<CurrentUser?> = _current
private var _currentUserDto = EqualityMutableLiveData<UserDto?>(null)
val currentUserDto: LiveData<UserDto?> = _currentUserDto
private var _currentUserDto = MutableStateFlow<UserDto?>(null)
val currentUserDto: UserDto? get() = _currentUserDto.value
val currentUserDtoFlow: StateFlow<UserDto?> get() = _currentUserDto
val currentServer: LiveData<JellyfinServer?> get() = _current.map { it?.server }
val currentUser: LiveData<JellyfinUser?> get() = _current.map { it?.user }
val currentServer: JellyfinServer? get() = _current.value?.server
val currentServerFlow: Flow<JellyfinServer?> get() = _current.map { it?.server }
val currentUser: JellyfinUser? get() = _current.value?.user
val currentUserFlow: Flow<JellyfinUser?> get() = _current.map { it?.user }
/**
* Adds a server to the app database and updated the [ApiClient] to the server's URL
@ -65,7 +68,7 @@ class ServerRepository
serverDao.addOrUpdateServer(server)
}
apiClient.update(baseUrl = server.url, accessToken = null)
_current.setValueOnMain(null)
_current.value = null
}
/**
@ -127,7 +130,7 @@ class ServerRepository
userId: UUID?,
): CurrentUser? {
if (serverId == null || userId == null) {
_current.setValueOnMain(null)
_current.value = null
return null
}
val serverAndUsers =
@ -218,7 +221,7 @@ class ServerRepository
}
suspend fun removeUser(user: JellyfinUser) {
if (currentUser.value?.id == user.id) {
if (current.value?.user?.id == user.id) {
withContext(Dispatchers.Main) {
_current.value = null
}
@ -237,7 +240,7 @@ class ServerRepository
}
suspend fun removeServer(server: JellyfinServer) {
if (currentServer.value?.id == server.id) {
if (current.value?.server?.id == server.id) {
withContext(Dispatchers.Main) {
_current.value = null
}
@ -274,18 +277,19 @@ class ServerRepository
) {
val newUser = user.copy(pin = pin, requireLogin = requireLogin)
val updatedUser = serverDao.addOrUpdateUser(newUser)
if (currentUser.value?.id == updatedUser.id && currentServer.value?.id == user.serverId) {
val cur = current.value
if (cur?.user?.id == updatedUser.id && cur.server?.id == user.serverId) {
// Updating current user, so push out the change
current.value?.let {
val newCurrent = it.copy(user = updatedUser)
_current.setValueOnMain(newCurrent)
_current.value = newCurrent
}
}
}
suspend fun authorizeQuickConnect(code: String): Boolean =
withContext(ioDispatcher) {
val userId = currentUser.value?.id
val userId = current.value?.user?.id
if (userId == null) {
Timber.e("No user logged in for Quick Connect authorization")
throw IllegalStateException("Must be logged in to authorize Quick Connect")