web-app bug fixes

This commit is contained in:
shabinder 2021-03-09 17:45:22 +05:30
parent 0d42ea545c
commit 601d8135db
16 changed files with 181 additions and 36 deletions

View File

@ -21,7 +21,7 @@ kotlin {
sourceSets { sourceSets {
named("commonMain") { named("commonMain") {
dependencies { dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2")
} }
} }
@ -33,6 +33,7 @@ kotlin {
implementation(compose.material) implementation(compose.material)
implementation(compose.foundation) implementation(compose.foundation)
implementation(compose.materialIconsExtended) implementation(compose.materialIconsExtended)
implementation(Decompose.decompose)
implementation(Decompose.extensionsCompose) implementation(Decompose.extensionsCompose)
} }
} }
@ -44,6 +45,7 @@ kotlin {
implementation(compose.material) implementation(compose.material)
implementation(compose.desktop.common) implementation(compose.desktop.common)
implementation(compose.materialIconsExtended) implementation(compose.materialIconsExtended)
implementation(Decompose.decompose)
implementation(Decompose.extensionsCompose) implementation(Decompose.extensionsCompose)
} }
} }

View File

@ -11,6 +11,7 @@ kotlin {
dependencies { dependencies {
api("dev.icerock.moko:parcelize:0.6.0") api("dev.icerock.moko:parcelize:0.6.0")
api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0") api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0")
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3")
} }
} }
} }

View File

@ -0,0 +1,9 @@
package com.shabinder.common.models.wynk
data class AlbumRefWynk(
val id: String,
val largeImage: String,
val smallImage: String,
val title: String,
val type: String
)

View File

@ -0,0 +1,7 @@
package com.shabinder.common.models.wynk
data class HtDataWynk(
val cutName: String,
val previewUrl: String,
val vcode: String
)

View File

@ -0,0 +1,36 @@
package com.shabinder.common.models.wynk
data class ItemWynk(
val album: String,
val albumRef: AlbumRefWynk,
val basicShortUrl: String,
val branchUrl: String,
val contentLang: String,
val contentState: String,
val count: Int,
val cues: List<String>,
val downloadPrice: String,
val downloadUrl: String,
val duration: Int, //in Seconds
val exclusive: Boolean,
val formats: List<String>,
val htData: List<HtDataWynk>,
val id: String,
val isHt: Boolean,
val itemContentLang: String,
val keywords: String,
val largeImage: String,
val lyrics_avl: String,
val ostreamingUrl: String,
val purchaseUrl: String,
val rentUrl: String,
val serverEtag: String,
val shortUrl: String,
val smallImage: String, //Cover Image after Replacing 120x120 with 720x720
val subtitle: String, // String : `ArtistName - TrackName`
val subtitleId: String, //ARTIST NAME,artist-id , etc //USE SUBTITLE INSTEAD
val subtitleType: String, // ARTIST etc
val title: String,
val type: String, //Song ,etc
val videoPresent: Boolean
)

View File

@ -0,0 +1,36 @@
package com.shabinder.common.models.wynk
// Use Kotlinx JSON Parsing as in YT Music
data class ShortURLWynk(
val actualTotal: Int,
val basicShortUrl: String,
val branchUrl: String,
val count: Int,
val downloadUrl: String,
val duration: Int,
val exclusive: Boolean,
val followCount: String,
val id: String,
val isCurated: Boolean,
val isFollowable: Boolean,
val isHt: Boolean,
val itemIds: List<String>,
val itemTypes: List<String>, //Songs , etc
val items: List<ItemWynk>,
val lang: String,
val largeImage: String, //Cover Image Alternate
val lastUpdated: Long,
val offset: Int,
val owner: String,
val playIcon: Boolean,
val playlistImage: String, //Cover Image
val redesignFeaturedImage: String,
val shortUrl: String,
val singers: List<SingerWynk>,
val smallImage: String,
val title: String,
val total: Int,
val type: String
)

View File

@ -0,0 +1,10 @@
package com.shabinder.common.models.wynk
data class SingerWynk(
val id: String,
val isCurated: Boolean,
val packageId: String,
val smallImage: String,
val title: String,
val type: String
)

View File

