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 {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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: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 {
|
||||||
|
@ -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"))
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
@ -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>) {
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user