Skip to content

Commit

Permalink
started activation component
Browse files Browse the repository at this point in the history
  • Loading branch information
DatL4g committed Nov 16, 2023
1 parent 56ba984 commit 8e5ac52
Show file tree
Hide file tree
Showing 23 changed files with 441 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package dev.datlag.burningseries.ui.custom

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.multiplatform.webview.web.WebView
import com.multiplatform.webview.web.WebViewNavigator
import com.multiplatform.webview.web.WebViewState

@Composable
actual fun PlatformWebView(state: WebViewState, navigator: WebViewNavigator, modifier: Modifier) {
WebView(
state = state,
navigator = navigator,
modifier = modifier
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package dev.datlag.burningseries.ui.custom

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.multiplatform.webview.web.WebViewNavigator
import com.multiplatform.webview.web.WebViewState

@Composable
expect fun PlatformWebView(state: WebViewState, navigator: WebViewNavigator, modifier: Modifier = Modifier)
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package dev.datlag.burningseries.ui.custom.state

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import dev.datlag.burningseries.LocalDarkMode
import dev.datlag.burningseries.SharedRes
import dev.icerock.moko.resources.StringResource
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource

@Composable
fun BrowserState(text: String) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterVertically),
horizontalAlignment = Alignment.CenterHorizontally,
) {
val painterRes = if (LocalDarkMode.current) {
SharedRes.images.browser_dark
} else {
SharedRes.images.browser_light
}
Image(
modifier = Modifier.fillMaxWidth(0.7F),
painter = painterResource(painterRes),
contentDescription = text
)
Text(
modifier = Modifier.fillMaxWidth(0.85F),
text = text,
fontWeight = FontWeight.SemiBold,
softWrap = true,
textAlign = TextAlign.Center
)
}
}

@Composable
fun BrowserState(text: StringResource) = BrowserState(text = stringResource(text))
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,9 @@ sealed class DialogConfig : Parcelable {
val selected: Series.Language,
val languages: List<Series.Language>
) : DialogConfig()