@ -13,7 +13,6 @@ kotlin {
implementation(project(":common:data-models")) implementation(project(":common:data-models"))
implementation(project(":common:database")) implementation(project(":common:database"))
implementation(project(":fuzzywuzzy:app")) implementation(project(":fuzzywuzzy:app"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.1.1") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.1.1")
implementation(Ktor.clientCore) implementation(Ktor.clientCore)
@ -26,17 +25,17 @@ kotlin {
api(Koin.test) api(Koin.test)
api(Extras.kermit) api(Extras.kermit)
api(Extras.youtubeDownloader)
api(Extras.mp3agic)
} }
} }
androidMain { androidMain {
dependencies{ dependencies{
implementation(compose.materialIconsExtended) implementation(compose.materialIconsExtended)
implementation(Koin.android)
implementation(Ktor.clientAndroid) implementation(Ktor.clientAndroid)
implementation(Extras.Android.fetch) implementation(Extras.Android.fetch)
implementation(Koin.android)
implementation(Extras.Android.razorpay) implementation(Extras.Android.razorpay)
api(Extras.youtubeDownloader)
api(Extras.mp3agic)
//api(files("$rootDir/libs/mobile-ffmpeg.aar")) //api(files("$rootDir/libs/mobile-ffmpeg.aar"))
} }
} }
@ -45,6 +44,8 @@ kotlin {
implementation(compose.materialIconsExtended) implementation(compose.materialIconsExtended)
implementation(Ktor.clientApache) implementation(Ktor.clientApache)
implementation(Ktor.slf4j) implementation(Ktor.slf4j)
api(Extras.youtubeDownloader)
api(Extras.mp3agic)
} }
} }
jsMain { jsMain {

View File

@ -17,7 +17,9 @@ dependencies {
implementation(Decompose.decompose) implementation(Decompose.decompose)
implementation(Koin.core) implementation(Koin.core)
implementation(MVIKotlin.mvikotlin) implementation(MVIKotlin.mvikotlin)
implementation(MVIKotlin.coroutines)
implementation(MVIKotlin.mvikotlinMain) implementation(MVIKotlin.mvikotlinMain)
implementation(MVIKotlin.mvikotlinLogging)
implementation(project(":common:root")) implementation(project(":common:root"))
implementation(project(":common:main")) implementation(project(":common:main"))
implementation(project(":common:list")) implementation(project(":common:list"))

View File

@ -4,6 +4,7 @@ import com.arkivanov.decompose.lifecycle.LifecycleRegistry
import com.arkivanov.decompose.lifecycle.destroy import com.arkivanov.decompose.lifecycle.destroy
import com.arkivanov.decompose.lifecycle.resume import com.arkivanov.decompose.lifecycle.resume
import com.arkivanov.mvikotlin.core.store.StoreFactory import com.arkivanov.mvikotlin.core.store.StoreFactory
import com.arkivanov.mvikotlin.logging.store.LoggingStoreFactory
import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory
import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.DownloadStatus
import com.shabinder.common.root.SpotiFlyerRoot import com.shabinder.common.root.SpotiFlyerRoot
@ -33,7 +34,7 @@ class App(props: AppProps): RComponent<AppProps, RState>(props) {
private val root = SpotiFlyerRoot(ctx, private val root = SpotiFlyerRoot(ctx,
object : SpotiFlyerRoot.Dependencies{ object : SpotiFlyerRoot.Dependencies{
override val storeFactory: StoreFactory = DefaultStoreFactory override val storeFactory: StoreFactory = LoggingStoreFactory(DefaultStoreFactory)
override val fetchPlatformQueryResult = dependencies.fetchPlatformQueryResult override val fetchPlatformQueryResult = dependencies.fetchPlatformQueryResult
override val directories = dependencies.directories override val directories = dependencies.directories
override val database: Database? = directories.db override val database: Database? = directories.db

View File

@ -1,27 +1,48 @@
package extras package extras
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch import react.RComponent
import react.RProps
import react.RState import react.RState
import react.setState import react.setState
abstract class RenderableComponent< abstract class RenderableComponent<
T : Any, T : Any,
S : RState S : Any
>( >(
props: Props<T>, props: Props<T>,
initialState: S initialState: S
) : RenderableRootComponent<T, S>(props,initialState) { ) : RComponent<RenderableComponent.Props<T>, RenderableComponent.State<S>>(props) {
protected abstract val stateFlow: Flow<S> protected abstract val stateFlow: Flow<S>
protected val model: T get() = props.model
protected var scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
init {
state = State(data = initialState)
}
override fun componentDidMount() { override fun componentDidMount() {
super.componentDidMount() if(!scope.isActive)
scope = CoroutineScope(Dispatchers.Default)
scope.launch { scope.launch {
stateFlow.collectLatest { stateFlow.collect {
setState { state = it } setState { data = it }
} }
} }
} }
override fun componentWillUnmount() {
scope.cancel("Component Unmounted")
}
interface Props<T : Any> : RProps {
var model: T
}
class State<S>(
var data: S
):RState
} }

View File

@ -3,12 +3,7 @@ package extras
import com.arkivanov.decompose.value.Value import com.arkivanov.decompose.value.Value
import com.arkivanov.decompose.value.ValueObserver import com.arkivanov.decompose.value.ValueObserver
import extras.RenderableRootComponent.Props import extras.RenderableRootComponent.Props
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import react.RComponent import react.RComponent
import react.RProps import react.RProps
import react.RState import react.RState
@ -24,15 +19,16 @@ abstract class RenderableRootComponent<
protected val model: T get() = props.model protected val model: T get() = props.model
private val subscriptions = ArrayList<Subscription<*>>() private val subscriptions = ArrayList<Subscription<*>>()
protected lateinit var scope: CoroutineScope protected var scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
init { init {
state = initialState this.state = initialState
} }
override fun componentDidMount() { override fun componentDidMount() {
subscriptions.forEach { subscribe(it) } subscriptions.forEach { subscribe(it) }
scope = CoroutineScope(Dispatchers.Default) if(!scope.isActive)
scope = CoroutineScope(Dispatchers.Default)
} }
private fun <T : Any> subscribe(subscription: Subscription<T>) { private fun <T : Any> subscribe(subscription: Subscription<T>) {

View File

@ -9,3 +9,9 @@ fun <M : Any, T : RenderableRootComponent<M, *>> RBuilder.renderableChild(clazz:
attrs.model = model attrs.model = model
} }
} }
fun <M : Any, T : RenderableComponent<M, *>> RBuilder.renderableChild(clazz: KClass<out T>, model: M) {
child(clazz) {
key = model.uniqueId().toString()
attrs.model = model
}
}

View File

@ -1,9 +1,14 @@
package home package home
import com.shabinder.common.main.SpotiFlyerMain import com.shabinder.common.main.SpotiFlyerMain
import com.shabinder.common.main.SpotiFlyerMain.State
import extras.RenderableComponent import extras.RenderableComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.css.* import kotlinx.css.*
import react.* import react.*
import styled.css import styled.css
@ -11,13 +16,26 @@ import styled.styledDiv
class HomeScreen( class HomeScreen(
props: Props<SpotiFlyerMain>, props: Props<SpotiFlyerMain>,
override val stateFlow: Flow<State> = props.model.models.map { State(it) } ) : RenderableComponent<SpotiFlyerMain, State>(
) : RenderableComponent<SpotiFlyerMain, HomeScreen.State>(
props, props,
initialState = State(data = SpotiFlyerMain.State()) initialState = State()
) { ) {
override val stateFlow: Flow<SpotiFlyerMain.State> = model.models
override fun componentDidMount() {
if(!scope.isActive)
scope = CoroutineScope(Dispatchers.Default)
scope.launch {
stateFlow.collect {
println("Updating State = $it")
setState { data = it }
}
}
}
override fun RBuilder.render() { override fun RBuilder.render() {
println("Rendering New State = \"${state.data}\" ")
styledDiv{ styledDiv{
css { css {
display = Display.flex display = Display.flex
@ -32,8 +50,10 @@ class HomeScreen(
} }
SearchBar { SearchBar {
println("Search Props ${state.data.link}")
link = state.data.link link = state.data.link
search = model::onLinkSearch search = model::onLinkSearch
onLinkChange = model::onInputLinkChanged
} }
IconList { IconList {
@ -46,10 +66,6 @@ class HomeScreen(
isBadge = true isBadge = true
} }
} }
class State(
var data: SpotiFlyerMain.State
):RState
} }

View File

@ -10,6 +10,7 @@ import styled.*
external interface SearchbarProps : RProps { external interface SearchbarProps : RProps {
var link: String var link: String
var search:(String)->Unit var search:(String)->Unit
var onLinkChange:(String)->Unit
} }
@Suppress("FunctionName") @Suppress("FunctionName")
@ -22,8 +23,6 @@ fun RBuilder.SearchBar(handler:SearchbarProps.() -> Unit) = child(searchbar){
val searchbar = functionalComponent<SearchbarProps>("SearchBar"){ props -> val searchbar = functionalComponent<SearchbarProps>("SearchBar"){ props ->
val (link,setLink) = useState(props.link)
styledDiv{ styledDiv{
css { css {
classes = mutableListOf("searchBox") classes = mutableListOf("searchBox")
@ -33,9 +32,10 @@ val searchbar = functionalComponent<SearchbarProps>("SearchBar"){ props ->
placeholder = "Search" placeholder = "Search"
onChangeFunction = { onChangeFunction = {
val target = it.target as HTMLInputElement val target = it.target as HTMLInputElement
setLink(target.value) props.onLinkChange(target.value)
println(target.value)
} }
value = link value = props.link
} }
css { css {
classes = mutableListOf("searchInput") classes = mutableListOf("searchInput")
@ -44,7 +44,7 @@ val searchbar = functionalComponent<SearchbarProps>("SearchBar"){ props ->
styledButton { styledButton {
attrs { attrs {
onClickFunction = { onClickFunction = {
props.search(link) props.search(props.link)
} }
} }
css { css {

View File

@ -13,9 +13,10 @@ import styled.styledDiv
class ListScreen( class ListScreen(
props: Props<SpotiFlyerList>, props: Props<SpotiFlyerList>,
override val stateFlow: Flow<State> = props.model.models.map { State(it) }
) : RenderableComponent<SpotiFlyerList, ListScreen.State>(props,initialState = State(SpotiFlyerList.State())) { ) : RenderableComponent<SpotiFlyerList, ListScreen.State>(props,initialState = State(SpotiFlyerList.State())) {
override val stateFlow: Flow<State> = model.models.map { State(it) }
override fun RBuilder.render() { override fun RBuilder.render() {
styledDiv { styledDiv {
attrs { attrs {