Better handling for Seerr request permissions & minor discover UI tweaks (#2084)
## Description No longer assume that if a user has "Request 4K" then they must also have regular "Request". Now the requesting dialogs will handle: - User has only "Request" (including just "Request Movie" vs "Request TV") - User has only "Request 4K" - User has both Also fixes an issue where the request button would be disabled if a previous request was declined. And also because it helped me with testing this and is useful, the dialog in settings to remove a Seerr server now also shows the username in addition to URL. ### Related issues https://github.com/damontecres/Wholphin/issues/702#issuecomment-6132914691 ### Testing Manually on the emulator with the test cases above. I also tested with and without "Advanced Requests" permission ## Screenshots N/A, the dialog change is minor ## AI or LLM usage None
This commit is contained in:
parent
ebf44699ca
commit
e273195e93
11 changed files with 170 additions and 70 deletions
|
|
@ -54,3 +54,11 @@ fun SeerrUserConfig?.hasPermission(permission: SeerrPermission): Boolean {
|
|||
permission.parents.any { hasPermission(it) } ||
|
||||
SeerrPermission.ADMIN.hasPermission(permissions)
|
||||
}
|
||||
|
||||
val SeerrUserConfig?.canRequestMovies: Boolean
|
||||
get() =
|
||||
hasPermission(SeerrPermission.REQUEST_MOVIE) || hasPermission(SeerrPermission.REQUEST_4K_MOVIE)
|
||||
|
||||
val SeerrUserConfig?.canRequestTv: Boolean
|
||||
get() =
|
||||
hasPermission(SeerrPermission.REQUEST_TV) || hasPermission(SeerrPermission.REQUEST_4K_TV)
|
||||
|
|
|
|||
|
|
@ -205,11 +205,15 @@ data class CurrentSeerr(
|
|||
val config: SeerrUserConfig,
|
||||
val serverConfig: PublicSettings,
|
||||
) {
|
||||
val requestMovieEnabled: Boolean
|
||||
get() = config.hasPermission(SeerrPermission.REQUEST_MOVIE)
|
||||
val request4kMovieEnabled: Boolean
|
||||
get() =
|
||||
(serverConfig.movie4kEnabled ?: false) &&
|
||||
config.hasPermission(SeerrPermission.REQUEST_4K_MOVIE)
|
||||
|
||||
val requestTvEnabled: Boolean
|
||||
get() = config.hasPermission(SeerrPermission.REQUEST_TV)
|
||||
val request4kTvEnabled: Boolean
|
||||
get() =
|
||||
(serverConfig.series4kEnabled ?: false) &&
|
||||
|
|
|
|||
|
|
@ -231,28 +231,35 @@ fun DialogPopup(
|
|||
onDismissRequest = onDismissRequest,
|
||||
properties = properties,
|
||||
) {
|
||||
val elevatedContainerColor =
|
||||
MaterialTheme.colorScheme.surfaceColorAtElevation(elevation)
|
||||
DialogPopupContent(
|
||||
title = title,
|
||||
dialogItems = dialogItems,
|
||||
waiting = waiting,
|
||||
onDismissRequest = onDismissRequest,
|
||||
dismissOnClick = dismissOnClick,
|
||||
elevation = elevation,
|
||||
modifier =
|
||||
Modifier.onKeyEvent { event ->
|
||||
val code = event.nativeKeyEvent.keyCode
|
||||
if (event.nativeKeyEvent.action == KeyEvent.ACTION_UP &&
|
||||
code in
|
||||
setOf(
|
||||
KeyEvent.KEYCODE_ENTER,
|
||||
KeyEvent.KEYCODE_DPAD_CENTER,
|
||||
KeyEvent.KEYCODE_NUMPAD_ENTER,
|
||||
)
|
||||
) {
|
||||
waiting = false
|
||||
}
|
||||
false
|
||||
},
|
||||
Modifier
|
||||
.onKeyEvent { event ->
|
||||
val code = event.nativeKeyEvent.keyCode
|
||||
if (event.nativeKeyEvent.action == KeyEvent.ACTION_UP &&
|
||||
code in
|
||||
setOf(
|
||||
KeyEvent.KEYCODE_ENTER,
|
||||
KeyEvent.KEYCODE_DPAD_CENTER,
|
||||
KeyEvent.KEYCODE_NUMPAD_ENTER,
|
||||
)
|
||||
) {
|
||||
waiting = false
|
||||
}
|
||||
false
|
||||
}.shadow(elevation = elevation, shape = RoundedCornerShape(28.0.dp))
|
||||
.graphicsLayer {
|
||||
this.clip = true
|
||||
this.shape = RoundedCornerShape(28.0.dp)
|
||||
}.drawBehind { drawRect(color = elevatedContainerColor) }
|
||||
.padding(PaddingValues(24.dp)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -266,20 +273,10 @@ fun DialogPopupContent(
|
|||
onDismissRequest: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
dismissOnClick: Boolean = true,
|
||||
elevation: Dp = 8.dp,
|
||||
) {
|
||||
val elevatedContainerColor =
|
||||
MaterialTheme.colorScheme.surfaceColorAtElevation(elevation)
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier =
|
||||
modifier
|
||||
.shadow(elevation = elevation, shape = RoundedCornerShape(28.0.dp))
|
||||
.graphicsLayer {
|
||||
this.clip = true
|
||||
this.shape = RoundedCornerShape(28.0.dp)
|
||||
}.drawBehind { drawRect(color = elevatedContainerColor) }
|
||||
.padding(PaddingValues(24.dp)),
|
||||
modifier = modifier,
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
|
|
|
|||
|
|
@ -42,9 +42,8 @@ import com.github.damontecres.wholphin.data.model.DiscoverRating
|
|||
import com.github.damontecres.wholphin.data.model.LocalTrailer
|
||||
import com.github.damontecres.wholphin.data.model.RemoteTrailer
|
||||
import com.github.damontecres.wholphin.data.model.SeerrAvailability
|
||||
import com.github.damontecres.wholphin.data.model.SeerrPermission
|
||||
import com.github.damontecres.wholphin.data.model.Trailer
|
||||
import com.github.damontecres.wholphin.data.model.hasPermission
|
||||
import com.github.damontecres.wholphin.data.model.canRequestMovies
|
||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||
import com.github.damontecres.wholphin.services.SeerrUserConfig
|
||||
import com.github.damontecres.wholphin.services.TrailerService
|
||||
|
|
@ -84,14 +83,12 @@ fun DiscoverMovieDetails(
|
|||
}
|
||||
val state by viewModel.state.collectAsState()
|
||||
val userConfig by viewModel.userConfig.collectAsState(null)
|
||||
val requestEnabled by viewModel.requestEnabled.collectAsState(false)
|
||||
val request4kEnabled by viewModel.request4kEnabled.collectAsState(false)
|
||||
|
||||
var overviewDialog by remember { mutableStateOf<ItemDetailsDialogInfo?>(null) }
|
||||
var showRequestDialog by remember { mutableStateOf(false) }
|
||||
|
||||
val requestStr = stringResource(R.string.request)
|
||||
val request4kStr = stringResource(R.string.request_4k)
|
||||
|
||||
when (val st = state.movie) {
|
||||
is DataLoadingState.Error -> {
|
||||
ErrorMessage(st, modifier)
|
||||
|
|
@ -153,6 +150,7 @@ fun DiscoverMovieDetails(
|
|||
RequestMovieDialog(
|
||||
loading = state.profileLoading,
|
||||
data = state.requestData,
|
||||
requestEnabled = requestEnabled,
|
||||
request4kEnabled = request4kEnabled,
|
||||
movie = movie,
|
||||
onSubmit = {
|
||||
|
|
@ -253,7 +251,7 @@ fun DiscoverMovieDetailsContent(
|
|||
}
|
||||
}
|
||||
},
|
||||
canRequest = userConfig.hasPermission(SeerrPermission.REQUEST),
|
||||
canRequest = userConfig.canRequestMovies,
|
||||
canCancel = canCancel,
|
||||
trailers = trailers,
|
||||
trailerOnClick = trailerOnClick,
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@ class DiscoverMovieViewModel
|
|||
val state: StateFlow<DiscoverMovieState> = _state
|
||||
|
||||
val userConfig = seerrServerRepository.current.map { it?.config }
|
||||
val requestEnabled =
|
||||
seerrServerRepository.current.map { it?.requestMovieEnabled ?: false }
|
||||
val request4kEnabled =
|
||||
seerrServerRepository.current.map { it?.request4kMovieEnabled ?: false }
|
||||
|
||||
|
|
@ -191,7 +193,7 @@ class DiscoverMovieViewModel
|
|||
return@launchIO
|
||||
}
|
||||
try {
|
||||
seerrService.api.requestApi.requestPost(
|
||||
val postRequest =
|
||||
RequestPostRequest(
|
||||
is4k = request.is4k,
|
||||
mediaId = request.movieId,
|
||||
|
|
@ -205,8 +207,9 @@ class DiscoverMovieViewModel
|
|||
profileId = request.profileId,
|
||||
rootFolder = request.folder,
|
||||
tags = emptyList(),
|
||||
),
|
||||
)
|
||||
)
|
||||
Timber.v("postRequest=%s", postRequest)
|
||||
seerrService.api.requestApi.requestPost(postRequest)
|
||||
} catch (ex: CancellationException) {
|
||||
throw ex
|
||||
} catch (ex: Exception) {
|
||||
|
|
|
|||
|
|
@ -36,9 +36,8 @@ import com.github.damontecres.wholphin.data.model.BaseItem
|
|||
import com.github.damontecres.wholphin.data.model.DiscoverItem
|
||||
import com.github.damontecres.wholphin.data.model.DiscoverRating
|
||||
import com.github.damontecres.wholphin.data.model.SeerrAvailability
|
||||
import com.github.damontecres.wholphin.data.model.SeerrPermission
|
||||
import com.github.damontecres.wholphin.data.model.Trailer
|
||||
import com.github.damontecres.wholphin.data.model.hasPermission
|
||||
import com.github.damontecres.wholphin.data.model.canRequestTv
|
||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||
import com.github.damontecres.wholphin.services.SeerrUserConfig
|
||||
import com.github.damontecres.wholphin.services.TrailerService
|
||||
|
|
@ -87,6 +86,7 @@ fun DiscoverSeriesDetails(
|
|||
val context = LocalContext.current
|
||||
val resources = LocalResources.current
|
||||
val state by viewModel.state.collectAsState()
|
||||
val requestEnabled by viewModel.requestEnabled.collectAsState()
|
||||
val request4kEnabled by viewModel.request4kEnabled.collectAsState()
|
||||
|
||||
var overviewDialog by remember { mutableStateOf<ItemDetailsDialogInfo?>(null) }
|
||||
|
|
@ -111,6 +111,7 @@ fun DiscoverSeriesDetails(
|
|||
DiscoverSeriesDetailsContent(
|
||||
preferences = preferences,
|
||||
series = item,
|
||||
availability = state.availability,
|
||||
userConfig = userConfig,
|
||||
rating = state.rating,
|
||||
canCancel = state.canCancelRequest,
|
||||
|
|
@ -187,6 +188,7 @@ fun DiscoverSeriesDetails(
|
|||
title = state.tvSeries.successValue?.name ?: "",
|
||||
seasons = state.seasons,
|
||||
seasons4k = state.seasons4k,
|
||||
requestEnabled = requestEnabled,
|
||||
request4kEnabled = request4kEnabled,
|
||||
onSubmit = {
|
||||
showRequestSeasonDialog = false
|
||||
|
|
@ -211,6 +213,7 @@ fun DiscoverSeriesDetailsContent(
|
|||
preferences: UserPreferences,
|
||||
userConfig: SeerrUserConfig?,
|
||||
series: TvDetails,
|
||||
availability: SeerrAvailability,
|
||||
rating: DiscoverRating?,
|
||||
canCancel: Boolean,
|
||||
seasons: List<RequestSeason>,
|
||||
|
|
@ -274,9 +277,7 @@ fun DiscoverSeriesDetailsContent(
|
|||
.padding(top = HeaderUtils.topPadding, bottom = 16.dp),
|
||||
)
|
||||
ExpandableDiscoverButtons(
|
||||
availability =
|
||||
SeerrAvailability.from(series.mediaInfo?.status)
|
||||
?: SeerrAvailability.UNKNOWN,
|
||||
availability = availability,
|
||||
requestOnClick = requestOnClick,
|
||||
pendingOnClick = requestOnClick,
|
||||
cancelOnClick = cancelOnClick,
|
||||
|
|
@ -290,7 +291,7 @@ fun DiscoverSeriesDetailsContent(
|
|||
}
|
||||
}
|
||||
},
|
||||
canRequest = userConfig.hasPermission(SeerrPermission.REQUEST),
|
||||
canRequest = userConfig.canRequestTv,
|
||||
canCancel = canCancel,
|
||||
trailers = trailers,
|
||||
trailerOnClick = trailerOnClick,
|
||||
|
|
|
|||
|
|
@ -72,6 +72,10 @@ class DiscoverSeriesViewModel
|
|||
val state: StateFlow<DiscoverSeriesState> = _state
|
||||
|
||||
val userConfig = seerrServerRepository.current.map { it?.config }
|
||||
val requestEnabled =
|
||||
seerrServerRepository.current
|
||||
.map { it?.requestTvEnabled ?: false }
|
||||
.stateIn(viewModelScope, SharingStarted.Eagerly, false)
|
||||
val request4kEnabled =
|
||||
seerrServerRepository.current
|
||||
.map { it?.request4kTvEnabled ?: false }
|
||||
|
|
@ -105,7 +109,7 @@ class DiscoverSeriesViewModel
|
|||
backdropService.submit(discoveredItem)
|
||||
|
||||
updateSeasonStatus(tv)
|
||||
updateCanCancel()
|
||||
updateAvailability()
|
||||
|
||||
viewModelScope.launchIO {
|
||||
val rating =
|
||||
|
|
@ -185,6 +189,7 @@ class DiscoverSeriesViewModel
|
|||
tv: TvDetails,
|
||||
is4k: Boolean,
|
||||
) {
|
||||
Timber.v("updateSeasonStatus: id=%s, is4k=%s", tv.id, is4k)
|
||||
val currentUserId = seerrServerRepository.currentUserId.first()
|
||||
val seasonStatus = mutableMapOf<Int, RequestStatus>()
|
||||
val seasonAvailability = mutableMapOf<Int, SeerrAvailability>()
|
||||
|
|
@ -225,7 +230,10 @@ class DiscoverSeriesViewModel
|
|||
seasonStatus[season.seasonNumber] = new
|
||||
}
|
||||
editable[season.seasonNumber] =
|
||||
currentUserId == req.requestedBy?.id && req.status == RequestStatus.PENDING.status
|
||||
currentUserId == req.requestedBy?.id && (
|
||||
req.status == RequestStatus.PENDING.status ||
|
||||
req.status == RequestStatus.DECLINED.status
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -294,7 +302,7 @@ class DiscoverSeriesViewModel
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun updateCanCancel() {
|
||||
private suspend fun updateAvailability() {
|
||||
val user = userConfig.firstOrNull()
|
||||
val canCancel =
|
||||
canUserCancelRequest(
|
||||
|
|
@ -303,7 +311,32 @@ class DiscoverSeriesViewModel
|
|||
?.mediaInfo
|
||||
?.requests,
|
||||
)
|
||||
_state.update { it.copy(canCancelRequest = canCancel) }
|
||||
val status =
|
||||
state.value.tvSeries.successValue?.let { tv ->
|
||||
val overallStatus = SeerrAvailability.from(tv.mediaInfo?.status)
|
||||
if (overallStatus == SeerrAvailability.UNKNOWN) {
|
||||
val userRequests =
|
||||
tv.mediaInfo
|
||||
?.requests
|
||||
?.filter { it.requestedBy?.id == user?.id }
|
||||
.orEmpty()
|
||||
if (userRequests.any { RequestStatus.from(it.status) == RequestStatus.PENDING }) {
|
||||
SeerrAvailability.PENDING
|
||||
} else if (userRequests.any { RequestStatus.from(it.status) == RequestStatus.APPROVED }) {
|
||||
SeerrAvailability.PROCESSING
|
||||
} else {
|
||||
SeerrAvailability.UNKNOWN
|
||||
}
|
||||
} else {
|
||||
overallStatus
|
||||
}
|
||||
}
|
||||
_state.update {
|
||||
it.copy(
|
||||
canCancelRequest = canCancel,
|
||||
availability = status ?: SeerrAvailability.UNKNOWN,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun requestOnClick() {
|
||||
|
|
@ -386,7 +419,7 @@ class DiscoverSeriesViewModel
|
|||
|
||||
fetchAndSetItem().await()?.let {
|
||||
updateSeasonStatus(it)
|
||||
updateCanCancel()
|
||||
updateAvailability()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -407,7 +440,7 @@ class DiscoverSeriesViewModel
|
|||
|
||||
fetchAndSetItem().await()?.let {
|
||||
updateSeasonStatus(it)
|
||||
updateCanCancel()
|
||||
updateAvailability()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -426,4 +459,5 @@ data class DiscoverSeriesState(
|
|||
val canCancelRequest: Boolean = false,
|
||||
val profileLoading: LoadingState = LoadingState.Pending,
|
||||
val requestData: SeerrRequestData = SeerrRequestData(),
|
||||
val availability: SeerrAvailability = SeerrAvailability.UNKNOWN,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import com.github.damontecres.wholphin.util.LoadingState
|
|||
fun RequestMovieDialog(
|
||||
loading: LoadingState,
|
||||
data: SeerrRequestData,
|
||||
requestEnabled: Boolean,
|
||||
request4kEnabled: Boolean,
|
||||
movie: MovieDetails,
|
||||
onSubmit: (MovieRequest) -> Unit,
|
||||
|
|
@ -54,10 +55,11 @@ fun RequestMovieDialog(
|
|||
RequestMovie(
|
||||
loading = loading,
|
||||
data = data,
|
||||
requestEnabled = requestEnabled,
|
||||
request4kEnabled = request4kEnabled,
|
||||
movie = movie,
|
||||
onSubmit = onSubmit,
|
||||
modifier = Modifier.padding(16.dp),
|
||||
modifier = Modifier.padding(24.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -66,6 +68,7 @@ fun RequestMovieDialog(
|
|||
fun RequestMovie(
|
||||
loading: LoadingState,
|
||||
data: SeerrRequestData,
|
||||
requestEnabled: Boolean,
|
||||
request4kEnabled: Boolean,
|
||||
movie: MovieDetails,
|
||||
onSubmit: (MovieRequest) -> Unit,
|
||||
|
|
@ -90,7 +93,8 @@ fun RequestMovie(
|
|||
if (data.profiles.isEmpty() && data.rootFolders.isEmpty() &&
|
||||
data.profiles4k.isEmpty() && data.rootFolders4k.isEmpty()
|
||||
) {
|
||||
if (request4kEnabled) {
|
||||
// Both enabled, show choice
|
||||
if (requestEnabled && request4kEnabled) {
|
||||
val items =
|
||||
remember {
|
||||
listOf(
|
||||
|
|
@ -118,12 +122,13 @@ fun RequestMovie(
|
|||
)
|
||||
} else {
|
||||
LaunchedEffect(Unit) {
|
||||
onSubmit.invoke(MovieRequest(data, movie.id, false, null, null))
|
||||
onSubmit.invoke(MovieRequest(data, movie.id, request4kEnabled, null, null))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
RequestMovieWithOptions(
|
||||
data = data,
|
||||
requestEnabled = requestEnabled,
|
||||
request4kEnabled = request4kEnabled,
|
||||
movie = movie,
|
||||
onSubmit = onSubmit,
|
||||
|
|
@ -137,12 +142,13 @@ fun RequestMovie(
|
|||
@Composable
|
||||
private fun RequestMovieWithOptions(
|
||||
data: SeerrRequestData,
|
||||
requestEnabled: Boolean,
|
||||
request4kEnabled: Boolean,
|
||||
movie: MovieDetails,
|
||||
onSubmit: (MovieRequest) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var is4k by remember { mutableStateOf(request4kEnabled) }
|
||||
var is4k by remember { mutableStateOf(!requestEnabled) }
|
||||
var profile by remember(is4k) {
|
||||
mutableStateOf(
|
||||
if (is4k) {
|
||||
|
|
@ -185,7 +191,7 @@ private fun RequestMovieWithOptions(
|
|||
.fillMaxWidth()
|
||||
.padding(bottom = 8.dp),
|
||||
) {
|
||||
if (request4kEnabled) {
|
||||
if (requestEnabled && request4kEnabled) {
|
||||
ClickSwitch(
|
||||
label = stringResource(R.string.request_4k),
|
||||
checked = is4k,
|
||||
|
|
@ -377,7 +383,7 @@ fun MovieRequestPreview() {
|
|||
),
|
||||
profiles4k =
|
||||
listOf(
|
||||
SeerrProfile(1, "HD", true),
|
||||
SeerrProfile(1, "4K", true),
|
||||
SeerrProfile(2, "Ultra HD", false),
|
||||
),
|
||||
rootFolders4k =
|
||||
|
|
@ -385,7 +391,8 @@ fun MovieRequestPreview() {
|
|||
SeerrRootFolder(1, "/movies", "400GB", true),
|
||||
),
|
||||
),
|
||||
request4kEnabled = false,
|
||||
requestEnabled = true,
|
||||
request4kEnabled = true,
|
||||
movie = MovieDetails(id = 1, title = "Movie Name", releaseDate = "2026"),
|
||||
onSubmit = {},
|
||||
modifier =
|
||||
|
|
|
|||
|
|
@ -65,11 +65,12 @@ fun RequestSeasons(
|
|||
seasons: List<RequestSeason>,
|
||||
seasons4k: List<RequestSeason>,
|
||||
data: SeerrRequestData,
|
||||
requestEnabled: Boolean,
|
||||
request4kEnabled: Boolean,
|
||||
onSubmit: (TvRequest) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var is4k by remember { mutableStateOf(request4kEnabled) }
|
||||
var is4k by remember { mutableStateOf(!requestEnabled) }
|
||||
val seasons = remember(is4k, seasons, seasons4k) { if (is4k) seasons4k else seasons }
|
||||
|
||||
val allSeasonNumbers =
|
||||
|
|
@ -161,7 +162,7 @@ fun RequestSeasons(
|
|||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp),
|
||||
) {
|
||||
if (request4kEnabled) {
|
||||
if (requestEnabled && request4kEnabled) {
|
||||
ClickSwitch(
|
||||
label = stringResource(R.string.request_4k),
|
||||
checked = is4k,
|
||||
|
|
@ -391,6 +392,7 @@ fun RequestSeasonsDialog(
|
|||
data: SeerrRequestData,
|
||||
seasons: List<RequestSeason>,
|
||||
seasons4k: List<RequestSeason>,
|
||||
requestEnabled: Boolean,
|
||||
request4kEnabled: Boolean,
|
||||
onSubmit: (TvRequest) -> Unit,
|
||||
onDismissRequest: () -> Unit,
|
||||
|
|
@ -418,6 +420,7 @@ fun RequestSeasonsDialog(
|
|||
data = data,
|
||||
seasons = seasons,
|
||||
seasons4k = seasons4k,
|
||||
requestEnabled = requestEnabled,
|
||||
request4kEnabled = request4kEnabled,
|
||||
onSubmit = onSubmit,
|
||||
modifier =
|
||||
|
|
@ -475,6 +478,7 @@ fun RequestSeasonsPreview() {
|
|||
SeerrRootFolder(1, "/tv", "400GB", true),
|
||||
),
|
||||
),
|
||||
requestEnabled = true,
|
||||
request4kEnabled = true,
|
||||
onSubmit = { },
|
||||
modifier = Modifier.width(400.dp),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
package com.github.damontecres.wholphin.ui.preferences
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.data.model.SeerrAuthMethod
|
||||
import com.github.damontecres.wholphin.data.model.SeerrServer
|
||||
import com.github.damontecres.wholphin.data.model.SeerrUser
|
||||
import com.github.damontecres.wholphin.preferences.AppPreference
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
|
@ -45,3 +52,40 @@ enum class PreferenceScreenOption {
|
|||
fun fromString(name: String?) = entries.firstOrNull { it.name == name } ?: BASIC
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberSeerrDialogBody(
|
||||
user: SeerrUser,
|
||||
server: SeerrServer,
|
||||
ex: Exception? = null,
|
||||
): String {
|
||||
val username =
|
||||
when (user.authMethod) {
|
||||
SeerrAuthMethod.LOCAL,
|
||||
SeerrAuthMethod.JELLYFIN,
|
||||
-> user.username ?: stringResource(R.string.unknown)
|
||||
|
||||
SeerrAuthMethod.API_KEY -> stringResource(R.string.api_key)
|
||||
}
|
||||
val method =
|
||||
when (user.authMethod) {
|
||||
SeerrAuthMethod.LOCAL -> R.string.seerr_local_user
|
||||
SeerrAuthMethod.JELLYFIN -> R.string.seerr_jellyfin_user
|
||||
SeerrAuthMethod.API_KEY -> 0
|
||||
}.let {
|
||||
if (it != 0) " (${stringResource(it)})" else ""
|
||||
}
|
||||
|
||||
val body =
|
||||
"""
|
||||
${stringResource(R.string.url)}: ${server.url}
|
||||
${stringResource(R.string.username)}: $username$method
|
||||
""".trimIndent() +
|
||||
if (ex != null) {
|
||||
"\n\n${stringResource(R.string.voice_error_server)}: ${ex.localizedMessage}"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
|
||||
return remember { body }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ import androidx.tv.material3.surfaceColorAtElevation
|
|||
import coil3.SingletonImageLoader
|
||||
import coil3.imageLoader
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.data.model.SeerrServer
|
||||
import com.github.damontecres.wholphin.data.model.SeerrUser
|
||||
import com.github.damontecres.wholphin.preferences.AppPreference
|
||||
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||
import com.github.damontecres.wholphin.preferences.AppSwitchPreference
|
||||
|
|
@ -62,6 +64,7 @@ import com.github.damontecres.wholphin.preferences.experimentalPreferences
|
|||
import com.github.damontecres.wholphin.preferences.lazyListWrapScrolling
|
||||
import com.github.damontecres.wholphin.preferences.screensaverPreferences
|
||||
import com.github.damontecres.wholphin.preferences.updatePlaybackPreferences
|
||||
import com.github.damontecres.wholphin.services.CurrentSeerr
|
||||
import com.github.damontecres.wholphin.services.Release
|
||||
import com.github.damontecres.wholphin.services.SeerrConnectionStatus
|
||||
import com.github.damontecres.wholphin.services.UpdateChecker
|
||||
|
|
@ -478,7 +481,8 @@ fun PreferencesContent(
|
|||
when (val conn = seerrConnection) {
|
||||
is SeerrConnectionStatus.Error -> {
|
||||
SeerrDialogMode.Error(
|
||||
conn.server.url,
|
||||
conn.server,
|
||||
conn.user,
|
||||
conn.ex,
|
||||
)
|
||||
}
|
||||
|
|
@ -489,7 +493,7 @@ fun PreferencesContent(
|
|||
|
||||
is SeerrConnectionStatus.Success -> {
|
||||
SeerrDialogMode.Remove(
|
||||
conn.current.server.url,
|
||||
conn.current,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -701,14 +705,16 @@ fun PreferencesContent(
|
|||
}
|
||||
when (val mode = seerrDialogMode) {
|
||||
is SeerrDialogMode.Remove -> {
|
||||
val body = rememberSeerrDialogBody(mode.current.user, mode.current.server)
|
||||
ConfirmDialog(
|
||||
title = stringResource(R.string.remove_seerr_server),
|
||||
body = mode.serverUrl,
|
||||
body = body,
|
||||
onCancel = { seerrDialogMode = SeerrDialogMode.None },
|
||||
onConfirm = {
|
||||
seerrVm.removeServer()
|
||||
seerrDialogMode = SeerrDialogMode.None
|
||||
},
|
||||
elevation = 3.dp,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -732,15 +738,7 @@ fun PreferencesContent(
|
|||
}
|
||||
|
||||
is SeerrDialogMode.Error -> {
|
||||
val errorStr = stringResource(R.string.voice_error_server)
|
||||
val body =
|
||||
remember(mode) {
|
||||
"""
|
||||
${mode.serverUrl}
|
||||
|
||||
$errorStr: ${mode.ex.localizedMessage}
|
||||
""".trimIndent()
|
||||
}
|
||||
val body = rememberSeerrDialogBody(mode.user, mode.server, mode.ex)
|
||||
ConfirmDialog(
|
||||
title = stringResource(R.string.remove_seerr_server),
|
||||
body = body,
|
||||
|
|
@ -750,6 +748,7 @@ fun PreferencesContent(
|
|||
seerrDialogMode = SeerrDialogMode.None
|
||||
},
|
||||
bodyColor = MaterialTheme.colorScheme.error,
|
||||
elevation = 3.dp,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -865,11 +864,12 @@ private sealed interface SeerrDialogMode {
|
|||
data object Add : SeerrDialogMode
|
||||
|
||||
data class Remove(
|
||||
val serverUrl: String,
|
||||
val current: CurrentSeerr,
|
||||
) : SeerrDialogMode
|
||||
|
||||
data class Error(
|
||||
val serverUrl: String,
|
||||
val server: SeerrServer,
|
||||
val user: SeerrUser,
|
||||
val ex: Exception,
|
||||
) : SeerrDialogMode
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue