Skip to content

Commit

Permalink
prepare favorite and search screen
Browse files Browse the repository at this point in the history
  • Loading branch information
DatL4g committed Nov 13, 2023
1 parent f7a2159 commit 3a6a1e7
Show file tree
Hide file tree
Showing 15 changed files with 201 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.datlag.burningseries.module

import dev.datlag.burningseries.network.state.HomeStateMachine
import dev.datlag.burningseries.network.state.SearchStateMachine
import org.kodein.di.DI
import org.kodein.di.bindSingleton
import org.kodein.di.instance
Expand All @@ -15,5 +16,8 @@ object NetworkModule {
bindSingleton {
HomeStateMachine(instance())
}
bindSingleton {
SearchStateMachine(instance())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import dev.datlag.burningseries.model.state.HomeState
import dev.datlag.burningseries.network.state.HomeStateMachine
import dev.datlag.burningseries.shared.SharedRes
import dev.datlag.burningseries.ui.navigation.Component
import dev.datlag.burningseries.ui.screen.initial.favorite.FavoriteScreenComponent
import dev.datlag.burningseries.ui.screen.initial.home.HomeScreenComponent
import dev.datlag.burningseries.ui.screen.initial.search.SearchScreenComponent
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOn
import org.kodein.di.DI
Expand Down Expand Up @@ -52,7 +54,9 @@ class InitialScreenComponent(
initialPages = {
Pages(
items = listOf(
View.Home
View.Home,
View.Favorite,
View.Search
),
selectedIndex = 0
)
Expand All @@ -75,6 +79,14 @@ class InitialScreenComponent(
componentContext,
di
)
is View.Favorite -> FavoriteScreenComponent(
componentContext,
di
)
is View.Search -> SearchScreenComponent(
componentContext,
di
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,10 @@ sealed class View : Parcelable {

@Parcelize
data object Home : View(), Parcelable

@Parcelize
data object Favorite : View(), Parcelable

@Parcelize
data object Search : View(), Parcelable
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package dev.datlag.burningseries.ui.screen.initial.favorite

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

interface FavoriteComponent : Component {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package dev.datlag.burningseries.ui.screen.initial.favorite

import androidx.compose.runtime.Composable

@Composable
fun FavoriteScreen(component: FavoriteComponent) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package dev.datlag.burningseries.ui.screen.initial.favorite

import androidx.compose.runtime.Composable
import com.arkivanov.decompose.ComponentContext
import org.kodein.di.DI

class FavoriteScreenComponent(
componentContext: ComponentContext,
override val di: DI
) : FavoriteComponent, ComponentContext by componentContext {

@Composable
override fun render() {
FavoriteScreen(this)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package dev.datlag.burningseries.ui.screen.initial.search

import dev.datlag.burningseries.model.state.SearchState
import dev.datlag.burningseries.ui.navigation.Component
import kotlinx.coroutines.flow.StateFlow

interface SearchComponent : Component {

val searchState: StateFlow<SearchState>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package dev.datlag.burningseries.ui.screen.initial.search

import androidx.compose.runtime.Composable

@Composable
fun SearchScreen(component: SearchComponent) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package dev.datlag.burningseries.ui.screen.initial.search

import androidx.compose.runtime.Composable
import com.arkivanov.decompose.ComponentContext
import dev.datlag.burningseries.common.ioDispatcher
import dev.datlag.burningseries.common.ioScope
import dev.datlag.burningseries.model.state.SearchState
import dev.datlag.burningseries.network.state.SearchStateMachine
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.stateIn
import org.kodein.di.DI
import org.kodein.di.instance

class SearchScreenComponent(
componentContext: ComponentContext,
override val di: DI
) : SearchComponent, ComponentContext by componentContext {

private val homeStateMachine: SearchStateMachine by di.instance()
override val searchState: StateFlow<SearchState> = homeStateMachine.state.flowOn(ioDispatcher()).stateIn(ioScope(), SharingStarted.Lazily, SearchState.Loading)

@Composable
override fun render() {
SearchScreen(this)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ fun SeasonAndLanguageButtons(
onClick = {
onSeasonClick()
},
enabled = seasons.size > 1
enabled = seasons.size > 1,
modifier = Modifier.weight(1F)
) {
val seasonText = if (selectedSeason.title.toIntOrNull() != null) {
stringResource(SharedRes.strings.season_placeholder, selectedSeason.title)
Expand All @@ -51,7 +52,8 @@ fun SeasonAndLanguageButtons(
onClick = {
onLanguageClick()
},
enabled = languages.size > 1
enabled = languages.size > 1,
modifier = Modifier.weight(1F)
) {
CountryImage(
code = selectedLanguage.value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ data object BSUtil {
const val PROTOCOL_HTTPS = "https://"

const val HOST_BS_TO = "bs.to"
const val SEARCH = "andere-serien"

val episodeNumberRegex = "[|({]\\s*Ep([.]|isode)?\\s*(\\d+)\\s*[|)}]".toRegex(RegexOption.IGNORE_CASE)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package dev.datlag.burningseries.model

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class Genre(
@SerialName("title") val title: String,
@SerialName("items") val items: List<Item>
) {

@Serializable
data class Item(
@SerialName("title") val title: String,
@SerialName("href") val href: String
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package dev.datlag.burningseries.model.state

import dev.datlag.burningseries.model.Genre

sealed interface SearchState {
data object Loading : SearchState
data class Success(val genres: List<Genre>) : SearchState
data class Error(val msg: String) : SearchState
}

sealed interface SearchAction {
data object Retry : SearchAction
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.datlag.burningseries.network.scraper

import dev.datlag.burningseries.model.BSUtil
import dev.datlag.burningseries.model.Genre
import dev.datlag.burningseries.model.Home
import dev.datlag.burningseries.model.Series
import dev.datlag.burningseries.model.common.getDigitsOrNull
Expand Down Expand Up @@ -222,6 +223,34 @@ data object BurningSeries {
)
}

suspend fun getSearch(client: HttpClient): List<Genre> {
val doc = getDocument(client, BSUtil.SEARCH) ?: return emptyList()
return doc.querySelector("#seriesContainer")?.querySelectorAll(".genre")?.mapNotNull { element ->
val genre = element.querySelector("string")?.textContent()?.trim() ?: String()

if (genre.isNotBlank()) {
Genre(
title = genre,
items = element.querySelectorAll("li").mapNotNull { item ->
val title = item.querySelector("a")?.textContent()?.trim() ?: String()
val href = BSUtil.normalizeHref(item.querySelector("a")?.getHref() ?: String())

if (title.isNotBlank() && href.isNotBlank()) {
Genre.Item(
title = title,
href = href
)
} else {
null
}
}
)
} else {
null
}
} ?: emptyList()
}

private suspend fun getCover(client: HttpClient, href: String): Pair<String?, Boolean> {
return getCover(getDocument(client, href))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package dev.datlag.burningseries.network.state

import com.freeletics.flowredux.dsl.FlowReduxStateMachine
import dev.datlag.burningseries.model.state.SearchAction
import dev.datlag.burningseries.model.state.SearchState
import dev.datlag.burningseries.network.scraper.BurningSeries
import io.ktor.client.*
import kotlinx.coroutines.ExperimentalCoroutinesApi

@OptIn(ExperimentalCoroutinesApi::class)
class SearchStateMachine(
private val client: HttpClient
) : FlowReduxStateMachine<SearchState, SearchAction>(initialState = SearchState.Loading) {
init {
spec {
inState<SearchState.Loading> {
onEnter { state ->
try {
val loadedGenres = BurningSeries.getSearch(client)
if (loadedGenres.isEmpty()) {
state.override { SearchState.Error(String()) }
} else {
state.override { SearchState.Success(loadedGenres) }
}
} catch (t: Throwable) {
state.override { SearchState.Error(t.message ?: String()) }
}
}
}

inState<SearchState.Error> {
on<SearchAction.Retry> { _, state ->
state.override { SearchState.Loading }
}
}
}
}
}

0 comments on commit 3a6a1e7

Please sign in to comment.