mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-25 02:14:32 +01:00
web-app bug fixes
This commit is contained in:
parent
0d42ea545c
commit
601d8135db
@ -21,7 +21,7 @@ kotlin {
|
||||
sourceSets {
|
||||
named("commonMain") {
|
||||
dependencies {
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ kotlin {
|
||||
implementation(compose.material)
|
||||
implementation(compose.foundation)
|
||||
implementation(compose.materialIconsExtended)
|
||||
implementation(Decompose.decompose)
|
||||
implementation(Decompose.extensionsCompose)
|
||||
}
|
||||
}
|
||||
@ -44,6 +45,7 @@ kotlin {
|
||||
implementation(compose.material)
|
||||
implementation(compose.desktop.common)
|
||||
implementation(compose.materialIconsExtended)
|
||||
implementation(Decompose.decompose)
|
||||
implementation(Decompose.extensionsCompose)
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ kotlin {
|
||||
dependencies {
|
||||
api("dev.icerock.moko:parcelize:0.6.0")
|
||||
api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0")
|
||||
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
)
|
@ -0,0 +1,7 @@
|
||||
package com.shabinder.common.models.wynk
|
||||
|
||||
data class HtDataWynk(
|
||||
val cutName: String,
|
||||
val previewUrl: String,
|
||||
val vcode: String
|
||||
)
|
@ -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
|
||||
)
|
@ -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
|
||||
)
|
@ -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
|
||||
)
|
@ -13,7 +13,6 @@ kotlin {
|
||||
implementation(project(":common:data-models"))
|
||||
implementation(project(":common:database"))
|
||||
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-datetime:0.1.1")
|
||||
implementation(Ktor.clientCore)
|
||||
@ -26,17 +25,17 @@ kotlin {
|
||||
api(Koin.test)
|
||||
|
||||
api(Extras.kermit)
|
||||
api(Extras.youtubeDownloader)
|
||||
api(Extras.mp3agic)
|
||||
}
|
||||
}
|
||||
androidMain {
|
||||
dependencies{
|
||||
implementation(compose.materialIconsExtended)
|
||||
implementation(Koin.android)
|
||||
implementation(Ktor.clientAndroid)
|
||||
implementation(Extras.Android.fetch)
|
||||
implementation(Koin.android)
|
||||
implementation(Extras.Android.razorpay)
|
||||
api(Extras.youtubeDownloader)
|
||||
api(Extras.mp3agic)
|
||||
//api(files("$rootDir/libs/mobile-ffmpeg.aar"))
|
||||
}
|
||||
}
|
||||
@ -45,6 +44,8 @@ kotlin {
|
||||
implementation(compose.materialIconsExtended)
|
||||
implementation(Ktor.clientApache)
|
||||
implementation(Ktor.slf4j)
|
||||
api(Extras.youtubeDownloader)
|
||||
api(Extras.mp3agic)
|
||||
}
|
||||
}
|
||||
jsMain {
|
||||
|
@ -17,7 +17,9 @@ dependencies {
|
||||
implementation(Decompose.decompose)
|
||||
implementation(Koin.core)
|
||||
implementation(MVIKotlin.mvikotlin)
|
||||
implementation(MVIKotlin.coroutines)
|
||||
implementation(MVIKotlin.mvikotlinMain)
|
||||
implementation(MVIKotlin.mvikotlinLogging)
|
||||
implementation(project(":common:root"))
|
||||
implementation(project(":common:main"))
|
||||
implementation(project(":common:list"))
|
||||
|
@ -4,6 +4,7 @@ import com.arkivanov.decompose.lifecycle.LifecycleRegistry
|
||||
import com.arkivanov.decompose.lifecycle.destroy
|
||||
import com.arkivanov.decompose.lifecycle.resume
|
||||
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
||||
import com.arkivanov.mvikotlin.logging.store.LoggingStoreFactory
|
||||
import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
import com.shabinder.common.root.SpotiFlyerRoot
|
||||
@ -33,7 +34,7 @@ class App(props: AppProps): RComponent<AppProps, RState>(props) {
|
||||
|
||||
private val root = SpotiFlyerRoot(ctx,
|
||||
object : SpotiFlyerRoot.Dependencies{
|
||||
override val storeFactory: StoreFactory = DefaultStoreFactory
|
||||
override val storeFactory: StoreFactory = LoggingStoreFactory(DefaultStoreFactory)
|
||||
override val fetchPlatformQueryResult = dependencies.fetchPlatformQueryResult
|
||||
override val directories = dependencies.directories
|
||||
override val database: Database? = directories.db
|
||||
|
@ -1,27 +1,48 @@
|
||||
package extras
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import react.RComponent
|
||||
import react.RProps
|
||||
import react.RState
|
||||
import react.setState
|
||||
|
||||
abstract class RenderableComponent<
|
||||
T : Any,
|
||||
S : RState
|
||||
S : Any
|
||||
>(
|
||||
props: Props<T>,
|
||||
initialState: S
|
||||
) : RenderableRootComponent<T, S>(props,initialState) {
|
||||
) : RComponent<RenderableComponent.Props<T>, RenderableComponent.State<S>>(props) {
|
||||
|
||||
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() {
|
||||
super.componentDidMount()
|
||||
if(!scope.isActive)
|
||||
scope = CoroutineScope(Dispatchers.Default)
|
||||
scope.launch {
|
||||
stateFlow.collectLatest {
|
||||
setState { state = it }
|
||||
stateFlow.collect {
|
||||
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
|
||||
}
|
@ -3,12 +3,7 @@ package extras
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.arkivanov.decompose.value.ValueObserver
|
||||
import extras.RenderableRootComponent.Props
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.*
|
||||
import react.RComponent
|
||||
import react.RProps
|
||||
import react.RState
|
||||
@ -24,15 +19,16 @@ abstract class RenderableRootComponent<
|
||||
|
||||
protected val model: T get() = props.model
|
||||
private val subscriptions = ArrayList<Subscription<*>>()
|
||||
protected lateinit var scope: CoroutineScope
|
||||
protected var scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
||||
|
||||
init {
|
||||
state = initialState
|
||||
this.state = initialState
|
||||
}
|
||||
|
||||
override fun componentDidMount() {
|
||||
subscriptions.forEach { subscribe(it) }
|
||||
scope = CoroutineScope(Dispatchers.Default)
|
||||
if(!scope.isActive)
|
||||
scope = CoroutineScope(Dispatchers.Default)
|
||||
}
|
||||
|
||||
private fun <T : Any> subscribe(subscription: Subscription<T>) {
|
||||
|
@ -9,3 +9,9 @@ fun <M : Any, T : RenderableRootComponent<M, *>> RBuilder.renderableChild(clazz:
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,14 @@
|
||||
package home
|
||||
|
||||
import com.shabinder.common.main.SpotiFlyerMain
|
||||
import com.shabinder.common.main.SpotiFlyerMain.State
|
||||
import extras.RenderableComponent
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
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 react.*
|
||||
import styled.css
|
||||
@ -11,13 +16,26 @@ import styled.styledDiv
|
||||
|
||||
class HomeScreen(
|
||||
props: Props<SpotiFlyerMain>,
|
||||
override val stateFlow: Flow<State> = props.model.models.map { State(it) }
|
||||
) : RenderableComponent<SpotiFlyerMain, HomeScreen.State>(
|
||||
) : RenderableComponent<SpotiFlyerMain, State>(
|
||||
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() {
|
||||
println("Rendering New State = \"${state.data}\" ")
|
||||
styledDiv{
|
||||
css {
|
||||
display = Display.flex
|
||||
@ -32,8 +50,10 @@ class HomeScreen(
|
||||
}
|
||||
|
||||
SearchBar {
|
||||
println("Search Props ${state.data.link}")
|
||||
link = state.data.link
|
||||
search = model::onLinkSearch
|
||||
onLinkChange = model::onInputLinkChanged
|
||||
}
|
||||
|
||||
IconList {
|
||||
@ -46,10 +66,6 @@ class HomeScreen(
|
||||
isBadge = true
|
||||
}
|
||||
}
|
||||
|
||||
class State(
|
||||
var data: SpotiFlyerMain.State
|
||||
):RState
|
||||
}
|
||||
|
||||
|
||||
|
@ -10,6 +10,7 @@ import styled.*
|
||||
external interface SearchbarProps : RProps {
|
||||
var link: String
|
||||
var search:(String)->Unit
|
||||
var onLinkChange:(String)->Unit
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
@ -22,8 +23,6 @@ fun RBuilder.SearchBar(handler:SearchbarProps.() -> Unit) = child(searchbar){
|
||||
|
||||
|
||||
val searchbar = functionalComponent<SearchbarProps>("SearchBar"){ props ->
|
||||
val (link,setLink) = useState(props.link)
|
||||
|
||||
styledDiv{
|
||||
css {
|
||||
classes = mutableListOf("searchBox")
|
||||
@ -33,9 +32,10 @@ val searchbar = functionalComponent<SearchbarProps>("SearchBar"){ props ->
|
||||
placeholder = "Search"
|
||||
onChangeFunction = {
|
||||
val target = it.target as HTMLInputElement
|
||||
setLink(target.value)
|
||||
props.onLinkChange(target.value)
|
||||
println(target.value)
|
||||
}
|
||||
value = link
|
||||
value = props.link
|
||||
}
|
||||
css {
|
||||
classes = mutableListOf("searchInput")
|
||||
@ -44,7 +44,7 @@ val searchbar = functionalComponent<SearchbarProps>("SearchBar"){ props ->
|
||||
styledButton {
|
||||
attrs {
|
||||
onClickFunction = {
|
||||
props.search(link)
|
||||
props.search(props.link)
|
||||
}
|
||||
}
|
||||
css {
|
||||
|
@ -13,9 +13,10 @@ import styled.styledDiv
|
||||
|
||||
class ListScreen(
|
||||
props: Props<SpotiFlyerList>,
|
||||
override val stateFlow: Flow<State> = props.model.models.map { State(it) }
|
||||
) : RenderableComponent<SpotiFlyerList, ListScreen.State>(props,initialState = State(SpotiFlyerList.State())) {
|
||||
|
||||
override val stateFlow: Flow<State> = model.models.map { State(it) }
|
||||
|
||||
override fun RBuilder.render() {
|
||||
styledDiv {
|
||||
attrs {
|
||||
|
Loading…
Reference in New Issue
Block a user