mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-23 10:12:06 +00:00
ref(cloud): ui adjustments
This commit is contained in:
parent
914f4147e9
commit
996fd7f949
7 changed files with 160 additions and 67 deletions
|
|
@ -84,30 +84,7 @@ private class TorboxDebridProviderApi(
|
||||||
val normalized = deviceCode.trim()
|
val normalized = deviceCode.trim()
|
||||||
if (normalized.isBlank()) return DebridDeviceAuthorizationTokenResult.Failed(null)
|
if (normalized.isBlank()) return DebridDeviceAuthorizationTokenResult.Failed(null)
|
||||||
val response = TorboxApiClient.redeemDeviceAuthorization(deviceCode = normalized)
|
val response = TorboxApiClient.redeemDeviceAuthorization(deviceCode = normalized)
|
||||||
val envelope = response.body
|
return torboxDeviceAuthorizationTokenResult(response)
|
||||||
val accessToken = envelope
|
|
||||||
?.takeIf { response.isSuccessful && it.success != false }
|
|
||||||
?.data
|
|
||||||
?.accessToken
|
|
||||||
?.takeIf { it.isNotBlank() }
|
|
||||||
if (accessToken != null) {
|
|
||||||
return DebridDeviceAuthorizationTokenResult.Authorized(accessToken)
|
|
||||||
}
|
|
||||||
val message = listOfNotNull(envelope?.error, envelope?.detail, response.rawBody)
|
|
||||||
.joinToString(" ")
|
|
||||||
.lowercase()
|
|
||||||
return when {
|
|
||||||
message.contains("pending") || message.contains("not authorized") ->
|
|
||||||
DebridDeviceAuthorizationTokenResult.Pending
|
|
||||||
message.contains("expired") ->
|
|
||||||
DebridDeviceAuthorizationTokenResult.Expired
|
|
||||||
response.status == 404 || response.status == 409 || response.status == 425 ->
|
|
||||||
DebridDeviceAuthorizationTokenResult.Pending
|
|
||||||
response.status == 410 ->
|
|
||||||
DebridDeviceAuthorizationTokenResult.Expired
|
|
||||||
else ->
|
|
||||||
DebridDeviceAuthorizationTokenResult.Failed(envelope?.detail ?: envelope?.error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun resolveClientStream(
|
override suspend fun resolveClientStream(
|
||||||
|
|
@ -320,6 +297,39 @@ internal fun premiumizeDeviceAuthorizationFromResponse(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun torboxDeviceAuthorizationTokenResult(
|
||||||
|
response: DebridApiResponse<TorboxEnvelopeDto<TorboxDeviceTokenDto>>,
|
||||||
|
): DebridDeviceAuthorizationTokenResult {
|
||||||
|
val envelope = response.body
|
||||||
|
val accessToken = envelope
|
||||||
|
?.takeIf { response.isSuccessful && it.success != false }
|
||||||
|
?.data
|
||||||
|
?.accessToken
|
||||||
|
?.takeIf { it.isNotBlank() }
|
||||||
|
if (accessToken != null) {
|
||||||
|
return DebridDeviceAuthorizationTokenResult.Authorized(accessToken)
|
||||||
|
}
|
||||||
|
val message = listOfNotNull(envelope?.error, envelope?.detail, response.rawBody)
|
||||||
|
.joinToString(" ")
|
||||||
|
.lowercase()
|
||||||
|
return when {
|
||||||
|
message.contains("pending") ||
|
||||||
|
message.contains("not authorized") ||
|
||||||
|
message.contains("not been used") ||
|
||||||
|
message.contains("not used yet") ||
|
||||||
|
message.contains("scan the code") ->
|
||||||
|
DebridDeviceAuthorizationTokenResult.Pending
|
||||||
|
message.contains("expired") ->
|
||||||
|
DebridDeviceAuthorizationTokenResult.Expired
|
||||||
|
response.status == 404 || response.status == 409 || response.status == 425 ->
|
||||||
|
DebridDeviceAuthorizationTokenResult.Pending
|
||||||
|
response.status == 410 ->
|
||||||
|
DebridDeviceAuthorizationTokenResult.Expired
|
||||||
|
else ->
|
||||||
|
DebridDeviceAuthorizationTokenResult.Failed(envelope?.detail ?: envelope?.error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal fun premiumizeDeviceAuthorizationTokenResult(
|
internal fun premiumizeDeviceAuthorizationTokenResult(
|
||||||
response: DebridApiResponse<PremiumizeDeviceTokenDto>,
|
response: DebridApiResponse<PremiumizeDeviceTokenDto>,
|
||||||
): DebridDeviceAuthorizationTokenResult {
|
): DebridDeviceAuthorizationTokenResult {
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,10 @@ fun HomeScreen(
|
||||||
|
|
||||||
val addonsUiState by AddonRepository.uiState.collectAsStateWithLifecycle()
|
val addonsUiState by AddonRepository.uiState.collectAsStateWithLifecycle()
|
||||||
val homeUiState by HomeRepository.uiState.collectAsStateWithLifecycle()
|
val homeUiState by HomeRepository.uiState.collectAsStateWithLifecycle()
|
||||||
val homeSettingsUiState by HomeCatalogSettingsRepository.uiState.collectAsStateWithLifecycle()
|
val homeSettingsUiState by remember {
|
||||||
|
HomeCatalogSettingsRepository.snapshot()
|
||||||
|
HomeCatalogSettingsRepository.uiState
|
||||||
|
}.collectAsStateWithLifecycle()
|
||||||
val homeListState = rememberLazyListState()
|
val homeListState = rememberLazyListState()
|
||||||
val collections by CollectionRepository.collections.collectAsStateWithLifecycle()
|
val collections by CollectionRepository.collections.collectAsStateWithLifecycle()
|
||||||
val continueWatchingPreferences by ContinueWatchingPreferencesRepository.uiState.collectAsStateWithLifecycle()
|
val continueWatchingPreferences by ContinueWatchingPreferencesRepository.uiState.collectAsStateWithLifecycle()
|
||||||
|
|
@ -612,7 +615,10 @@ fun HomeScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
items(3) {
|
items(3) {
|
||||||
HomeSkeletonRow(modifier = Modifier.padding(horizontal = 16.dp))
|
HomeSkeletonRow(
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
|
showHeaderAccent = !homeSettingsUiState.hideCatalogUnderline,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,10 @@ fun HomeSkeletonHero(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeSkeletonRow(modifier: Modifier = Modifier) {
|
fun HomeSkeletonRow(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
showHeaderAccent: Boolean = true,
|
||||||
|
) {
|
||||||
val brush = rememberHomeSkeletonBrush()
|
val brush = rememberHomeSkeletonBrush()
|
||||||
val posterCardStyle = rememberPosterCardStyleUiState()
|
val posterCardStyle = rememberPosterCardStyleUiState()
|
||||||
val skeletonWidth = if (posterCardStyle.catalogLandscapeModeEnabled) {
|
val skeletonWidth = if (posterCardStyle.catalogLandscapeModeEnabled) {
|
||||||
|
|
@ -207,15 +210,17 @@ fun HomeSkeletonRow(modifier: Modifier = Modifier) {
|
||||||
.clip(RoundedCornerShape(6.dp))
|
.clip(RoundedCornerShape(6.dp))
|
||||||
.background(brush),
|
.background(brush),
|
||||||
)
|
)
|
||||||
// Accent bar
|
if (showHeaderAccent) {
|
||||||
Box(
|
// Accent bar
|
||||||
modifier = Modifier
|
Box(
|
||||||
.width(60.dp)
|
modifier = Modifier
|
||||||
.height(4.dp)
|
.width(60.dp)
|
||||||
.clip(RoundedCornerShape(999.dp))
|
.height(4.dp)
|
||||||
.background(brush),
|
.clip(RoundedCornerShape(999.dp))
|
||||||
)
|
.background(brush),
|
||||||
Spacer(modifier = Modifier.height(2.dp))
|
)
|
||||||
|
Spacer(modifier = Modifier.height(2.dp))
|
||||||
|
}
|
||||||
// Poster row
|
// Poster row
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ import com.nuvio.app.features.cloud.CloudLibraryItemType
|
||||||
import com.nuvio.app.features.cloud.CloudLibraryRepository
|
import com.nuvio.app.features.cloud.CloudLibraryRepository
|
||||||
import com.nuvio.app.features.cloud.CloudLibraryUiState
|
import com.nuvio.app.features.cloud.CloudLibraryUiState
|
||||||
import com.nuvio.app.features.debrid.DebridSettingsRepository
|
import com.nuvio.app.features.debrid.DebridSettingsRepository
|
||||||
|
import com.nuvio.app.features.home.HomeCatalogSettingsRepository
|
||||||
import com.nuvio.app.features.home.components.HomeEmptyStateCard
|
import com.nuvio.app.features.home.components.HomeEmptyStateCard
|
||||||
import com.nuvio.app.features.home.components.HomePosterCard
|
import com.nuvio.app.features.home.components.HomePosterCard
|
||||||
import com.nuvio.app.features.home.components.HomeSkeletonRow
|
import com.nuvio.app.features.home.components.HomeSkeletonRow
|
||||||
|
|
@ -107,6 +108,10 @@ fun LibraryScreen(
|
||||||
WatchedRepository.ensureLoaded()
|
WatchedRepository.ensureLoaded()
|
||||||
WatchedRepository.uiState
|
WatchedRepository.uiState
|
||||||
}.collectAsStateWithLifecycle()
|
}.collectAsStateWithLifecycle()
|
||||||
|
val homeCatalogSettingsUiState by remember {
|
||||||
|
HomeCatalogSettingsRepository.snapshot()
|
||||||
|
HomeCatalogSettingsRepository.uiState
|
||||||
|
}.collectAsStateWithLifecycle()
|
||||||
val networkStatusUiState by NetworkStatusRepository.uiState.collectAsStateWithLifecycle()
|
val networkStatusUiState by NetworkStatusRepository.uiState.collectAsStateWithLifecycle()
|
||||||
var observedOfflineState by remember { mutableStateOf(false) }
|
var observedOfflineState by remember { mutableStateOf(false) }
|
||||||
var sourceModeName by rememberSaveable { mutableStateOf(LibraryViewMode.Saved.name) }
|
var sourceModeName by rememberSaveable { mutableStateOf(LibraryViewMode.Saved.name) }
|
||||||
|
|
@ -230,7 +235,10 @@ fun LibraryScreen(
|
||||||
when {
|
when {
|
||||||
!uiState.isLoaded || (uiState.isLoading && uiState.sections.isEmpty()) -> {
|
!uiState.isLoaded || (uiState.isLoading && uiState.sections.isEmpty()) -> {
|
||||||
items(3) {
|
items(3) {
|
||||||
HomeSkeletonRow(modifier = Modifier.padding(horizontal = 16.dp))
|
HomeSkeletonRow(
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
|
showHeaderAccent = !homeCatalogSettingsUiState.hideCatalogUnderline,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -288,6 +296,7 @@ fun LibraryScreen(
|
||||||
librarySections(
|
librarySections(
|
||||||
sections = uiState.sections,
|
sections = uiState.sections,
|
||||||
watchedKeys = watchedUiState.watchedKeys,
|
watchedKeys = watchedUiState.watchedKeys,
|
||||||
|
showHeaderAccent = !homeCatalogSettingsUiState.hideCatalogUnderline,
|
||||||
onPosterClick = onPosterClick,
|
onPosterClick = onPosterClick,
|
||||||
onSectionViewAllClick = onSectionViewAllClick,
|
onSectionViewAllClick = onSectionViewAllClick,
|
||||||
onPosterLongClick = onPosterLongClick,
|
onPosterLongClick = onPosterLongClick,
|
||||||
|
|
@ -423,7 +432,7 @@ private fun LazyListScope.cloudLibrarySkeletonItems() {
|
||||||
modifier = Modifier.padding(horizontal = 16.dp),
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
items(4) {
|
items(3) {
|
||||||
CloudLibrarySkeletonRow()
|
CloudLibrarySkeletonRow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -856,24 +865,17 @@ private fun CloudLibrarySkeletonToolbar(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val brush = rememberCloudLibrarySkeletonBrush()
|
val brush = rememberCloudLibrarySkeletonBrush()
|
||||||
Column(
|
Row(
|
||||||
modifier = modifier.fillMaxWidth(),
|
modifier = modifier.fillMaxWidth(),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.weight(1f),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
) {
|
) {
|
||||||
CloudSkeletonBlock(brush = brush, width = 92.dp, height = 34.dp, cornerRadius = 12.dp)
|
CloudSkeletonBlock(brush = brush, width = 112.dp, height = 36.dp, cornerRadius = 12.dp)
|
||||||
CloudSkeletonBlock(brush = brush, width = 78.dp, height = 34.dp, cornerRadius = 12.dp)
|
CloudSkeletonBlock(brush = brush, width = 92.dp, height = 36.dp, cornerRadius = 12.dp)
|
||||||
CloudSkeletonBlock(
|
|
||||||
brush = brush,
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
height = 34.dp,
|
|
||||||
cornerRadius = 12.dp,
|
|
||||||
)
|
|
||||||
CloudSkeletonBlock(brush = brush, width = 40.dp, height = 40.dp, cornerRadius = 20.dp)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -903,35 +905,29 @@ private fun CloudLibrarySkeletonRow(
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
verticalArrangement = Arrangement.spacedBy(7.dp),
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
) {
|
) {
|
||||||
CloudSkeletonBlock(
|
CloudSkeletonBlock(
|
||||||
brush = brush,
|
brush = brush,
|
||||||
modifier = Modifier.fillMaxWidth(0.74f),
|
modifier = Modifier.fillMaxWidth(0.74f),
|
||||||
height = 17.dp,
|
height = 18.dp,
|
||||||
cornerRadius = 6.dp,
|
cornerRadius = 6.dp,
|
||||||
)
|
)
|
||||||
CloudSkeletonBlock(
|
CloudSkeletonBlock(
|
||||||
brush = brush,
|
brush = brush,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(0.9f),
|
||||||
height = 12.dp,
|
height = 14.dp,
|
||||||
cornerRadius = 6.dp,
|
cornerRadius = 6.dp,
|
||||||
)
|
)
|
||||||
CloudSkeletonBlock(
|
CloudSkeletonBlock(
|
||||||
brush = brush,
|
brush = brush,
|
||||||
modifier = Modifier.fillMaxWidth(0.58f),
|
modifier = Modifier.fillMaxWidth(0.52f),
|
||||||
height = 12.dp,
|
height = 12.dp,
|
||||||
cornerRadius = 6.dp,
|
cornerRadius = 6.dp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
CloudSkeletonBlock(brush = brush, width = 32.dp, height = 32.dp, cornerRadius = 16.dp)
|
CloudSkeletonBlock(brush = brush, width = 48.dp, height = 48.dp, cornerRadius = 24.dp)
|
||||||
}
|
}
|
||||||
CloudSkeletonBlock(
|
|
||||||
brush = brush,
|
|
||||||
modifier = Modifier.fillMaxWidth(0.44f),
|
|
||||||
height = 4.dp,
|
|
||||||
cornerRadius = 999.dp,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -987,6 +983,7 @@ private enum class LibraryViewMode {
|
||||||
private fun LazyListScope.librarySections(
|
private fun LazyListScope.librarySections(
|
||||||
sections: List<LibrarySection>,
|
sections: List<LibrarySection>,
|
||||||
watchedKeys: Set<String>,
|
watchedKeys: Set<String>,
|
||||||
|
showHeaderAccent: Boolean,
|
||||||
onPosterClick: ((LibraryItem) -> Unit)?,
|
onPosterClick: ((LibraryItem) -> Unit)?,
|
||||||
onSectionViewAllClick: ((LibrarySection) -> Unit)?,
|
onSectionViewAllClick: ((LibrarySection) -> Unit)?,
|
||||||
onPosterLongClick: ((LibraryItem, LibrarySection) -> Unit)?,
|
onPosterLongClick: ((LibraryItem, LibrarySection) -> Unit)?,
|
||||||
|
|
@ -1001,6 +998,7 @@ private fun LazyListScope.librarySections(
|
||||||
entries = previewItems,
|
entries = previewItems,
|
||||||
headerHorizontalPadding = 16.dp,
|
headerHorizontalPadding = 16.dp,
|
||||||
rowContentPadding = PaddingValues(horizontal = 16.dp),
|
rowContentPadding = PaddingValues(horizontal = 16.dp),
|
||||||
|
showHeaderAccent = showHeaderAccent,
|
||||||
onViewAllClick = if (section.items.size > LIBRARY_SECTION_PREVIEW_LIMIT) {
|
onViewAllClick = if (section.items.size > LIBRARY_SECTION_PREVIEW_LIMIT) {
|
||||||
onSectionViewAllClick?.let { { it(section) } }
|
onSectionViewAllClick?.let { { it(section) } }
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,10 @@ fun SearchScreen(
|
||||||
val addonsUiState by AddonRepository.uiState.collectAsStateWithLifecycle()
|
val addonsUiState by AddonRepository.uiState.collectAsStateWithLifecycle()
|
||||||
val uiState by SearchRepository.uiState.collectAsStateWithLifecycle()
|
val uiState by SearchRepository.uiState.collectAsStateWithLifecycle()
|
||||||
val discoverUiState by SearchRepository.discoverUiState.collectAsStateWithLifecycle()
|
val discoverUiState by SearchRepository.discoverUiState.collectAsStateWithLifecycle()
|
||||||
val homeCatalogSettingsUiState by HomeCatalogSettingsRepository.uiState.collectAsStateWithLifecycle()
|
val homeCatalogSettingsUiState by remember {
|
||||||
|
HomeCatalogSettingsRepository.snapshot()
|
||||||
|
HomeCatalogSettingsRepository.uiState
|
||||||
|
}.collectAsStateWithLifecycle()
|
||||||
val recentSearches by SearchHistoryRepository.uiState.collectAsStateWithLifecycle()
|
val recentSearches by SearchHistoryRepository.uiState.collectAsStateWithLifecycle()
|
||||||
val watchedUiState by WatchedRepository.uiState.collectAsStateWithLifecycle()
|
val watchedUiState by WatchedRepository.uiState.collectAsStateWithLifecycle()
|
||||||
val networkStatusUiState by NetworkStatusRepository.uiState.collectAsStateWithLifecycle()
|
val networkStatusUiState by NetworkStatusRepository.uiState.collectAsStateWithLifecycle()
|
||||||
|
|
@ -305,13 +308,19 @@ fun SearchScreen(
|
||||||
when {
|
when {
|
||||||
isWaitingForSearch -> {
|
isWaitingForSearch -> {
|
||||||
items(2) {
|
items(2) {
|
||||||
HomeSkeletonRow(modifier = Modifier.padding(horizontal = homeSectionPadding))
|
HomeSkeletonRow(
|
||||||
|
modifier = Modifier.padding(horizontal = homeSectionPadding),
|
||||||
|
showHeaderAccent = !homeCatalogSettingsUiState.hideCatalogUnderline,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uiState.isLoading && uiState.sections.isEmpty() -> {
|
uiState.isLoading && uiState.sections.isEmpty() -> {
|
||||||
items(2) {
|
items(2) {
|
||||||
HomeSkeletonRow(modifier = Modifier.padding(horizontal = homeSectionPadding))
|
HomeSkeletonRow(
|
||||||
|
modifier = Modifier.padding(horizontal = homeSectionPadding),
|
||||||
|
showHeaderAccent = !homeCatalogSettingsUiState.hideCatalogUnderline,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -351,7 +360,10 @@ fun SearchScreen(
|
||||||
}
|
}
|
||||||
if (uiState.isLoading) {
|
if (uiState.isLoading) {
|
||||||
item(key = "search_loading_more") {
|
item(key = "search_loading_more") {
|
||||||
HomeSkeletonRow(modifier = Modifier.padding(horizontal = homeSectionPadding))
|
HomeSkeletonRow(
|
||||||
|
modifier = Modifier.padding(horizontal = homeSectionPadding),
|
||||||
|
showHeaderAccent = !homeCatalogSettingsUiState.hideCatalogUnderline,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,12 +123,15 @@ import nuvio.composeapp.generated.resources.settings_debrid_section_providers
|
||||||
import nuvio.composeapp.generated.resources.settings_debrid_section_title
|
import nuvio.composeapp.generated.resources.settings_debrid_section_title
|
||||||
import org.jetbrains.compose.resources.stringResource
|
import org.jetbrains.compose.resources.stringResource
|
||||||
|
|
||||||
|
private const val CLOUD_SERVICES_FAQ_URL = "https://nuvioapp.space/faq#common-cloud-library-and-cloud-services"
|
||||||
|
|
||||||
internal fun LazyListScope.debridSettingsContent(
|
internal fun LazyListScope.debridSettingsContent(
|
||||||
isTablet: Boolean,
|
isTablet: Boolean,
|
||||||
settings: DebridSettings,
|
settings: DebridSettings,
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
var showResolverProviderDialog by rememberSaveable { mutableStateOf(false) }
|
var showResolverProviderDialog by rememberSaveable { mutableStateOf(false) }
|
||||||
|
val uriHandler = LocalUriHandler.current
|
||||||
val resolverProviders = settings.resolverServices.map { it.provider }
|
val resolverProviders = settings.resolverServices.map { it.provider }
|
||||||
val activeResolverProvider = settings.activeResolverCredential?.provider
|
val activeResolverProvider = settings.activeResolverCredential?.provider
|
||||||
SettingsSection(
|
SettingsSection(
|
||||||
|
|
@ -141,6 +144,15 @@ internal fun LazyListScope.debridSettingsContent(
|
||||||
text = stringResource(Res.string.settings_debrid_experimental_notice),
|
text = stringResource(Res.string.settings_debrid_experimental_notice),
|
||||||
)
|
)
|
||||||
SettingsGroupDivider(isTablet = isTablet)
|
SettingsGroupDivider(isTablet = isTablet)
|
||||||
|
DebridPreferenceRow(
|
||||||
|
isTablet = isTablet,
|
||||||
|
title = "Learn more",
|
||||||
|
description = "Cloud Library, connected accounts, and playable-link preparation.",
|
||||||
|
value = "Open",
|
||||||
|
enabled = true,
|
||||||
|
onClick = { runCatching { uriHandler.openUri(CLOUD_SERVICES_FAQ_URL) } },
|
||||||
|
)
|
||||||
|
SettingsGroupDivider(isTablet = isTablet)
|
||||||
SettingsSwitchRow(
|
SettingsSwitchRow(
|
||||||
title = stringResource(Res.string.settings_debrid_cloud_library),
|
title = stringResource(Res.string.settings_debrid_cloud_library),
|
||||||
description = stringResource(Res.string.settings_debrid_cloud_library_description),
|
description = stringResource(Res.string.settings_debrid_cloud_library_description),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
package com.nuvio.app.features.debrid
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class TorboxDeviceAuthTest {
|
||||||
|
@Test
|
||||||
|
fun `maps unused device code response to pending`() {
|
||||||
|
val response = DebridApiResponse(
|
||||||
|
status = 400,
|
||||||
|
body = TorboxEnvelopeDto<TorboxDeviceTokenDto>(
|
||||||
|
success = false,
|
||||||
|
detail = "This device code has not been used yet. Please wait for the user to scan the code.",
|
||||||
|
),
|
||||||
|
rawBody = "",
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
DebridDeviceAuthorizationTokenResult.Pending,
|
||||||
|
torboxDeviceAuthorizationTokenResult(response),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `maps authorized and expired Torbox device states`() {
|
||||||
|
assertTrue(
|
||||||
|
torboxDeviceAuthorizationTokenResult(
|
||||||
|
DebridApiResponse(
|
||||||
|
status = 200,
|
||||||
|
body = TorboxEnvelopeDto(
|
||||||
|
success = true,
|
||||||
|
data = TorboxDeviceTokenDto(accessToken = "tb-token", tokenType = "Bearer"),
|
||||||
|
),
|
||||||
|
rawBody = "",
|
||||||
|
),
|
||||||
|
) is DebridDeviceAuthorizationTokenResult.Authorized,
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
DebridDeviceAuthorizationTokenResult.Expired,
|
||||||
|
torboxDeviceAuthorizationTokenResult(
|
||||||
|
DebridApiResponse(
|
||||||
|
status = 410,
|
||||||
|
body = TorboxEnvelopeDto(success = false, detail = "Device code expired."),
|
||||||
|
rawBody = "",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue