mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Add my requests page
This commit is contained in:
parent
921ffadc1c
commit
ee640f82db
11 changed files with 364 additions and 13 deletions
|
|
@ -23,7 +23,9 @@ data class BaseItem(
|
||||||
val data: BaseItemDto,
|
val data: BaseItemDto,
|
||||||
val useSeriesForPrimary: Boolean,
|
val useSeriesForPrimary: Boolean,
|
||||||
) : CardGridItem {
|
) : CardGridItem {
|
||||||
override val id get() = data.id
|
val id get() = data.id
|
||||||
|
|
||||||
|
override val gridId get() = id.toString()
|
||||||
|
|
||||||
override val playable: Boolean
|
override val playable: Boolean
|
||||||
get() = type.playable
|
get() = type.playable
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,9 @@ import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.layout.onGloballyPositioned
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextDecoration
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.tv.material3.Card
|
import androidx.tv.material3.Card
|
||||||
import androidx.tv.material3.CardDefaults
|
import androidx.tv.material3.CardDefaults
|
||||||
|
|
@ -35,7 +33,6 @@ import com.github.damontecres.wholphin.ui.components.Genre
|
||||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||||
import com.github.damontecres.wholphin.ui.setup.rememberIdColor
|
import com.github.damontecres.wholphin.ui.setup.rememberIdColor
|
||||||
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
||||||
import timber.log.Timber
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -173,11 +173,12 @@ class GenreViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Genre(
|
data class Genre(
|
||||||
override val id: UUID,
|
val id: UUID,
|
||||||
val name: String,
|
val name: String,
|
||||||
val imageUrl: String?,
|
val imageUrl: String?,
|
||||||
val color: Color,
|
val color: Color,
|
||||||
) : CardGridItem {
|
) : CardGridItem {
|
||||||
|
override val gridId: String get() = id.toString()
|
||||||
override val playable: Boolean = false
|
override val playable: Boolean = false
|
||||||
override val sortName: String get() = name
|
override val sortName: String get() = name
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,12 +73,11 @@ import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
private const val DEBUG = false
|
private const val DEBUG = false
|
||||||
|
|
||||||
interface CardGridItem {
|
interface CardGridItem {
|
||||||
val id: UUID
|
val gridId: String
|
||||||
val playable: Boolean
|
val playable: Boolean
|
||||||
val sortName: String
|
val sortName: String
|
||||||
}
|
}
|
||||||
|
|
@ -244,7 +243,7 @@ fun <T : CardGridItem> CardGrid(
|
||||||
} else if (isPlayKeyUp(it)) {
|
} else if (isPlayKeyUp(it)) {
|
||||||
val item = pager.getOrNull(focusedIndex)
|
val item = pager.getOrNull(focusedIndex)
|
||||||
if (item?.playable == true) {
|
if (item?.playable == true) {
|
||||||
Timber.v("Clicked play on ${item.id}")
|
Timber.v("Clicked play on ${item.gridId}")
|
||||||
onClickPlay.invoke(focusedIndex, item)
|
onClickPlay.invoke(focusedIndex, item)
|
||||||
}
|
}
|
||||||
return@onKeyEvent true
|
return@onKeyEvent true
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
package com.github.damontecres.wholphin.ui.discover
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.slideInVertically
|
||||||
|
import androidx.compose.animation.slideOutVertically
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
|
import com.github.damontecres.wholphin.R
|
||||||
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
|
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
|
import com.github.damontecres.wholphin.ui.components.TabRow
|
||||||
|
import com.github.damontecres.wholphin.ui.logTab
|
||||||
|
import com.github.damontecres.wholphin.ui.nav.NavDrawerItem
|
||||||
|
import com.github.damontecres.wholphin.ui.preferences.PreferencesViewModel
|
||||||
|
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DiscoverPage(
|
||||||
|
preferences: UserPreferences,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
preferencesViewModel: PreferencesViewModel = hiltViewModel(),
|
||||||
|
) {
|
||||||
|
val rememberedTabIndex =
|
||||||
|
remember { preferencesViewModel.getRememberedTab(preferences, NavDrawerItem.Discover.id, 0) }
|
||||||
|
|
||||||
|
val tabs =
|
||||||
|
listOf(
|
||||||
|
stringResource(R.string.discover),
|
||||||
|
stringResource(R.string.request),
|
||||||
|
)
|
||||||
|
var selectedTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) }
|
||||||
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
|
val firstTabFocusRequester = remember { FocusRequester() }
|
||||||
|
LaunchedEffect(Unit) { firstTabFocusRequester.tryRequestFocus() }
|
||||||
|
|
||||||
|
LaunchedEffect(selectedTabIndex) {
|
||||||
|
logTab("discover", selectedTabIndex)
|
||||||
|
preferencesViewModel.saveRememberedTab(preferences, NavDrawerItem.Discover.id, selectedTabIndex)
|
||||||
|
preferencesViewModel.backdropService.clearBackdrop()
|
||||||
|
}
|
||||||
|
|
||||||
|
var showHeader by rememberSaveable { mutableStateOf(true) }
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) { focusRequester.tryRequestFocus() }
|
||||||
|
Column(
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
AnimatedVisibility(
|
||||||
|
showHeader,
|
||||||
|
enter = slideInVertically() + fadeIn(),
|
||||||
|
exit = slideOutVertically() + fadeOut(),
|
||||||
|
) {
|
||||||
|
TabRow(
|
||||||
|
selectedTabIndex = selectedTabIndex,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.padding(start = 32.dp, top = 16.dp, bottom = 16.dp)
|
||||||
|
.focusRequester(firstTabFocusRequester),
|
||||||
|
tabs = tabs,
|
||||||
|
onClick = { selectedTabIndex = it },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
when (selectedTabIndex) {
|
||||||
|
// Discover
|
||||||
|
0 -> {
|
||||||
|
SeerrDiscoverPage(
|
||||||
|
preferences = preferences,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requests
|
||||||
|
1 -> {
|
||||||
|
SeerrRequestsPage(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
ErrorMessage("Invalid tab index $selectedTabIndex", null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.github.damontecres.wholphin.ui.seerr
|
package com.github.damontecres.wholphin.ui.discover
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
|
@ -0,0 +1,216 @@
|
||||||
|
package com.github.damontecres.wholphin.ui.discover
|
||||||
|
|
||||||
|
import androidx.compose.foundation.focusable
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import androidx.tv.material3.MaterialTheme
|
||||||
|
import androidx.tv.material3.Text
|
||||||
|
import com.github.damontecres.wholphin.R
|
||||||
|
import com.github.damontecres.wholphin.api.seerr.model.MediaRequest
|
||||||
|
import com.github.damontecres.wholphin.data.model.DiscoverItem
|
||||||
|
import com.github.damontecres.wholphin.data.model.SeerrItemType
|
||||||
|
import com.github.damontecres.wholphin.services.BackdropService
|
||||||
|
import com.github.damontecres.wholphin.services.NavigationManager
|
||||||
|
import com.github.damontecres.wholphin.services.SeerrServerRepository
|
||||||
|
import com.github.damontecres.wholphin.services.SeerrService
|
||||||
|
import com.github.damontecres.wholphin.ui.cards.DiscoverItemCard
|
||||||
|
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
|
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||||
|
import com.github.damontecres.wholphin.ui.detail.CardGrid
|
||||||
|
import com.github.damontecres.wholphin.ui.detail.CardGridItem
|
||||||
|
import com.github.damontecres.wholphin.ui.launchIO
|
||||||
|
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
|
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||||
|
import com.github.damontecres.wholphin.util.DataLoadingState
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.awaitAll
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.sync.Semaphore
|
||||||
|
import kotlinx.coroutines.sync.withPermit
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class SeerrRequestsViewModel
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
private val seerrServerRepository: SeerrServerRepository,
|
||||||
|
private val seerrService: SeerrService,
|
||||||
|
val navigationManager: NavigationManager,
|
||||||
|
private val backdropService: BackdropService,
|
||||||
|
) : ViewModel() {
|
||||||
|
val state = MutableStateFlow(SeerrRequestsState.EMPTY)
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewModelScope.launchIO {
|
||||||
|
backdropService.clearBackdrop()
|
||||||
|
}
|
||||||
|
seerrServerRepository.current
|
||||||
|
.onEach { user ->
|
||||||
|
state.update { it.copy(requests = DataLoadingState.Loading) }
|
||||||
|
if (user != null) {
|
||||||
|
val semaphore = Semaphore(3)
|
||||||
|
val mediaRequests =
|
||||||
|
seerrService.api.requestApi
|
||||||
|
.requestGet()
|
||||||
|
.results
|
||||||
|
.orEmpty()
|
||||||
|
val requests =
|
||||||
|
mediaRequests.mapNotNull { request ->
|
||||||
|
if (request.media?.tmdbId != null) {
|
||||||
|
viewModelScope.async(Dispatchers.IO) {
|
||||||
|
semaphore.withPermit {
|
||||||
|
val type = SeerrItemType.fromString(request.type)
|
||||||
|
when (type) {
|
||||||
|
SeerrItemType.MOVIE -> {
|
||||||
|
seerrService.api.moviesApi
|
||||||
|
.movieMovieIdGet(
|
||||||
|
movieId = request.media.tmdbId,
|
||||||
|
).let { DiscoverItem(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
SeerrItemType.TV -> {
|
||||||
|
seerrService.api.tvApi
|
||||||
|
.tvTvIdGet(tvId = request.media.tmdbId)
|
||||||
|
.let { DiscoverItem(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
SeerrItemType.PERSON -> {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
SeerrItemType.UNKNOWN -> {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}?.let { RequestGridItem(request, it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Timber.v("No TMDB ID for request %s", request.id)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val results = requests.awaitAll().filterNotNull()
|
||||||
|
|
||||||
|
state.update { it.copy(requests = DataLoadingState.Success(results)) }
|
||||||
|
}
|
||||||
|
}.launchIn(viewModelScope)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateBackdrop(item: DiscoverItem?) {
|
||||||
|
viewModelScope.launchIO {
|
||||||
|
if (item != null) {
|
||||||
|
backdropService.submit("discover_${item.id}", item.backDropUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class SeerrRequestsState(
|
||||||
|
val requests: DataLoadingState<List<RequestGridItem>>,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
val EMPTY = SeerrRequestsState(DataLoadingState.Pending)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class RequestGridItem(
|
||||||
|
val request: MediaRequest,
|
||||||
|
val item: DiscoverItem,
|
||||||
|
) : CardGridItem {
|
||||||
|
override val gridId: String = request.id.toString()
|
||||||
|
override val playable: Boolean = false
|
||||||
|
override val sortName: String = request.updatedAt ?: "0000"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SeerrRequestsPage(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
viewModel: SeerrRequestsViewModel = hiltViewModel(),
|
||||||
|
) {
|
||||||
|
val state by viewModel.state.collectAsState(SeerrRequestsState.EMPTY)
|
||||||
|
|
||||||
|
when (val state = state.requests) {
|
||||||
|
is DataLoadingState.Error -> {
|
||||||
|
ErrorMessage(state.message, state.exception, modifier.focusable())
|
||||||
|
}
|
||||||
|
|
||||||
|
DataLoadingState.Loading,
|
||||||
|
DataLoadingState.Pending,
|
||||||
|
-> {
|
||||||
|
LoadingPage(modifier.focusable())
|
||||||
|
}
|
||||||
|
|
||||||
|
is DataLoadingState.Success<List<RequestGridItem>> -> {
|
||||||
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
if (state.data.isNotEmpty()) {
|
||||||
|
focusRequester.tryRequestFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column(modifier = modifier) {
|
||||||
|
// Text(
|
||||||
|
// text = stringResource(R.string.request),
|
||||||
|
// style = MaterialTheme.typography.displaySmall,
|
||||||
|
// color = MaterialTheme.colorScheme.onBackground,
|
||||||
|
// textAlign = TextAlign.Center,
|
||||||
|
// modifier = Modifier.fillMaxWidth(),
|
||||||
|
// )
|
||||||
|
if (state.data.isEmpty()) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.no_results),
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onBackground,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
CardGrid(
|
||||||
|
pager = state.data,
|
||||||
|
onClickItem = { index: Int, item: RequestGridItem ->
|
||||||
|
viewModel.navigationManager.navigateTo(Destination.DiscoveredItem(item.item))
|
||||||
|
},
|
||||||
|
onLongClickItem = { index: Int, item: RequestGridItem ->
|
||||||
|
},
|
||||||
|
onClickPlay = { _, item ->
|
||||||
|
},
|
||||||
|
letterPosition = { c: Char -> 0 },
|
||||||
|
gridFocusRequester = focusRequester,
|
||||||
|
showJumpButtons = false,
|
||||||
|
showLetterButtons = false,
|
||||||
|
spacing = 24.dp,
|
||||||
|
cardContent = @Composable { item, onClick, onLongClick, mod ->
|
||||||
|
DiscoverItemCard(
|
||||||
|
item = item?.item,
|
||||||
|
onClick = onClick,
|
||||||
|
onLongClick = onLongClick,
|
||||||
|
showOverlay = true,
|
||||||
|
modifier = mod,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
columns = 6,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -28,11 +28,11 @@ import com.github.damontecres.wholphin.ui.detail.episode.EpisodeDetails
|
||||||
import com.github.damontecres.wholphin.ui.detail.movie.MovieDetails
|
import com.github.damontecres.wholphin.ui.detail.movie.MovieDetails
|
||||||
import com.github.damontecres.wholphin.ui.detail.series.SeriesDetails
|
import com.github.damontecres.wholphin.ui.detail.series.SeriesDetails
|
||||||
import com.github.damontecres.wholphin.ui.detail.series.SeriesOverview
|
import com.github.damontecres.wholphin.ui.detail.series.SeriesOverview
|
||||||
|
import com.github.damontecres.wholphin.ui.discover.DiscoverPage
|
||||||
import com.github.damontecres.wholphin.ui.main.HomePage
|
import com.github.damontecres.wholphin.ui.main.HomePage
|
||||||
import com.github.damontecres.wholphin.ui.main.SearchPage
|
import com.github.damontecres.wholphin.ui.main.SearchPage
|
||||||
import com.github.damontecres.wholphin.ui.playback.PlaybackPage
|
import com.github.damontecres.wholphin.ui.playback.PlaybackPage
|
||||||
import com.github.damontecres.wholphin.ui.preferences.PreferencesPage
|
import com.github.damontecres.wholphin.ui.preferences.PreferencesPage
|
||||||
import com.github.damontecres.wholphin.ui.seerr.SeerrDiscoverPage
|
|
||||||
import com.github.damontecres.wholphin.ui.setup.InstallUpdatePage
|
import com.github.damontecres.wholphin.ui.setup.InstallUpdatePage
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import org.jellyfin.sdk.model.api.CollectionType
|
import org.jellyfin.sdk.model.api.CollectionType
|
||||||
|
|
@ -253,7 +253,7 @@ fun DestinationContent(
|
||||||
}
|
}
|
||||||
|
|
||||||
Destination.Discover -> {
|
Destination.Discover -> {
|
||||||
SeerrDiscoverPage(
|
DiscoverPage(
|
||||||
preferences = preferences,
|
preferences = preferences,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -479,6 +479,11 @@ fun PreferencesContent(
|
||||||
} else {
|
} else {
|
||||||
val currentUser by seerrVm.currentUser.observeAsState()
|
val currentUser by seerrVm.currentUser.observeAsState()
|
||||||
val status by seerrVm.serverConnectionStatus.collectAsState(LoadingState.Pending)
|
val status by seerrVm.serverConnectionStatus.collectAsState(LoadingState.Pending)
|
||||||
|
LaunchedEffect(status) {
|
||||||
|
if (status == LoadingState.Success) {
|
||||||
|
showSeerrServerDialog = false
|
||||||
|
}
|
||||||
|
}
|
||||||
AddSeerServerDialog(
|
AddSeerServerDialog(
|
||||||
currentUsername = currentUser?.name,
|
currentUsername = currentUser?.name,
|
||||||
status = status,
|
status = status,
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic state for loading something from the API
|
* Generic state for loading something from the API
|
||||||
|
*
|
||||||
|
* @see DataLoadingState
|
||||||
*/
|
*/
|
||||||
sealed interface LoadingState {
|
sealed interface LoadingState {
|
||||||
data object Pending : LoadingState
|
data object Pending : LoadingState
|
||||||
|
|
@ -68,3 +70,28 @@ sealed interface HomeRowLoadingState {
|
||||||
listOfNotNull(message, exception?.localizedMessage).joinToString(" - ")
|
listOfNotNull(message, exception?.localizedMessage).joinToString(" - ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic state for loading something from the API
|
||||||
|
*
|
||||||
|
* @see LoadingState
|
||||||
|
*/
|
||||||
|
sealed interface DataLoadingState<out T> {
|
||||||
|
data object Pending : DataLoadingState<Nothing>
|
||||||
|
|
||||||
|
data object Loading : DataLoadingState<Nothing>
|
||||||
|
|
||||||
|
data class Success<T>(
|
||||||
|
val data: T,
|
||||||
|
) : DataLoadingState<T>
|
||||||
|
|
||||||
|
data class Error(
|
||||||
|
val message: String? = null,
|
||||||
|
val exception: Throwable? = null,
|
||||||
|
) : DataLoadingState<Nothing> {
|
||||||
|
constructor(exception: Throwable) : this(null, exception)
|
||||||
|
|
||||||
|
val localizedMessage: String =
|
||||||
|
listOfNotNull(message, exception?.localizedMessage).joinToString(" - ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1260,6 +1260,8 @@ components:
|
||||||
type: integer
|
type: integer
|
||||||
rootFolder:
|
rootFolder:
|
||||||
type: string
|
type: string
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
required:
|
required:
|
||||||
- id
|
- id
|
||||||
- status
|
- status
|
||||||
|
|
@ -1269,10 +1271,10 @@ components:
|
||||||
id:
|
id:
|
||||||
type: integer
|
type: integer
|
||||||
readOnly: true
|
readOnly: true
|
||||||
tmdbid:
|
tmdbId:
|
||||||
type: integer
|
type: integer
|
||||||
readOnly: true
|
readOnly: true
|
||||||
tvdbid:
|
tvdbId:
|
||||||
type: integer
|
type: integer
|
||||||
readOnly: true
|
readOnly: true
|
||||||
nullable: true
|
nullable: true
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue