mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-21 16:54:33 +01:00
Web App Dep Updation and Moving to IR backend
This commit is contained in:
parent
034aa246d8
commit
2cdb764430
@ -129,7 +129,7 @@ dependencies {
|
||||
}
|
||||
|
||||
//implementation("com.jakewharton.timber:timber:4.7.1")
|
||||
implementation("dev.icerock.moko:parcelize:0.6.1")
|
||||
implementation("dev.icerock.moko:parcelize:0.7.0")
|
||||
implementation("com.github.shabinder:storage-chooser:2.0.4.45")
|
||||
implementation("com.google.accompanist:accompanist-insets:0.11.1")
|
||||
|
||||
|
@ -41,7 +41,7 @@ class App: Application(), KoinComponent {
|
||||
|
||||
val tracker: Tracker by lazy {
|
||||
TrackerBuilder.createDefault(
|
||||
"https://kind-grasshopper-73.telebit.io/matomo/matomo.php", 1)
|
||||
"https://matomo.spotiflyer.ml/matomo.php", 1)
|
||||
.build(Matomo.getInstance(this)).apply {
|
||||
if (BuildConfig.DEBUG) {
|
||||
/*Timber.plant(DebugTree())
|
||||
@ -85,7 +85,7 @@ class App: Application(), KoinComponent {
|
||||
}
|
||||
// Send Crash Report to self hosted Acrarium (FOSS)
|
||||
httpSender {
|
||||
uri = "https://kind-grasshopper-73.telebit.io/acrarium/report"
|
||||
uri = "https://acrarium.spotiflyer.ml/report"
|
||||
basicAuthLogin = "sDj2xCKQIxw0dujf"
|
||||
basicAuthPassword = "O83du0TsgsDJ69zN"
|
||||
httpMethod = HttpSender.Method.POST
|
||||
|
@ -42,7 +42,7 @@ kotlin {
|
||||
implementation(compose.animation)
|
||||
|
||||
implementation(Extras.kermit)
|
||||
implementation("dev.icerock.moko:parcelize:0.6.1")
|
||||
implementation("dev.icerock.moko:parcelize:0.7.0")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-native-mt") {
|
||||
@Suppress("DEPRECATION")
|
||||
isForce = true
|
||||
|
@ -34,7 +34,7 @@ kotlin {
|
||||
jvm("desktop")
|
||||
android()
|
||||
|
||||
js(/*BOTH*/) {
|
||||
js(BOTH) {
|
||||
browser()
|
||||
// nodejs()
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ kotlin {
|
||||
jvm("desktop")
|
||||
android()
|
||||
|
||||
js(/*BOTH*/) {
|
||||
js(BOTH) {
|
||||
browser()
|
||||
// nodejs()
|
||||
}
|
||||
@ -65,7 +65,7 @@ kotlin {
|
||||
implementation(Extras.kermit)
|
||||
implementation(Serialization.json)
|
||||
implementation("co.touchlab:stately-common:1.1.7")
|
||||
implementation("dev.icerock.moko:parcelize:0.6.1")
|
||||
implementation("dev.icerock.moko:parcelize:0.7.0")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-native-mt") {
|
||||
@Suppress("DEPRECATION")
|
||||
isForce = true
|
||||
@ -102,9 +102,9 @@ kotlin {
|
||||
named("jsMain") {
|
||||
dependencies {
|
||||
implementation(Ktor.clientJs)
|
||||
implementation("org.jetbrains:kotlin-react:17.0.1-pre.148-kotlin-1.4.30")
|
||||
implementation("org.jetbrains:kotlin-styled:1.0.0-pre.115-kotlin-1.4.10")
|
||||
implementation("org.jetbrains:kotlin-react-dom:17.0.1-pre.148-kotlin-1.4.30")
|
||||
implementation("org.jetbrains.kotlin-wrappers:kotlin-react:17.0.2-pre.213-kotlin-1.5.10")
|
||||
implementation("org.jetbrains.kotlin-wrappers:kotlin-react-dom:17.0.2-pre.213-kotlin-1.5.10")
|
||||
implementation("org.jetbrains.kotlin-wrappers:kotlin-styled:5.3.0-pre.213-kotlin-1.5.10")
|
||||
}
|
||||
}
|
||||
if(HostOS.isMac){
|
||||
|
@ -17,32 +17,12 @@
|
||||
package com.shabinder.common.uikit
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.CircularProgressIndicator
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.ExtendedFloatingActionButton
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
@ -51,7 +31,7 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.arkivanov.decompose.extensions.compose.jetbrains.asState
|
||||
import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState
|
||||
import com.shabinder.common.di.Picture
|
||||
import com.shabinder.common.list.SpotiFlyerList
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
@ -64,7 +44,7 @@ fun SpotiFlyerListContent(
|
||||
component: SpotiFlyerList,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val model by component.models.asState()
|
||||
val model by component.model.subscribeAsState()
|
||||
|
||||
LaunchedEffect(model.errorOccurred) {
|
||||
/*Handle if Any Exception Occurred*/
|
||||
|
@ -17,57 +17,21 @@
|
||||
package com.shabinder.common.uikit
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.OutlinedButton
|
||||
import androidx.compose.material.Switch
|
||||
import androidx.compose.material.SwitchDefaults
|
||||
import androidx.compose.material.Tab
|
||||
import androidx.compose.material.TabPosition
|
||||
import androidx.compose.material.TabRow
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.TabRowDefaults.tabIndicatorOffset
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextField
|
||||
import androidx.compose.material.TextFieldDefaults.textFieldColors
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.History
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
import androidx.compose.material.icons.rounded.CardGiftcard
|
||||
import androidx.compose.material.icons.rounded.Edit
|
||||
import androidx.compose.material.icons.rounded.Flag
|
||||
import androidx.compose.material.icons.rounded.Insights
|
||||
import androidx.compose.material.icons.rounded.Share
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.material.icons.rounded.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
@ -80,7 +44,7 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.arkivanov.decompose.extensions.compose.jetbrains.asState
|
||||
import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState
|
||||
import com.shabinder.common.di.Picture
|
||||
import com.shabinder.common.main.SpotiFlyerMain
|
||||
import com.shabinder.common.main.SpotiFlyerMain.HomeCategory
|
||||
@ -89,7 +53,7 @@ import com.shabinder.common.models.methods
|
||||
|
||||
@Composable
|
||||
fun SpotiFlyerMainContent(component: SpotiFlyerMain) {
|
||||
val model by component.models.asState()
|
||||
val model by component.model.subscribeAsState()
|
||||
|
||||
Column {
|
||||
SearchPanel(
|
||||
|
@ -17,7 +17,7 @@
|
||||
package com.shabinder.common.models
|
||||
|
||||
sealed class CorsProxy(open val url: String) {
|
||||
data class SelfHostedCorsProxy(override val url: String = "https://kind-grasshopper-73.telebit.io/cors/" /*"https://spotiflyer.azurewebsites.net/"*/) : CorsProxy(url)
|
||||
data class SelfHostedCorsProxy(override val url: String = "https://cors.spotiflyer.ml/cors/" /*"https://spotiflyer.azurewebsites.net/"*/) : CorsProxy(url)
|
||||
data class PublicProxyWithExtension(override val url: String = "https://cors.bridged.cc/") : CorsProxy(url)
|
||||
|
||||
fun toggle(mode: CorsProxy? = null): CorsProxy {
|
||||
|
@ -21,7 +21,7 @@ import com.shabinder.common.di.Dir
|
||||
import com.shabinder.common.di.currentPlatform
|
||||
import com.shabinder.common.di.youtubeMp3.Yt1sMp3
|
||||
import com.shabinder.common.models.AllPlatforms
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.*
|
||||
|
||||
class YoutubeMp3(
|
||||
override val httpClient: HttpClient,
|
||||
@ -33,7 +33,7 @@ class YoutubeMp3(
|
||||
getLinkFromYt1sMp3(videoID)?.let {
|
||||
logger.i { "Download Link: $it" }
|
||||
if (currentPlatform is AllPlatforms.Js/* && corsProxy !is CorsProxy.PublicProxyWithExtension*/)
|
||||
"https://kind-grasshopper-73.telebit.io/cors/$it"
|
||||
"https://cors.spotiflyer.ml/cors/$it"
|
||||
// "https://spotiflyer.azurewebsites.net/$it" // Data OUT Limit issue
|
||||
else it
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ import io.github.shabinder.YoutubeDownloader
|
||||
import io.github.shabinder.models.YoutubeVideo
|
||||
import io.github.shabinder.models.formats.Format
|
||||
import io.github.shabinder.models.quality.AudioQuality
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.*
|
||||
|
||||
class YoutubeProvider(
|
||||
private val httpClient: HttpClient,
|
||||
@ -37,7 +37,7 @@ class YoutubeProvider(
|
||||
) {
|
||||
val ytDownloader: YoutubeDownloader = YoutubeDownloader(
|
||||
enableCORSProxy = true,
|
||||
CORSProxyAddress = "https://kind-grasshopper-73.telebit.io/cors/"
|
||||
CORSProxyAddress = "https://cors.spotiflyer.ml/cors/"
|
||||
)
|
||||
|
||||
/*
|
||||
@ -157,7 +157,7 @@ class YoutubeProvider(
|
||||
val video = ytDownloader.getVideo(searchId)
|
||||
coverUrl = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg"
|
||||
val detail = video.videoDetails
|
||||
val name = detail.title?.replace(detail.author?.toUpperCase() ?: "", "", true)
|
||||
val name = detail.title?.replace(detail.author?.uppercase() ?: "", "", true)
|
||||
?: detail.title ?: ""
|
||||
// logger.i{ detail.toString() }
|
||||
trackList = listOf(
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
package com.shabinder.common.di.utils
|
||||
|
||||
import io.github.shabinder.TargetPlatforms
|
||||
import io.github.shabinder.activePlatform
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlin.native.concurrent.ThreadLocal
|
||||
|
||||
@ -31,6 +33,7 @@ val json by lazy {
|
||||
* Removing Illegal Chars from File Name
|
||||
* **/
|
||||
fun removeIllegalChars(fileName: String): String {
|
||||
if (activePlatform is TargetPlatforms.Js) return fileName
|
||||
val illegalCharArray = charArrayOf(
|
||||
'/',
|
||||
'\n',
|
||||
|
@ -31,7 +31,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
|
||||
interface SpotiFlyerList {
|
||||
|
||||
val models: Value<State>
|
||||
val model: Value<State>
|
||||
|
||||
/*
|
||||
* Download All Tracks(after filtering already Downloaded)
|
||||
|
@ -56,7 +56,7 @@ internal class SpotiFlyerListImpl(
|
||||
.maximumCacheSize(75)
|
||||
.build<String, Picture>()
|
||||
|
||||
override val models: Value<State> = store.asValue()
|
||||
override val model: Value<State> = store.asValue()
|
||||
|
||||
override fun onDownloadAllClicked(trackList: List<TrackDetails>) {
|
||||
store.accept(Intent.StartDownloadAll(trackList))
|
||||
|
@ -28,7 +28,7 @@ import com.shabinder.database.Database
|
||||
|
||||
interface SpotiFlyerMain {
|
||||
|
||||
val models: Value<State>
|
||||
val model: Value<State>
|
||||
|
||||
val analytics: Analytics
|
||||
|
||||
|
@ -23,10 +23,7 @@ import com.shabinder.common.caching.Cache
|
||||
import com.shabinder.common.di.Picture
|
||||
import com.shabinder.common.di.utils.asValue
|
||||
import com.shabinder.common.main.SpotiFlyerMain
|
||||
import com.shabinder.common.main.SpotiFlyerMain.Dependencies
|
||||
import com.shabinder.common.main.SpotiFlyerMain.HomeCategory
|
||||
import com.shabinder.common.main.SpotiFlyerMain.Output
|
||||
import com.shabinder.common.main.SpotiFlyerMain.State
|
||||
import com.shabinder.common.main.SpotiFlyerMain.*
|
||||
import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent
|
||||
import com.shabinder.common.main.store.SpotiFlyerMainStoreProvider
|
||||
import com.shabinder.common.main.store.getStore
|
||||
@ -55,7 +52,7 @@ internal class SpotiFlyerMainImpl(
|
||||
.maximumCacheSize(25)
|
||||
.build<String, Picture>()
|
||||
|
||||
override val models: Value<State> = store.asValue()
|
||||
override val model: Value<State> = store.asValue()
|
||||
|
||||
override val analytics = mainAnalytics
|
||||
|
||||
|
@ -27,24 +27,12 @@ import com.arkivanov.decompose.extensions.compose.jetbrains.rememberRootComponen
|
||||
import com.arkivanov.mvikotlin.core.lifecycle.LifecycleRegistry
|
||||
import com.arkivanov.mvikotlin.core.lifecycle.resume
|
||||
import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory
|
||||
import com.shabinder.common.di.Dir
|
||||
import com.shabinder.common.di.DownloadProgressFlow
|
||||
import com.shabinder.common.di.FetchPlatformQueryResult
|
||||
import com.shabinder.common.di.firstLaunchDone
|
||||
import com.shabinder.common.di.initKoin
|
||||
import com.shabinder.common.di.isFirstLaunch
|
||||
import com.shabinder.common.di.isInternetAccessible
|
||||
import com.shabinder.common.di.setDownloadDirectory
|
||||
import com.shabinder.common.di.toggleAnalytics
|
||||
import com.shabinder.common.di.*
|
||||
import com.shabinder.common.models.Actions
|
||||
import com.shabinder.common.models.PlatformActions
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.common.root.SpotiFlyerRoot
|
||||
import com.shabinder.common.uikit.SpotiFlyerColors
|
||||
import com.shabinder.common.uikit.SpotiFlyerRootContent
|
||||
import com.shabinder.common.uikit.SpotiFlyerShapes
|
||||
import com.shabinder.common.uikit.SpotiFlyerTypography
|
||||
import com.shabinder.common.uikit.colorOffWhite
|
||||
import com.shabinder.common.uikit.*
|
||||
import com.shabinder.database.Database
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.piwik.java.tracking.PiwikTracker
|
||||
@ -58,7 +46,7 @@ import javax.swing.JFileChooser.APPROVE_OPTION
|
||||
private val koin = initKoin(enableNetworkLogs = true).koin
|
||||
private lateinit var showToast: (String)->Unit
|
||||
private val tracker: PiwikTracker by lazy {
|
||||
PiwikTracker("https://kind-grasshopper-73.telebit.io/matomo/matomo.php")
|
||||
PiwikTracker("https://matomo.spotiflyer.ml/matomo.php")
|
||||
}
|
||||
|
||||
fun main() {
|
||||
|
@ -35,12 +35,6 @@ dependencies {
|
||||
implementation(MVIKotlin.coroutines)
|
||||
implementation(MVIKotlin.mvikotlinMain)
|
||||
implementation(MVIKotlin.mvikotlinLogging)
|
||||
implementation(Ktor.auth)
|
||||
implementation(Ktor.clientJs)
|
||||
implementation(Ktor.clientJson)
|
||||
implementation(Ktor.clientCore)
|
||||
implementation(Ktor.clientLogging)
|
||||
implementation(Ktor.clientSerialization)
|
||||
implementation(project(":common:root"))
|
||||
implementation(project(":common:main"))
|
||||
implementation(project(":common:list"))
|
||||
@ -48,20 +42,23 @@ dependencies {
|
||||
implementation(project(":common:data-models"))
|
||||
implementation(project(":common:dependency-injection"))
|
||||
implementation("co.touchlab:stately-common:1.1.7")
|
||||
implementation("dev.icerock.moko:parcelize:0.6.1")
|
||||
implementation("dev.icerock.moko:parcelize:0.7.0")
|
||||
// implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.1")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0") {
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.1") {
|
||||
// https://youtrack.jetbrains.com/issue/KTOR-2670
|
||||
isForce = true
|
||||
}
|
||||
implementation("org.jetbrains:kotlin-react:17.0.1-pre.148-kotlin-1.4.30")
|
||||
implementation("org.jetbrains:kotlin-react-dom:17.0.1-pre.148-kotlin-1.4.30")
|
||||
implementation("org.jetbrains:kotlin-styled:1.0.0-pre.115-kotlin-1.4.10")
|
||||
implementation("org.jetbrains:kotlin-react-router-dom:5.2.0-pre.148-kotlin-1.4.30")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-native-mt") {
|
||||
@Suppress("DEPRECATION")
|
||||
isForce = true
|
||||
}
|
||||
implementation("org.jetbrains.kotlin-wrappers:kotlin-react:17.0.2-pre.213-kotlin-1.5.10")
|
||||
implementation("org.jetbrains.kotlin-wrappers:kotlin-react-dom:17.0.2-pre.213-kotlin-1.5.10")
|
||||
implementation("org.jetbrains.kotlin-wrappers:kotlin-styled:5.3.0-pre.213-kotlin-1.5.10")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
js {
|
||||
js(IR) {
|
||||
//useCommonJs()
|
||||
browser {
|
||||
webpackTask {
|
||||
@ -77,5 +74,6 @@ kotlin {
|
||||
}
|
||||
}
|
||||
}
|
||||
binaries.executable()
|
||||
}
|
||||
}
|
@ -28,11 +28,7 @@ import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.common.root.SpotiFlyerRoot
|
||||
import com.shabinder.database.Database
|
||||
import extras.renderableChild
|
||||
import react.RBuilder
|
||||
import react.RComponent
|
||||
import react.RProps
|
||||
import react.RState
|
||||
import react.ReactElement
|
||||
import react.*
|
||||
import root.RootR
|
||||
|
||||
external interface AppProps : RProps {
|
||||
@ -46,6 +42,10 @@ fun RBuilder.App(attrs: AppProps.() -> Unit): ReactElement {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED", "NON_EXPORTABLE_TYPE")
|
||||
@OptIn(ExperimentalJsExport::class)
|
||||
@JsExport
|
||||
class App(props: AppProps): RComponent<AppProps, RState>(props) {
|
||||
|
||||
private val lifecycle = LifecycleRegistry()
|
||||
|
@ -17,53 +17,63 @@
|
||||
package extras
|
||||
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import com.arkivanov.decompose.value.ValueObserver
|
||||
import react.RComponent
|
||||
import react.RProps
|
||||
import react.RState
|
||||
import react.setState
|
||||
|
||||
abstract class RenderableComponent<
|
||||
T : Any,
|
||||
S : Any
|
||||
>(
|
||||
|
||||
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED", "NON_EXPORTABLE_TYPE")
|
||||
@OptIn(ExperimentalJsExport::class)
|
||||
@JsExport
|
||||
abstract class RenderableComponent<T: Any, S: RState>(
|
||||
props: Props<T>,
|
||||
initialState: S
|
||||
) : RComponent<RenderableComponent.Props<T>, RenderableComponent.State<S>>(props) {
|
||||
) : RComponent<Props<T>, S>(props) {
|
||||
|
||||
protected abstract val stateFlow: Value<S>
|
||||
protected val model: T get() = props.model
|
||||
protected var scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
||||
private val subscriptions = ArrayList<Subscription<*>>()
|
||||
protected val component: T get() = props.component
|
||||
|
||||
init {
|
||||
state = State(data = initialState)
|
||||
state = initialState
|
||||
}
|
||||
|
||||
override fun componentDidMount() {
|
||||
if(!scope.isActive)
|
||||
scope = CoroutineScope(Dispatchers.Default)
|
||||
scope.launch {
|
||||
stateFlow.subscribe {
|
||||
setState { data = it }
|
||||
}
|
||||
}
|
||||
subscriptions.forEach { subscribe(it) }
|
||||
}
|
||||
|
||||
private fun <T : Any> subscribe(subscription: Subscription<T>) {
|
||||
subscription.value.subscribe(subscription.observer)
|
||||
}
|
||||
|
||||
override fun componentWillUnmount() {
|
||||
scope.cancel("Component Unmounted")
|
||||
subscriptions.forEach { unsubscribe(it) }
|
||||
}
|
||||
|
||||
interface Props<T : Any> : RProps {
|
||||
var model: T
|
||||
private fun <T : Any> unsubscribe(subscription: Subscription<T>) {
|
||||
subscription.value.unsubscribe(subscription.observer)
|
||||
}
|
||||
|
||||
class State<S>(
|
||||
var data: S
|
||||
):RState
|
||||
protected fun <T : Any> Value<T>.bindToState(buildState: S.(T) -> Unit) {
|
||||
subscriptions += Subscription(this) { data -> setState { buildState(data) } }
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected class Subscription<T : Any>(
|
||||
val value: Value<T>,
|
||||
val observer: ValueObserver<T>
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED", "NON_EXPORTABLE_TYPE")
|
||||
@OptIn(ExperimentalJsExport::class)
|
||||
@JsExport
|
||||
class RStateWrapper<T>(
|
||||
var model: T
|
||||
) : RState
|
||||
|
||||
external interface Props<T : Any> : RProps {
|
||||
var component: T
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
/*
|
||||
* * Copyright (c) 2021 Shabinder Singh
|
||||
* * This program is free software: you can redistribute it and/or modify
|
||||
* * it under the terms of the GNU General Public License as published by
|
||||
* * the Free Software Foundation, either version 3 of the License, or
|
||||
* * (at your option) any later version.
|
||||
* *
|
||||
* * This program is distributed in the hope that it will be useful,
|
||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* * GNU General Public License for more details.
|
||||
* *
|
||||
* * You should have received a copy of the GNU General Public License
|
||||
* * along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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.isActive
|
||||
import react.RComponent
|
||||
import react.RProps
|
||||
import react.RState
|
||||
import react.setState
|
||||
|
||||
abstract class RenderableRootComponent<
|
||||
T : Any,
|
||||
S : RState
|
||||
>(
|
||||
props: Props<T>,
|
||||
initialState: S
|
||||
) : RComponent<Props<T>, S>(props) {
|
||||
|
||||
protected val model: T get() = props.model
|
||||
private val subscriptions = ArrayList<Subscription<*>>()
|
||||
protected var scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
||||
|
||||
init {
|
||||
this.state = initialState
|
||||
}
|
||||
|
||||
override fun componentDidMount() {
|
||||
subscriptions.forEach { subscribe(it) }
|
||||
if(!scope.isActive)
|
||||
scope = CoroutineScope(Dispatchers.Default)
|
||||
}
|
||||
|
||||
private fun <T : Any> subscribe(subscription: Subscription<T>) {
|
||||
subscription.value.subscribe(subscription.observer)
|
||||
}
|
||||
|
||||
override fun componentWillUnmount() {
|
||||
subscriptions.forEach { unsubscribe(it) }
|
||||
scope.cancel("Component Unmounted")
|
||||
}
|
||||
|
||||
private fun <T : Any> unsubscribe(subscription: Subscription<T>) {
|
||||
subscription.value.unsubscribe(subscription.observer)
|
||||
}
|
||||
|
||||
protected fun <T : Any> Value<T>.bindToState(buildState: S.(T) -> Unit) {
|
||||
subscriptions += Subscription(this) { data -> setState { buildState(data) } }
|
||||
}
|
||||
|
||||
interface Props<T : Any> : RProps {
|
||||
var model: T
|
||||
}
|
||||
|
||||
protected class Subscription<T : Any>(
|
||||
val value: Value<T>,
|
||||
val observer: ValueObserver<T>
|
||||
)
|
||||
}
|
@ -19,15 +19,9 @@ package extras
|
||||
import react.RBuilder
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
fun <M : Any, T : RenderableRootComponent<M, *>> RBuilder.renderableChild(clazz: KClass<out T>, model: M) {
|
||||
child(clazz) {
|
||||
key = model.uniqueId().toString()
|
||||
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
|
||||
attrs.component = model
|
||||
}
|
||||
}
|
||||
|
@ -16,21 +16,12 @@
|
||||
|
||||
package home
|
||||
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.shabinder.common.main.SpotiFlyerMain
|
||||
import com.shabinder.common.main.SpotiFlyerMain.State
|
||||
import extras.Props
|
||||
import extras.RStateWrapper
|
||||
import extras.RenderableComponent
|
||||
import kotlinx.browser.document
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.css.Align
|
||||
import kotlinx.css.Display
|
||||
import kotlinx.css.FlexDirection
|
||||
import kotlinx.css.JustifyContent
|
||||
import kotlinx.css.alignItems
|
||||
import kotlinx.css.display
|
||||
import kotlinx.css.flexDirection
|
||||
import kotlinx.css.flexGrow
|
||||
import kotlinx.css.justifyContent
|
||||
import kotlinx.css.*
|
||||
import kotlinx.dom.appendElement
|
||||
import react.RBuilder
|
||||
import styled.css
|
||||
@ -38,12 +29,20 @@ import styled.styledDiv
|
||||
|
||||
class HomeScreen(
|
||||
props: Props<SpotiFlyerMain>,
|
||||
) : RenderableComponent<SpotiFlyerMain, State>(
|
||||
) : RenderableComponent<SpotiFlyerMain, RStateWrapper<SpotiFlyerMain.State>>(
|
||||
props,
|
||||
initialState = State()
|
||||
initialState = RStateWrapper(props.component.model.value)
|
||||
) {
|
||||
|
||||
init {
|
||||
component.model.bindToState {
|
||||
model = it
|
||||
}
|
||||
}
|
||||
|
||||
override fun componentDidMount() {
|
||||
super.componentDidMount()
|
||||
// RazorPay Button
|
||||
val form = document.getElementById("razorpay-form")!!
|
||||
repeat(form.childNodes.length){
|
||||
form.childNodes.item(0)?.let { it1 -> form.removeChild(it1) }
|
||||
@ -56,8 +55,6 @@ class HomeScreen(
|
||||
}
|
||||
}
|
||||
|
||||
override val stateFlow: Value<SpotiFlyerMain.State> = model.models
|
||||
|
||||
override fun RBuilder.render() {
|
||||
styledDiv{
|
||||
css {
|
||||
@ -73,9 +70,9 @@ class HomeScreen(
|
||||
}
|
||||
|
||||
SearchBar {
|
||||
link = state.data.link
|
||||
search = model::onLinkSearch
|
||||
onLinkChange = model::onInputLinkChanged
|
||||
link = state.model.link
|
||||
search = component::onLinkSearch
|
||||
onLinkChange = component::onInputLinkChanged
|
||||
}
|
||||
|
||||
IconList {
|
||||
|
@ -17,22 +17,11 @@
|
||||
package home
|
||||
|
||||
import Styles
|
||||
import kotlinx.css.borderRadius
|
||||
import kotlinx.css.height
|
||||
import kotlinx.css.margin
|
||||
import kotlinx.css.px
|
||||
import kotlinx.css.width
|
||||
import kotlinx.css.*
|
||||
import kotlinx.html.id
|
||||
import react.RBuilder
|
||||
import react.RProps
|
||||
import react.ReactElement
|
||||
import react.child
|
||||
import react.functionalComponent
|
||||
import styled.css
|
||||
import styled.styledA
|
||||
import styled.styledDiv
|
||||
import styled.styledForm
|
||||
import styled.styledImg
|
||||
import react.*
|
||||
import react.dom.attrs
|
||||
import styled.*
|
||||
|
||||
external interface IconListProps : RProps {
|
||||
var iconsAndPlatforms: Map<String,String>
|
||||
@ -63,7 +52,7 @@ private val iconList = functionalComponent<IconListProps>("IconList") { props ->
|
||||
if(icon == firstElem && props.isBadge){
|
||||
//<form><script src="https://checkout.razorpay.com/v1/payment-button.js" data-payment_button_id="pl_GnKuuDBdBu0ank" async> </script> </form>
|
||||
styledForm {
|
||||
attrs{
|
||||
attrs {
|
||||
id = "razorpay-form"
|
||||
}
|
||||
}
|
||||
|
@ -25,12 +25,9 @@ import org.w3c.dom.HTMLInputElement
|
||||
import react.RBuilder
|
||||
import react.RProps
|
||||
import react.child
|
||||
import react.dom.attrs
|
||||
import react.functionalComponent
|
||||
import styled.css
|
||||
import styled.styledButton
|
||||
import styled.styledDiv
|
||||
import styled.styledImg
|
||||
import styled.styledInput
|
||||
import styled.*
|
||||
|
||||
external interface SearchbarProps : RProps {
|
||||
var link: String
|
||||
@ -45,8 +42,6 @@ fun RBuilder.SearchBar(handler:SearchbarProps.() -> Unit) = child(searchbar){
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
val searchbar = functionalComponent<SearchbarProps>("SearchBar"){ props ->
|
||||
styledDiv{
|
||||
css {
|
||||
|
@ -16,24 +16,10 @@
|
||||
|
||||
package list
|
||||
|
||||
import kotlinx.css.Align
|
||||
import kotlinx.css.Display
|
||||
import kotlinx.css.FlexDirection
|
||||
import kotlinx.css.TextAlign
|
||||
import kotlinx.css.alignItems
|
||||
import kotlinx.css.display
|
||||
import kotlinx.css.flexDirection
|
||||
import kotlinx.css.height
|
||||
import kotlinx.css.marginTop
|
||||
import kotlinx.css.px
|
||||
import kotlinx.css.textAlign
|
||||
import kotlinx.css.width
|
||||
import kotlinx.css.*
|
||||
import kotlinx.html.id
|
||||
import react.RBuilder
|
||||
import react.RProps
|
||||
import react.ReactElement
|
||||
import react.child
|
||||
import react.functionalComponent
|
||||
import react.*
|
||||
import react.dom.attrs
|
||||
import styled.css
|
||||
import styled.styledDiv
|
||||
import styled.styledH1
|
||||
|
@ -16,26 +16,11 @@
|
||||
|
||||
package list
|
||||
|
||||
import kotlinx.css.Align
|
||||
import kotlinx.css.Display
|
||||
import kotlinx.css.JustifyContent
|
||||
import kotlinx.css.WhiteSpace
|
||||
import kotlinx.css.alignItems
|
||||
import kotlinx.css.display
|
||||
import kotlinx.css.fontSize
|
||||
import kotlinx.css.height
|
||||
import kotlinx.css.justifyContent
|
||||
import kotlinx.css.px
|
||||
import kotlinx.css.whiteSpace
|
||||
import kotlinx.css.*
|
||||
import kotlinx.html.id
|
||||
import kotlinx.html.js.onClickFunction
|
||||
import react.RBuilder
|
||||
import react.RProps
|
||||
import react.ReactElement
|
||||
import react.child
|
||||
import react.functionalComponent
|
||||
import react.useEffect
|
||||
import react.useState
|
||||
import react.*
|
||||
import react.dom.attrs
|
||||
import styled.css
|
||||
import styled.styledDiv
|
||||
import styled.styledH5
|
||||
|
@ -17,17 +17,10 @@
|
||||
package list
|
||||
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
import kotlinx.css.borderRadius
|
||||
import kotlinx.css.em
|
||||
import kotlinx.css.margin
|
||||
import kotlinx.css.px
|
||||
import kotlinx.css.width
|
||||
import kotlinx.css.*
|
||||
import kotlinx.html.js.onClickFunction
|
||||
import react.RBuilder
|
||||
import react.RProps
|
||||
import react.ReactElement
|
||||
import react.child
|
||||
import react.functionalComponent
|
||||
import react.*
|
||||
import react.dom.attrs
|
||||
import styled.css
|
||||
import styled.styledDiv
|
||||
import styled.styledImg
|
||||
|
@ -16,55 +16,55 @@
|
||||
|
||||
package list
|
||||
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.shabinder.common.list.SpotiFlyerList
|
||||
import com.shabinder.common.list.SpotiFlyerList.State
|
||||
import extras.Props
|
||||
import extras.RStateWrapper
|
||||
import extras.RenderableComponent
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.css.Color
|
||||
import kotlinx.css.Display
|
||||
import kotlinx.css.FlexDirection
|
||||
import kotlinx.css.color
|
||||
import kotlinx.css.display
|
||||
import kotlinx.css.flexDirection
|
||||
import kotlinx.css.flexGrow
|
||||
import kotlinx.css.padding
|
||||
import kotlinx.css.px
|
||||
import kotlinx.css.*
|
||||
import kotlinx.html.id
|
||||
import react.RBuilder
|
||||
import react.dom.attrs
|
||||
import styled.css
|
||||
import styled.styledDiv
|
||||
import styled.styledSection
|
||||
|
||||
|
||||
class ListScreen(
|
||||
props: Props<SpotiFlyerList>,
|
||||
) : RenderableComponent<SpotiFlyerList, State>(props,initialState = State()) {
|
||||
|
||||
override val stateFlow: Value<SpotiFlyerList.State> = model.models
|
||||
) : RenderableComponent<SpotiFlyerList, RStateWrapper<State>>(
|
||||
props,
|
||||
initialState = RStateWrapper(props.component.model.value)
|
||||
) {
|
||||
init {
|
||||
component.model.bindToState {
|
||||
model = it
|
||||
}
|
||||
}
|
||||
|
||||
override fun RBuilder.render() {
|
||||
|
||||
val result = state.data.queryResult
|
||||
val queryResult = state.model.queryResult
|
||||
|
||||
styledSection {
|
||||
attrs {
|
||||
id = "list-screen"
|
||||
}
|
||||
|
||||
if(result == null) {
|
||||
if(queryResult == null) {
|
||||
LoadingAnim { }
|
||||
}else {
|
||||
CoverImage {
|
||||
coverImageURL = result.coverUrl
|
||||
coverName = result.title
|
||||
coverImageURL = queryResult.coverUrl
|
||||
coverName = queryResult.title
|
||||
}
|
||||
|
||||
DownloadAllButton {
|
||||
isActive = state.data.trackList.size > 1
|
||||
isActive = state.model.trackList.size > 1
|
||||
downloadAll = {
|
||||
model.onDownloadAllClicked(state.data.trackList)
|
||||
component.onDownloadAllClicked(state.model.trackList)
|
||||
}
|
||||
link = state.data.link
|
||||
link = state.model.link
|
||||
}
|
||||
|
||||
styledDiv {
|
||||
@ -74,10 +74,10 @@ class ListScreen(
|
||||
flexDirection = FlexDirection.column
|
||||
color = Color.white
|
||||
}
|
||||
state.data.trackList.forEachIndexed{ _, trackDetails ->
|
||||
state.model.trackList.forEachIndexed{ _, trackDetails ->
|
||||
TrackItem {
|
||||
details = trackDetails
|
||||
downloadTrack = model::onDownloadClicked
|
||||
downloadTrack = component::onDownloadClicked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,43 +18,11 @@ package list
|
||||
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import kotlinx.css.Align
|
||||
import kotlinx.css.Display
|
||||
import kotlinx.css.FlexDirection
|
||||
import kotlinx.css.Overflow
|
||||
import kotlinx.css.TextAlign
|
||||
import kotlinx.css.TextOverflow
|
||||
import kotlinx.css.WhiteSpace
|
||||
import kotlinx.css.alignItems
|
||||
import kotlinx.css.display
|
||||
import kotlinx.css.em
|
||||
import kotlinx.css.flexDirection
|
||||
import kotlinx.css.flexGrow
|
||||
import kotlinx.css.fontSize
|
||||
import kotlinx.css.height
|
||||
import kotlinx.css.margin
|
||||
import kotlinx.css.minWidth
|
||||
import kotlinx.css.overflow
|
||||
import kotlinx.css.padding
|
||||
import kotlinx.css.paddingRight
|
||||
import kotlinx.css.px
|
||||
import kotlinx.css.textAlign
|
||||
import kotlinx.css.textOverflow
|
||||
import kotlinx.css.whiteSpace
|
||||
import kotlinx.css.width
|
||||
import kotlinx.css.*
|
||||
import kotlinx.html.id
|
||||
import react.RBuilder
|
||||
import react.RProps
|
||||
import react.ReactElement
|
||||
import react.child
|
||||
import react.functionalComponent
|
||||
import react.useEffect
|
||||
import react.useState
|
||||
import styled.css
|
||||
import styled.styledDiv
|
||||
import styled.styledH3
|
||||
import styled.styledH4
|
||||
import styled.styledImg
|
||||
import react.*
|
||||
import react.dom.attrs
|
||||
import styled.*
|
||||
|
||||
external interface TrackItemProps : RProps {
|
||||
var details:TrackDetails
|
||||
|
@ -16,33 +16,13 @@
|
||||
|
||||
package navbar
|
||||
|
||||
import kotlinx.css.Align
|
||||
import kotlinx.css.Display
|
||||
import kotlinx.css.LinearDimension
|
||||
import kotlinx.css.alignItems
|
||||
import kotlinx.css.display
|
||||
import kotlinx.css.filter
|
||||
import kotlinx.css.fontSize
|
||||
import kotlinx.css.height
|
||||
import kotlinx.css.margin
|
||||
import kotlinx.css.marginLeft
|
||||
import kotlinx.css.marginRight
|
||||
import kotlinx.css.px
|
||||
import kotlinx.css.width
|
||||
import kotlinx.css.*
|
||||
import kotlinx.html.id
|
||||
import kotlinx.html.js.onBlurFunction
|
||||
import kotlinx.html.js.onClickFunction
|
||||
import react.RBuilder
|
||||
import react.RProps
|
||||
import react.ReactElement
|
||||
import react.child
|
||||
import react.functionalComponent
|
||||
import styled.css
|
||||
import styled.styledA
|
||||
import styled.styledDiv
|
||||
import styled.styledH1
|
||||
import styled.styledImg
|
||||
import styled.styledNav
|
||||
import react.*
|
||||
import react.dom.attrs
|
||||
import styled.*
|
||||
|
||||
@Suppress("FunctionName")
|
||||
fun RBuilder.NavBar(handler: NavBarProps.() -> Unit): ReactElement{
|
||||
|
@ -19,7 +19,8 @@ package root
|
||||
import com.arkivanov.decompose.RouterState
|
||||
import com.shabinder.common.root.SpotiFlyerRoot
|
||||
import com.shabinder.common.root.SpotiFlyerRoot.Child
|
||||
import extras.RenderableRootComponent
|
||||
import extras.Props
|
||||
import extras.RenderableComponent
|
||||
import extras.renderableChild
|
||||
import home.HomeScreen
|
||||
import list.ListScreen
|
||||
@ -27,31 +28,34 @@ import navbar.NavBar
|
||||
import react.RBuilder
|
||||
import react.RState
|
||||
|
||||
class RootR(props: Props<SpotiFlyerRoot>) : RenderableRootComponent<SpotiFlyerRoot, RootR.State>(
|
||||
class RootR(props: Props<SpotiFlyerRoot>) : RenderableComponent<SpotiFlyerRoot, State>(
|
||||
props = props,
|
||||
initialState = State(routerState = props.model.routerState.value)
|
||||
initialState = State(routerState = props.component.routerState.value)
|
||||
) {
|
||||
private val component: Child
|
||||
get() = model.routerState.value.activeChild.instance
|
||||
private val child: Child
|
||||
get() = component.routerState.value.activeChild.instance
|
||||
|
||||
private val callBacks get() = model.callBacks
|
||||
private val callBacks get() = component.callBacks
|
||||
|
||||
init {
|
||||
component.routerState.bindToState { routerState = it }
|
||||
}
|
||||
|
||||
override fun RBuilder.render() {
|
||||
NavBar {
|
||||
isBackVisible = (component is Child.List)
|
||||
isBackVisible = (child is Child.List)
|
||||
popBackToHomeScreen = callBacks::popBackToHomeScreen
|
||||
}
|
||||
when(component){
|
||||
is Child.Main -> renderableChild(HomeScreen::class, (component as Child.Main).component)
|
||||
is Child.List -> renderableChild(ListScreen::class, (component as Child.List).component)
|
||||
when(child){
|
||||
is Child.Main -> renderableChild(HomeScreen::class, (child as Child.Main).component)
|
||||
is Child.List -> renderableChild(ListScreen::class, (child as Child.List).component)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
model.routerState.bindToState { routerState = it }
|
||||
}
|
||||
class State(
|
||||
var routerState: RouterState<*, Child>
|
||||
) : RState
|
||||
|
||||
}
|
||||
|
||||
@Suppress("NON_EXPORTABLE_TYPE", "EXPERIMENTAL_IS_NOT_ENABLED")
|
||||
@OptIn(ExperimentalJsExport::class)
|
||||
@JsExport
|
||||
class State(
|
||||
var routerState: RouterState<*, Child>
|
||||
) : RState
|
||||
|
Loading…
Reference in New Issue
Block a user