@Parcelize
data class StreamUnavailable(
val episode: Series.Episode
) : DialogConfig()
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import kotlinx.coroutines.flow.StateFlow
interface SeriesComponent : Component {

val seriesState: StateFlow<SeriesState>
val episodeState: StateFlow<EpisodeState>

val child: Value<ChildSlot<*, Component>>
val dialog: Value<ChildSlot<DialogConfig, DialogComponent>>

val title: StateFlow<String>
Expand All @@ -22,6 +23,8 @@ interface SeriesComponent : Component {
val coverHref: StateFlow<String?>
val isFavorite: StateFlow<Boolean>

val loadingEpisodeHref: StateFlow<String?>

fun retryLoadingSeries(): Any?

fun goBack()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package dev.datlag.burningseries.ui.screen.initial.series

import com.arkivanov.essenty.parcelable.Parcelable
import com.arkivanov.essenty.parcelable.Parcelize
import dev.datlag.burningseries.model.Series

@Parcelize
sealed class SeriesConfig : Parcelable {

@Parcelize
data class Activate(
val episode: Series.Episode
) : SeriesConfig(), Parcelable
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,17 @@ import kotlin.math.abs
fun SeriesScreen(component: SeriesComponent) {
val href by component.commonHref.collectAsStateWithLifecycle()
val dialogState by component.dialog.subscribeAsState()
val childState by component.child.subscribeAsState()

SchemeTheme.setCommon(href)
when (calculateWindowSizeClass().widthSizeClass) {
WindowWidthSizeClass.Compact -> CompactScreen(component)
else -> DefaultScreen(component)

childState.child?.also { (_, instance) ->
instance.render()
} ?: run {
when (calculateWindowSizeClass().widthSizeClass) {
WindowWidthSizeClass.Compact -> CompactScreen(component)
else -> DefaultScreen(component)
}
}

val scope = rememberCoroutineScope()
Expand Down Expand Up @@ -224,7 +230,7 @@ private fun CompactScreen(component: SeriesComponent) {
}
}
is SeriesState.Success -> {
val episodeState by component.episodeState.collectAsStateWithLifecycle()
val loadingEpisode by component.loadingEpisodeHref.collectAsStateWithLifecycle()
val state = rememberLazyListState(
initialFirstVisibleItemIndex = StateSaver.seriesListIndex,
initialFirstVisibleItemScrollOffset = StateSaver.seriesListOffset
Expand Down Expand Up @@ -264,7 +270,7 @@ private fun CompactScreen(component: SeriesComponent) {
)
}

SeriesContent(current.series, episodeState) {
SeriesContent(current.series, loadingEpisode) {
component.itemClicked(it)
}
}
Expand Down Expand Up @@ -302,7 +308,7 @@ private fun DefaultScreen(component: SeriesComponent) {
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(2.dp)
) {
val episodeState by component.episodeState.collectAsStateWithLifecycle()
val loadingEpisode by component.loadingEpisodeHref.collectAsStateWithLifecycle()
val state = rememberLazyListState(
initialFirstVisibleItemIndex = StateSaver.seriesListIndex,
initialFirstVisibleItemScrollOffset = StateSaver.seriesListOffset
Expand Down Expand Up @@ -395,7 +401,7 @@ private fun DefaultScreen(component: SeriesComponent) {
}
}

SeriesContent(current.series, episodeState) {
SeriesContent(current.series, loadingEpisode) {
component.itemClicked(it)
}
}
Expand All @@ -412,17 +418,7 @@ private fun DefaultScreen(component: SeriesComponent) {
}
}

private fun LazyListScope.SeriesContent(content: Series, episodeState: EpisodeState, onEpisodeClick: (Series.Episode) -> Unit) {
val loadingEpisode = when (val current = episodeState) {
is EpisodeState.Loading -> current.episode.href
is EpisodeState.SuccessHoster -> current.episode.href
is EpisodeState.SuccessStream -> {
println(current.results)
null
}
else -> null
}

private fun LazyListScope.SeriesContent(content: Series, loadingEpisode: String?, onEpisodeClick: (Series.Episode) -> Unit) {
items(content.episodes, key = { it.href }) { episode ->
EpisodeItem(episode, loadingEpisode.equals(episode.href, true)) {
onEpisodeClick(episode)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,12 @@ package dev.datlag.burningseries.ui.screen.initial.series

import androidx.compose.runtime.Composable
import app.cash.sqldelight.coroutines.asFlow
import app.cash.sqldelight.coroutines.mapToOneNotNull
import app.cash.sqldelight.coroutines.mapToOneOrNull
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.router.slot.*
import com.arkivanov.decompose.value.Value
import com.arkivanov.essenty.backhandler.BackCallback
import com.arkivanov.essenty.backhandler.BackHandler
import dev.datlag.burningseries.common.defaultScope
import dev.datlag.burningseries.common.ioDispatcher
import dev.datlag.burningseries.common.ioScope
import dev.datlag.burningseries.common.launchIO
import dev.datlag.burningseries.common.*
import dev.datlag.burningseries.database.BurningSeries
import dev.datlag.burningseries.model.BSUtil
import dev.datlag.burningseries.model.Series
Expand All @@ -22,9 +17,13 @@ import dev.datlag.burningseries.model.state.SeriesAction
import dev.datlag.burningseries.model.state.SeriesState
import dev.datlag.burningseries.network.state.EpisodeStateMachine
import dev.datlag.burningseries.network.state.SeriesStateMachine
import dev.datlag.burningseries.ui.navigation.Component
import dev.datlag.burningseries.ui.navigation.DialogComponent
import dev.datlag.burningseries.ui.screen.initial.series.activate.ActivateScreenComponent
import dev.datlag.burningseries.ui.screen.initial.series.dialog.language.LanguageDialogComponent
import dev.datlag.burningseries.ui.screen.initial.series.dialog.season.SeasonDialogComponent
import dev.datlag.burningseries.ui.screen.initial.series.dialog.unavailable.UnavailableDialog
import dev.datlag.burningseries.ui.screen.initial.series.dialog.unavailable.UnavailableDialogComponent
import io.ktor.client.*
import kotlinx.coroutines.flow.*
import kotlinx.datetime.Clock
Expand Down Expand Up @@ -65,10 +64,29 @@ class SeriesScreenComponent(
}.stateIn(ioScope(), SharingStarted.Lazily, database.burningSeriesQueries.seriesByHref(commonHref.value).executeAsOneOrNull()?.favoriteSince?.let { it > 0 } ?: false)

private val episodeStateMachine by di.instance<EpisodeStateMachine>()
override val episodeState: StateFlow<EpisodeState> = episodeStateMachine.state.flowOn(ioDispatcher()).stateIn(ioScope(), SharingStarted.Lazily, EpisodeState.Waiting)
private val episodeState: StateFlow<EpisodeState> = episodeStateMachine.state.flowOn(ioDispatcher()).stateIn(ioScope(), SharingStarted.Lazily, EpisodeState.Waiting)
override val loadingEpisodeHref: StateFlow<String?> = episodeState.map {
(it as? EpisodeState.Loading)?.episode?.href ?: (it as? EpisodeState.SuccessHoster)?.episode?.href
}.stateIn(ioScope(), SharingStarted.Lazily, null)

private val navigation = SlotNavigation<SeriesConfig>()
override val child: Value<ChildSlot<*, Component>> = childSlot(
source = navigation,
handleBackButton = false
) { config, context ->
when (config) {
is SeriesConfig.Activate -> ActivateScreenComponent(
componentContext = context,
di = di,
episode = config.episode,
onGoBack = navigation::dismiss
)
}
}

private val dialogNavigation = SlotNavigation<DialogConfig>()
private val _dialog = childSlot(
key = "DialogChildSlot",
source = dialogNavigation
) { config, slotContext ->
when (config) {
Expand All @@ -92,16 +110,48 @@ class SeriesScreenComponent(
loadNewLanguage(it)
}
)
is DialogConfig.StreamUnavailable -> UnavailableDialogComponent(
componentContext = slotContext,
di = di,
episode = config.episode,
onDismissed = dialogNavigation::dismiss,
onActivate = {
navigation.activate(SeriesConfig.Activate(it))
}
)
}
}
override val dialog: Value<ChildSlot<DialogConfig, DialogComponent>> = _dialog

private val backCallback = BackCallback(priority = Int.MAX_VALUE) {
private val backCallback = BackCallback {
onGoBack()
}

init {
backHandler.register(backCallback)

ioScope().launchIO {
episodeState.collect { state ->
when (state) {
is EpisodeState.SuccessStream -> {
// ToDo("navigate to video player")
}
is EpisodeState.ErrorHoster, is EpisodeState.ErrorStream -> {
val episode = (state as? EpisodeState.ErrorHoster)?.episode
?: (state as? EpisodeState.ErrorStream)?.episode

if (episode != null) {
withMainContext {
showDialog(
DialogConfig.StreamUnavailable(episode)
)
}
}
}
else -> { }
}
}
}
}

@Composable
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package dev.datlag.burningseries.ui.screen.initial.series.activate

import dev.datlag.burningseries.model.Series
import dev.datlag.burningseries.ui.navigation.Component

interface ActivateComponent : Component {

val episode: Series.Episode
val scrapingJs: String

fun back()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package dev.datlag.burningseries.ui.screen.initial.series.activate

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBackIosNew
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import com.multiplatform.webview.web.rememberWebViewNavigator
import com.multiplatform.webview.web.rememberWebViewState
import dev.icerock.moko.resources.compose.stringResource
import dev.datlag.burningseries.SharedRes
import dev.datlag.burningseries.common.withIOContext
import dev.datlag.burningseries.model.BSUtil
import dev.datlag.burningseries.ui.custom.PlatformWebView
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ActivateScreen(component: ActivateComponent) {
Scaffold(
topBar = {
TopAppBar(
title = {
Text(text = stringResource(SharedRes.strings.activate_hint))
},
navigationIcon = {
IconButton(
onClick = {
component.back()
}
) {
Icon(
imageVector = Icons.Default.ArrowBackIosNew,
contentDescription = stringResource(SharedRes.strings.back)
)
}
}
)
}
) {
Box(modifier = Modifier.padding(it)) {
val state = rememberWebViewState(url = BSUtil.getBurningSeriesLink(component.episode.href))
val navigator = rememberWebViewNavigator()

PlatformWebView(state, navigator, Modifier.fillMaxSize())

LaunchedEffect(state) {
withIOContext {
do {
delay(3000)
navigator.evaluateJavaScript(component.scrapingJs) { result ->
println("Got: $result")
}
} while (isActive)
}
}
}
}
}
Loading

0 comments on commit 8e5ac52

Please sign in to comment.