Skip to content

Commit

Permalink
feat(WatchView): 🎉 add server selector
Browse files Browse the repository at this point in the history
  • Loading branch information
TobyBridle committed Dec 11, 2023
1 parent 0bfada1 commit 757b5c6
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,32 @@ package com.chouten.app.presentation.ui.screens.watch
import android.content.Intent
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.BottomSheetScaffold
import androidx.compose.material3.BottomSheetScaffoldState
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ListItem
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SheetState
import androidx.compose.material3.SheetValue
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
Expand All @@ -18,16 +40,23 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.viewModelScope
import com.chouten.app.R
import com.chouten.app.common.Navigation
import com.chouten.app.common.UiText
import com.chouten.app.domain.model.SnackbarModel
import com.chouten.app.presentation.ui.screens.watch.WatchViewModel.Companion.STATUS
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
Expand All @@ -48,6 +77,7 @@ data class WatchBundle(
val mediaTitle: String,
)

@OptIn(ExperimentalMaterial3Api::class)
@Composable
@Destination(
route = Navigation.WatchRoute
Expand All @@ -71,7 +101,7 @@ fun WatchView(
STATUS, WatchViewModelState.DEFAULT
).collectAsState()

val selectedServer by rememberSaveable { mutableIntStateOf(0) }
var selectedServer by rememberSaveable { mutableIntStateOf(0) }
val selectedSource by rememberSaveable { mutableIntStateOf(0) }

var servers by remember { mutableStateOf<List<WatchResult.ServerData>?>(null) }
Expand Down Expand Up @@ -104,21 +134,15 @@ fun WatchView(
bundle.selectedMediaIndex
)
}
} else {
if (servers == null) {
// We want to use the viewmodel scope here since we don't
// want the server handler to be cancelled when the view is recomposed
watchViewModel.viewModelScope.launch {
context.cacheDir.resolve("${bundle.mediaUuid}_server.json").useLines { lines ->
val text = lines.joinToString("\n")
val result = Json.decodeFromString<List<WatchResult.ServerData>>(text)
servers = result
}
} else if (servers == null) {
// We want to use the viewmodel scope here since we don't
// want the server handler to be cancelled when the view is recomposed
watchViewModel.viewModelScope.launch {
context.cacheDir.resolve("${bundle.mediaUuid}_server.json").useLines { lines ->
val text = lines.joinToString("\n")
val result = Json.decodeFromString<List<WatchResult.ServerData>>(text)
servers = result
}
} else {
val serverUrl =
servers?.getOrNull(selectedServer)?.list?.getOrNull(selectedSource)?.url
watchViewModel.getSource(serverUrl ?: "")
}
}

Expand All @@ -135,13 +159,111 @@ fun WatchView(
Box(
modifier = Modifier
.fillMaxSize()
.navigationBarsPadding()
.background(Color.Black)
) {
if (!status.isSourceSet) {
CircularProgressIndicator(
modifier = Modifier.align(Alignment.Center)
modifier = Modifier
.align(Alignment.Center)
)
}
AnimatedVisibility(visible = servers != null, enter = fadeIn()) {
val sourceLambda: suspend () -> Unit = {
val serverUrl =
servers?.getOrNull(selectedServer)?.list?.getOrNull(
selectedSource
)?.url
watchViewModel.getSource(serverUrl ?: "")
}
if (servers?.size == 1) {
runBlocking {
sourceLambda()
}
return@AnimatedVisibility
}
BottomSheetScaffold(
scaffoldState = BottomSheetScaffoldState(
bottomSheetState = SheetState(
skipPartiallyExpanded = true,
initialValue = SheetValue.Expanded
),
snackbarHostState = SnackbarHostState()
),
modifier = Modifier.fillMaxSize(),
sheetPeekHeight = 150.dp,
sheetContent = {
servers?.let {
if (it.isEmpty()) return@let null

LazyColumn(
modifier = Modifier
.fillMaxWidth()
.padding(20.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
itemsIndexed(it) { index, serverData ->
ListItem(
colors = ListItemDefaults.colors(
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
12.dp
)
),
modifier = Modifier
.clip(MaterialTheme.shapes.medium)
.clickable {
runBlocking { sourceLambda() }
},
headlineContent = {
Text(
serverData.title.trim(),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
},
supportingContent = {
Text(
"${serverData.list.firstOrNull()?.name?.trim()}",
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
},
leadingContent = {
Text(
"#${index + 1}",
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
},
trailingContent = {
Text("${serverData.list.size} Sources")
}
)
}
}
} ?: run {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
"(×﹏×)",
fontSize = MaterialTheme.typography.headlineLarge.fontSize
)
Text(
UiText.StringRes((R.string.no_servers_found))
.string(),
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
modifier = Modifier.padding(horizontal = 16.dp),
textAlign = TextAlign.Center
)
}
}
}
) {}
}
}
}

Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,5 @@
<string name="module_auto_update">Module Auto-Update</string>
<string name="module_autoupdate_error">Could not auto-update %1$s</string>
<string name="module_autoupdate_start">Attempting to auto update %1$s</string>
<string name="no_servers_found">Whoops.. Looks like no servers were found?</string>
</resources>

0 comments on commit 757b5c6

Please sign in to comment.