mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-22 01:04:31 +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("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.github.shabinder:storage-chooser:2.0.4.45")
|
||||||
implementation("com.google.accompanist:accompanist-insets:0.11.1")
|
implementation("com.google.accompanist:accompanist-insets:0.11.1")
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ class App: Application(), KoinComponent {
|
|||||||
|
|
||||||
val tracker: Tracker by lazy {
|
val tracker: Tracker by lazy {
|
||||||
TrackerBuilder.createDefault(
|
TrackerBuilder.createDefault(
|
||||||
"https://kind-grasshopper-73.telebit.io/matomo/matomo.php", 1)
|
"https://matomo.spotiflyer.ml/matomo.php", 1)
|
||||||
.build(Matomo.getInstance(this)).apply {
|
.build(Matomo.getInstance(this)).apply {
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
/*Timber.plant(DebugTree())
|
/*Timber.plant(DebugTree())
|
||||||
@ -85,7 +85,7 @@ class App: Application(), KoinComponent {
|
|||||||
}
|
}
|
||||||
// Send Crash Report to self hosted Acrarium (FOSS)
|
// Send Crash Report to self hosted Acrarium (FOSS)
|
||||||
httpSender {
|
httpSender {
|
||||||
uri = "https://kind-grasshopper-73.telebit.io/acrarium/report"
|
uri = "https://acrarium.spotiflyer.ml/report"
|
||||||
basicAuthLogin = "sDj2xCKQIxw0dujf"
|
basicAuthLogin = "sDj2xCKQIxw0dujf"
|
||||||
basicAuthPassword = "O83du0TsgsDJ69zN"
|
basicAuthPassword = "O83du0TsgsDJ69zN"
|
||||||
httpMethod = HttpSender.Method.POST
|
httpMethod = HttpSender.Method.POST
|
||||||
|
@ -42,7 +42,7 @@ kotlin {
|
|||||||
implementation(compose.animation)
|
implementation(compose.animation)
|
||||||
|
|
||||||
implementation(Extras.kermit)
|
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") {
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-native-mt") {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
isForce = true
|
isForce = true
|
||||||
|
@ -34,7 +34,7 @@ kotlin {
|
|||||||
jvm("desktop")
|
jvm("desktop")
|
||||||
android()
|
android()
|
||||||
|
|
||||||
js(/*BOTH*/) {
|
js(BOTH) {
|
||||||
browser()
|
browser()
|
||||||
// nodejs()
|
// nodejs()
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ kotlin {
|
|||||||
jvm("desktop")
|
jvm("desktop")
|
||||||
android()
|
android()
|
||||||
|
|
||||||
js(/*BOTH*/) {
|
js(BOTH) {
|
||||||
browser()
|
browser()
|
||||||
// nodejs()
|
// nodejs()
|
||||||
}
|
}
|
||||||
@ -65,7 +65,7 @@ kotlin {
|
|||||||
implementation(Extras.kermit)
|
implementation(Extras.kermit)
|
||||||
implementation(Serialization.json)
|
implementation(Serialization.json)
|
||||||
implementation("co.touchlab:stately-common:1.1.7")
|
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") {
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-native-mt") {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
isForce = true
|
isForce = true
|
||||||
@ -102,9 +102,9 @@ kotlin {
|
|||||||
named("jsMain") {
|
named("jsMain") {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(Ktor.clientJs)
|
implementation(Ktor.clientJs)
|
||||||
implementation("org.jetbrains:kotlin-react: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-styled:1.0.0-pre.115-kotlin-1.4.10")
|
implementation("org.jetbrains.kotlin-wrappers:kotlin-react-dom:17.0.2-pre.213-kotlin-1.5.10")
|
||||||
implementation("org.jetbrains:kotlin-react-dom:17.0.1-pre.148-kotlin-1.4.30")
|
implementation("org.jetbrains.kotlin-wrappers:kotlin-styled:5.3.0-pre.213-kotlin-1.5.10")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(HostOS.isMac){
|
if(HostOS.isMac){
|
||||||
|
@ -17,32 +17,12 @@
|
|||||||
package com.shabinder.common.uikit
|
package com.shabinder.common.uikit
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.*
|
||||||
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.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.material.CircularProgressIndicator
|
import androidx.compose.material.*
|
||||||
import androidx.compose.material.ExperimentalMaterialApi
|
import androidx.compose.runtime.*
|
||||||
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.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
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.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
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.di.Picture
|
||||||
import com.shabinder.common.list.SpotiFlyerList
|
import com.shabinder.common.list.SpotiFlyerList
|
||||||
import com.shabinder.common.models.DownloadStatus
|
import com.shabinder.common.models.DownloadStatus
|
||||||
@ -64,7 +44,7 @@ fun SpotiFlyerListContent(
|
|||||||
component: SpotiFlyerList,
|
component: SpotiFlyerList,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val model by component.models.asState()
|
val model by component.model.subscribeAsState()
|
||||||
|
|
||||||
LaunchedEffect(model.errorOccurred) {
|
LaunchedEffect(model.errorOccurred) {
|
||||||
/*Handle if Any Exception Occurred*/
|
/*Handle if Any Exception Occurred*/
|
||||||
|
@ -17,57 +17,21 @@
|
|||||||
package com.shabinder.common.uikit
|
package com.shabinder.common.uikit
|
||||||
|
|
||||||
import androidx.compose.animation.Crossfade
|
import androidx.compose.animation.Crossfade
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.*
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.layout.*
|
||||||
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.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.rememberScrollState
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.material.*
|
||||||
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.TabRowDefaults.tabIndicatorOffset
|
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.TextFieldDefaults.textFieldColors
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.History
|
import androidx.compose.material.icons.outlined.History
|
||||||
import androidx.compose.material.icons.outlined.Info
|
import androidx.compose.material.icons.outlined.Info
|
||||||
import androidx.compose.material.icons.rounded.CardGiftcard
|
import androidx.compose.material.icons.rounded.*
|
||||||
import androidx.compose.material.icons.rounded.Edit
|
import androidx.compose.runtime.*
|
||||||
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.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
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.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
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.di.Picture
|
||||||
import com.shabinder.common.main.SpotiFlyerMain
|
import com.shabinder.common.main.SpotiFlyerMain
|
||||||
import com.shabinder.common.main.SpotiFlyerMain.HomeCategory
|
import com.shabinder.common.main.SpotiFlyerMain.HomeCategory
|
||||||
@ -89,7 +53,7 @@ import com.shabinder.common.models.methods
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SpotiFlyerMainContent(component: SpotiFlyerMain) {
|
fun SpotiFlyerMainContent(component: SpotiFlyerMain) {
|
||||||
val model by component.models.asState()
|
val model by component.model.subscribeAsState()
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
SearchPanel(
|
SearchPanel(
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
package com.shabinder.common.models
|
package com.shabinder.common.models
|
||||||
|
|
||||||
sealed class CorsProxy(open val url: String) {
|
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)
|
data class PublicProxyWithExtension(override val url: String = "https://cors.bridged.cc/") : CorsProxy(url)
|
||||||
|
|
||||||
fun toggle(mode: CorsProxy? = null): CorsProxy {
|
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.currentPlatform
|
||||||
import com.shabinder.common.di.youtubeMp3.Yt1sMp3
|
import com.shabinder.common.di.youtubeMp3.Yt1sMp3
|
||||||
import com.shabinder.common.models.AllPlatforms
|
import com.shabinder.common.models.AllPlatforms
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.*
|
||||||
|
|
||||||
class YoutubeMp3(
|
class YoutubeMp3(
|
||||||
override val httpClient: HttpClient,
|
override val httpClient: HttpClient,
|
||||||
@ -33,7 +33,7 @@ class YoutubeMp3(
|
|||||||
getLinkFromYt1sMp3(videoID)?.let {
|
getLinkFromYt1sMp3(videoID)?.let {
|
||||||
logger.i { "Download Link: $it" }
|
logger.i { "Download Link: $it" }
|
||||||
if (currentPlatform is AllPlatforms.Js/* && corsProxy !is CorsProxy.PublicProxyWithExtension*/)
|
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
|
// "https://spotiflyer.azurewebsites.net/$it" // Data OUT Limit issue
|
||||||
else it
|
else it
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ import io.github.shabinder.YoutubeDownloader
|
|||||||
import io.github.shabinder.models.YoutubeVideo
|
import io.github.shabinder.models.YoutubeVideo
|
||||||
import io.github.shabinder.models.formats.Format
|
import io.github.shabinder.models.formats.Format
|
||||||
import io.github.shabinder.models.quality.AudioQuality
|
import io.github.shabinder.models.quality.AudioQuality
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.*
|
||||||
|
|
||||||
class YoutubeProvider(
|
class YoutubeProvider(
|
||||||
private val httpClient: HttpClient,
|
private val httpClient: HttpClient,
|
||||||
@ -37,7 +37,7 @@ class YoutubeProvider(
|
|||||||
) {
|
) {
|
||||||
val ytDownloader: YoutubeDownloader = YoutubeDownloader(
|
val ytDownloader: YoutubeDownloader = YoutubeDownloader(
|
||||||
enableCORSProxy = true,
|
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)
|
val video = ytDownloader.getVideo(searchId)
|
||||||
coverUrl = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg"
|
coverUrl = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg"
|
||||||
val detail = video.videoDetails
|
val detail = video.videoDetails
|
||||||
val name = detail.title?.replace(detail.author?.toUpperCase() ?: "", "", true)
|
val name = detail.title?.replace(detail.author?.uppercase() ?: "", "", true)
|
||||||
?: detail.title ?: ""
|
?: detail.title ?: ""
|
||||||
// logger.i{ detail.toString() }
|
// logger.i{ detail.toString() }
|
||||||
trackList = listOf(
|
trackList = listOf(
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.shabinder.common.di.utils
|
package com.shabinder.common.di.utils
|
||||||
|
|
||||||
|
import io.github.shabinder.TargetPlatforms
|
||||||
|
import io.github.shabinder.activePlatform
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlin.native.concurrent.ThreadLocal
|
import kotlin.native.concurrent.ThreadLocal
|
||||||
|
|
||||||
@ -31,6 +33,7 @@ val json by lazy {
|
|||||||
* Removing Illegal Chars from File Name
|
* Removing Illegal Chars from File Name
|
||||||
* **/
|
* **/
|
||||||
fun removeIllegalChars(fileName: String): String {
|
fun removeIllegalChars(fileName: String): String {
|
||||||
|
if (activePlatform is TargetPlatforms.Js) return fileName
|
||||||
val illegalCharArray = charArrayOf(
|
val illegalCharArray = charArrayOf(
|
||||||
'/',
|
'/',
|
||||||
'\n',
|
'\n',
|
||||||
|
@ -31,7 +31,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
|||||||
|
|
||||||
interface SpotiFlyerList {
|
interface SpotiFlyerList {
|
||||||
|
|
||||||
val models: Value<State>
|
val model: Value<State>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Download All Tracks(after filtering already Downloaded)
|
* Download All Tracks(after filtering already Downloaded)
|
||||||
|
@ -56,7 +56,7 @@ internal class SpotiFlyerListImpl(
|
|||||||
.maximumCacheSize(75)
|
.maximumCacheSize(75)
|
||||||
.build<String, Picture>()
|
.build<String, Picture>()
|
||||||
|
|
||||||
override val models: Value<State> = store.asValue()
|
override val model: Value<State> = store.asValue()
|
||||||
|
|
||||||
override fun onDownloadAllClicked(trackList: List<TrackDetails>) {
|
override fun onDownloadAllClicked(trackList: List<TrackDetails>) {
|
||||||
store.accept(Intent.StartDownloadAll(trackList))
|
store.accept(Intent.StartDownloadAll(trackList))
|
||||||
|
@ -28,7 +28,7 @@ import com.shabinder.database.Database
|
|||||||
|
|
||||||
interface SpotiFlyerMain {
|
interface SpotiFlyerMain {
|
||||||
|
|
||||||
val models: Value<State>
|
val model: Value<State>
|
||||||
|
|
||||||
val analytics: Analytics
|
val analytics: Analytics
|
||||||
|
|
||||||
|
@ -23,10 +23,7 @@ import com.shabinder.common.caching.Cache
|
|||||||
import com.shabinder.common.di.Picture
|
import com.shabinder.common.di.Picture
|
||||||
import com.shabinder.common.di.utils.asValue
|
import com.shabinder.common.di.utils.asValue
|
||||||
import com.shabinder.common.main.SpotiFlyerMain
|
import com.shabinder.common.main.SpotiFlyerMain
|
||||||
import com.shabinder.common.main.SpotiFlyerMain.Dependencies
|
import com.shabinder.common.main.SpotiFlyerMain.*
|
||||||
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.store.SpotiFlyerMainStore.Intent
|
import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent
|
||||||
import com.shabinder.common.main.store.SpotiFlyerMainStoreProvider
|
import com.shabinder.common.main.store.SpotiFlyerMainStoreProvider
|
||||||
import com.shabinder.common.main.store.getStore
|
import com.shabinder.common.main.store.getStore
|
||||||
@ -55,7 +52,7 @@ internal class SpotiFlyerMainImpl(
|
|||||||
.maximumCacheSize(25)
|
.maximumCacheSize(25)
|
||||||
.build<String, Picture>()
|
.build<String, Picture>()
|
||||||
|
|
||||||
override val models: Value<State> = store.asValue()
|
override val model: Value<State> = store.asValue()
|
||||||
|
|
||||||
override val analytics = mainAnalytics
|
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.LifecycleRegistry
|
||||||
import com.arkivanov.mvikotlin.core.lifecycle.resume
|
import com.arkivanov.mvikotlin.core.lifecycle.resume
|
||||||
import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory
|
import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory
|
||||||
import com.shabinder.common.di.Dir
|
import com.shabinder.common.di.*
|
||||||
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.models.Actions
|
import com.shabinder.common.models.Actions
|
||||||
import com.shabinder.common.models.PlatformActions
|
import com.shabinder.common.models.PlatformActions
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
import com.shabinder.common.root.SpotiFlyerRoot
|
import com.shabinder.common.root.SpotiFlyerRoot
|
||||||
import com.shabinder.common.uikit.SpotiFlyerColors
|
import com.shabinder.common.uikit.*
|
||||||
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.database.Database
|
import com.shabinder.database.Database
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.piwik.java.tracking.PiwikTracker
|
import org.piwik.java.tracking.PiwikTracker
|
||||||
@ -58,7 +46,7 @@ import javax.swing.JFileChooser.APPROVE_OPTION
|
|||||||
private val koin = initKoin(enableNetworkLogs = true).koin
|
private val koin = initKoin(enableNetworkLogs = true).koin
|
||||||
private lateinit var showToast: (String)->Unit
|
private lateinit var showToast: (String)->Unit
|
||||||
private val tracker: PiwikTracker by lazy {
|
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() {
|
fun main() {
|
||||||
|
@ -35,12 +35,6 @@ dependencies {
|
|||||||
implementation(MVIKotlin.coroutines)
|
implementation(MVIKotlin.coroutines)
|
||||||
implementation(MVIKotlin.mvikotlinMain)
|
implementation(MVIKotlin.mvikotlinMain)
|
||||||
implementation(MVIKotlin.mvikotlinLogging)
|
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:root"))
|
||||||
implementation(project(":common:main"))
|
implementation(project(":common:main"))
|
||||||
implementation(project(":common:list"))
|
implementation(project(":common:list"))
|
||||||
@ -48,20 +42,23 @@ dependencies {
|
|||||||
implementation(project(":common:data-models"))
|
implementation(project(":common:data-models"))
|
||||||
implementation(project(":common:dependency-injection"))
|
implementation(project(":common:dependency-injection"))
|
||||||
implementation("co.touchlab:stately-common:1.1.7")
|
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.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
|
// https://youtrack.jetbrains.com/issue/KTOR-2670
|
||||||
isForce = true
|
isForce = true
|
||||||
}
|
}
|
||||||
implementation("org.jetbrains:kotlin-react:17.0.1-pre.148-kotlin-1.4.30")
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-native-mt") {
|
||||||
implementation("org.jetbrains:kotlin-react-dom:17.0.1-pre.148-kotlin-1.4.30")
|
@Suppress("DEPRECATION")
|
||||||
implementation("org.jetbrains:kotlin-styled:1.0.0-pre.115-kotlin-1.4.10")
|
isForce = true
|
||||||
implementation("org.jetbrains:kotlin-react-router-dom:5.2.0-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")
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
js {
|
js(IR) {
|
||||||
//useCommonJs()
|
//useCommonJs()
|
||||||
browser {
|
browser {
|
||||||
webpackTask {
|
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.common.root.SpotiFlyerRoot
|
||||||
import com.shabinder.database.Database
|
import com.shabinder.database.Database
|
||||||
import extras.renderableChild
|
import extras.renderableChild
|
||||||
import react.RBuilder
|
import react.*
|
||||||
import react.RComponent
|
|
||||||
import react.RProps
|
|
||||||
import react.RState
|
|
||||||
import react.ReactElement
|
|
||||||
import root.RootR
|
import root.RootR
|
||||||
|
|
||||||
external interface AppProps : RProps {
|
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) {
|
class App(props: AppProps): RComponent<AppProps, RState>(props) {
|
||||||
|
|
||||||
private val lifecycle = LifecycleRegistry()
|
private val lifecycle = LifecycleRegistry()
|
||||||
|
@ -17,53 +17,63 @@
|
|||||||
package extras
|
package extras
|
||||||
|
|
||||||
import com.arkivanov.decompose.value.Value
|
import com.arkivanov.decompose.value.Value
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import com.arkivanov.decompose.value.ValueObserver
|
||||||
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 react.RComponent
|
import react.RComponent
|
||||||
import react.RProps
|
import react.RProps
|
||||||
import react.RState
|
import react.RState
|
||||||
import react.setState
|
import react.setState
|
||||||
|
|
||||||
abstract class RenderableComponent<
|
|
||||||
T : Any,
|
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED", "NON_EXPORTABLE_TYPE")
|
||||||
S : Any
|
@OptIn(ExperimentalJsExport::class)
|
||||||
>(
|
@JsExport
|
||||||
|
abstract class RenderableComponent<T: Any, S: RState>(
|
||||||
props: Props<T>,
|
props: Props<T>,
|
||||||
initialState: S
|
initialState: S
|
||||||
) : RComponent<RenderableComponent.Props<T>, RenderableComponent.State<S>>(props) {
|
) : RComponent<Props<T>, S>(props) {
|
||||||
|
|
||||||
protected abstract val stateFlow: Value<S>
|
private val subscriptions = ArrayList<Subscription<*>>()
|
||||||
protected val model: T get() = props.model
|
protected val component: T get() = props.component
|
||||||
protected var scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
state = State(data = initialState)
|
state = initialState
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun componentDidMount() {
|
override fun componentDidMount() {
|
||||||
if(!scope.isActive)
|
subscriptions.forEach { subscribe(it) }
|
||||||
scope = CoroutineScope(Dispatchers.Default)
|
}
|
||||||
scope.launch {
|
|
||||||
stateFlow.subscribe {
|
private fun <T : Any> subscribe(subscription: Subscription<T>) {
|
||||||
setState { data = it }
|
subscription.value.subscribe(subscription.observer)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun componentWillUnmount() {
|
override fun componentWillUnmount() {
|
||||||
scope.cancel("Component Unmounted")
|
subscriptions.forEach { unsubscribe(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props<T : Any> : RProps {
|
private fun <T : Any> unsubscribe(subscription: Subscription<T>) {
|
||||||
var model: T
|
subscription.value.unsubscribe(subscription.observer)
|
||||||
}
|
}
|
||||||
|
|
||||||
class State<S>(
|
protected fun <T : Any> Value<T>.bindToState(buildState: S.(T) -> Unit) {
|
||||||
var data: S
|
subscriptions += Subscription(this) { data -> setState { buildState(data) } }
|
||||||
):RState
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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 react.RBuilder
|
||||||
import kotlin.reflect.KClass
|
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) {
|
fun <M : Any, T : RenderableComponent<M, *>> RBuilder.renderableChild(clazz: KClass<out T>, model: M) {
|
||||||
child(clazz) {
|
child(clazz) {
|
||||||
key = model.uniqueId().toString()
|
key = model.uniqueId().toString()
|
||||||
attrs.model = model
|
attrs.component = model
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,21 +16,12 @@
|
|||||||
|
|
||||||
package home
|
package home
|
||||||
|
|
||||||
import com.arkivanov.decompose.value.Value
|
|
||||||
import com.shabinder.common.main.SpotiFlyerMain
|
import com.shabinder.common.main.SpotiFlyerMain
|
||||||
import com.shabinder.common.main.SpotiFlyerMain.State
|
import extras.Props
|
||||||
|
import extras.RStateWrapper
|
||||||
import extras.RenderableComponent
|
import extras.RenderableComponent
|
||||||
import kotlinx.browser.document
|
import kotlinx.browser.document
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.css.*
|
||||||
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.dom.appendElement
|
import kotlinx.dom.appendElement
|
||||||
import react.RBuilder
|
import react.RBuilder
|
||||||
import styled.css
|
import styled.css
|
||||||
@ -38,12 +29,20 @@ import styled.styledDiv
|
|||||||
|
|
||||||
class HomeScreen(
|
class HomeScreen(
|
||||||
props: Props<SpotiFlyerMain>,
|
props: Props<SpotiFlyerMain>,
|
||||||
) : RenderableComponent<SpotiFlyerMain, State>(
|
) : RenderableComponent<SpotiFlyerMain, RStateWrapper<SpotiFlyerMain.State>>(
|
||||||
props,
|
props,
|
||||||
initialState = State()
|
initialState = RStateWrapper(props.component.model.value)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
component.model.bindToState {
|
||||||
|
model = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun componentDidMount() {
|
override fun componentDidMount() {
|
||||||
super.componentDidMount()
|
super.componentDidMount()
|
||||||
|
// RazorPay Button
|
||||||
val form = document.getElementById("razorpay-form")!!
|
val form = document.getElementById("razorpay-form")!!
|
||||||
repeat(form.childNodes.length){
|
repeat(form.childNodes.length){
|
||||||
form.childNodes.item(0)?.let { it1 -> form.removeChild(it1) }
|
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() {
|
override fun RBuilder.render() {
|
||||||
styledDiv{
|
styledDiv{
|
||||||
css {
|
css {
|
||||||
@ -73,9 +70,9 @@ class HomeScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
SearchBar {
|
SearchBar {
|
||||||
link = state.data.link
|
link = state.model.link
|
||||||
search = model::onLinkSearch
|
search = component::onLinkSearch
|
||||||
onLinkChange = model::onInputLinkChanged
|
onLinkChange = component::onInputLinkChanged
|
||||||
}
|
}
|
||||||
|
|
||||||
IconList {
|
IconList {
|
||||||
|
@ -17,22 +17,11 @@
|
|||||||
package home
|
package home
|
||||||
|
|
||||||
import Styles
|
import Styles
|
||||||
import kotlinx.css.borderRadius
|
import kotlinx.css.*
|
||||||
import kotlinx.css.height
|
|
||||||
import kotlinx.css.margin
|
|
||||||
import kotlinx.css.px
|
|
||||||
import kotlinx.css.width
|
|
||||||
import kotlinx.html.id
|
import kotlinx.html.id
|
||||||
import react.RBuilder
|
import react.*
|
||||||
import react.RProps
|
import react.dom.attrs
|
||||||
import react.ReactElement
|
import styled.*
|
||||||
import react.child
|
|
||||||
import react.functionalComponent
|
|
||||||
import styled.css
|
|
||||||
import styled.styledA
|
|
||||||
import styled.styledDiv
|
|
||||||
import styled.styledForm
|
|
||||||
import styled.styledImg
|
|
||||||
|
|
||||||
external interface IconListProps : RProps {
|
external interface IconListProps : RProps {
|
||||||
var iconsAndPlatforms: Map<String,String>
|
var iconsAndPlatforms: Map<String,String>
|
||||||
@ -63,7 +52,7 @@ private val iconList = functionalComponent<IconListProps>("IconList") { props ->
|
|||||||
if(icon == firstElem && props.isBadge){
|
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>
|
//<form><script src="https://checkout.razorpay.com/v1/payment-button.js" data-payment_button_id="pl_GnKuuDBdBu0ank" async> </script> </form>
|
||||||
styledForm {
|
styledForm {
|
||||||
attrs{
|
attrs {
|
||||||
id = "razorpay-form"
|
id = "razorpay-form"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,12 +25,9 @@ import org.w3c.dom.HTMLInputElement
|
|||||||
import react.RBuilder
|
import react.RBuilder
|
||||||
import react.RProps
|
import react.RProps
|
||||||
import react.child
|
import react.child
|
||||||
|
import react.dom.attrs
|
||||||
import react.functionalComponent
|
import react.functionalComponent
|
||||||
import styled.css
|
import styled.*
|
||||||
import styled.styledButton
|
|
||||||
import styled.styledDiv
|
|
||||||
import styled.styledImg
|
|
||||||
import styled.styledInput
|
|
||||||
|
|
||||||
external interface SearchbarProps : RProps {
|
external interface SearchbarProps : RProps {
|
||||||
var link: String
|
var link: String
|
||||||
@ -45,8 +42,6 @@ fun RBuilder.SearchBar(handler:SearchbarProps.() -> Unit) = child(searchbar){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val searchbar = functionalComponent<SearchbarProps>("SearchBar"){ props ->
|
val searchbar = functionalComponent<SearchbarProps>("SearchBar"){ props ->
|
||||||
styledDiv{
|
styledDiv{
|
||||||
css {
|
css {
|
||||||
|
@ -16,24 +16,10 @@
|
|||||||
|
|
||||||
package list
|
package list
|
||||||
|
|
||||||
import kotlinx.css.Align
|
import kotlinx.css.*
|
||||||
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.html.id
|
import kotlinx.html.id
|
||||||
import react.RBuilder
|
import react.*
|
||||||
import react.RProps
|
import react.dom.attrs
|
||||||
import react.ReactElement
|
|
||||||
import react.child
|
|
||||||
import react.functionalComponent
|
|
||||||
import styled.css
|
import styled.css
|
||||||
import styled.styledDiv
|
import styled.styledDiv
|
||||||
import styled.styledH1
|
import styled.styledH1
|
||||||
|
@ -16,26 +16,11 @@
|
|||||||
|
|
||||||
package list
|
package list
|
||||||
|
|
||||||
import kotlinx.css.Align
|
import kotlinx.css.*
|
||||||
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.html.id
|
import kotlinx.html.id
|
||||||
import kotlinx.html.js.onClickFunction
|
import kotlinx.html.js.onClickFunction
|
||||||
import react.RBuilder
|
import react.*
|
||||||
import react.RProps
|
import react.dom.attrs
|
||||||
import react.ReactElement
|
|
||||||
import react.child
|
|
||||||
import react.functionalComponent
|
|
||||||
import react.useEffect
|
|
||||||
import react.useState
|
|
||||||
import styled.css
|
import styled.css
|
||||||
import styled.styledDiv
|
import styled.styledDiv
|
||||||
import styled.styledH5
|
import styled.styledH5
|
||||||
|
@ -17,17 +17,10 @@
|
|||||||
package list
|
package list
|
||||||
|
|
||||||
import com.shabinder.common.models.DownloadStatus
|
import com.shabinder.common.models.DownloadStatus
|
||||||
import kotlinx.css.borderRadius
|
import kotlinx.css.*
|
||||||
import kotlinx.css.em
|
|
||||||
import kotlinx.css.margin
|
|
||||||
import kotlinx.css.px
|
|
||||||
import kotlinx.css.width
|
|
||||||
import kotlinx.html.js.onClickFunction
|
import kotlinx.html.js.onClickFunction
|
||||||
import react.RBuilder
|
import react.*
|
||||||
import react.RProps
|
import react.dom.attrs
|
||||||
import react.ReactElement
|
|
||||||
import react.child
|
|
||||||
import react.functionalComponent
|
|
||||||
import styled.css
|
import styled.css
|
||||||
import styled.styledDiv
|
import styled.styledDiv
|
||||||
import styled.styledImg
|
import styled.styledImg
|
||||||
|
@ -16,55 +16,55 @@
|
|||||||
|
|
||||||
package list
|
package list
|
||||||
|
|
||||||
import com.arkivanov.decompose.value.Value
|
|
||||||
import com.shabinder.common.list.SpotiFlyerList
|
import com.shabinder.common.list.SpotiFlyerList
|
||||||
import com.shabinder.common.list.SpotiFlyerList.State
|
import com.shabinder.common.list.SpotiFlyerList.State
|
||||||
|
import extras.Props
|
||||||
|
import extras.RStateWrapper
|
||||||
import extras.RenderableComponent
|
import extras.RenderableComponent
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.css.*
|
||||||
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.html.id
|
import kotlinx.html.id
|
||||||
import react.RBuilder
|
import react.RBuilder
|
||||||
|
import react.dom.attrs
|
||||||
import styled.css
|
import styled.css
|
||||||
import styled.styledDiv
|
import styled.styledDiv
|
||||||
import styled.styledSection
|
import styled.styledSection
|
||||||
|
|
||||||
|
|
||||||
class ListScreen(
|
class ListScreen(
|
||||||
props: Props<SpotiFlyerList>,
|
props: Props<SpotiFlyerList>,
|
||||||
) : RenderableComponent<SpotiFlyerList, State>(props,initialState = State()) {
|
) : RenderableComponent<SpotiFlyerList, RStateWrapper<State>>(
|
||||||
|
props,
|
||||||
override val stateFlow: Value<SpotiFlyerList.State> = model.models
|
initialState = RStateWrapper(props.component.model.value)
|
||||||
|
) {
|
||||||
|
init {
|
||||||
|
component.model.bindToState {
|
||||||
|
model = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun RBuilder.render() {
|
override fun RBuilder.render() {
|
||||||
|
|
||||||
val result = state.data.queryResult
|
val queryResult = state.model.queryResult
|
||||||
|
|
||||||
styledSection {
|
styledSection {
|
||||||
attrs {
|
attrs {
|
||||||
id = "list-screen"
|
id = "list-screen"
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result == null) {
|
if(queryResult == null) {
|
||||||
LoadingAnim { }
|
LoadingAnim { }
|
||||||
}else {
|
}else {
|
||||||
CoverImage {
|
CoverImage {
|
||||||
coverImageURL = result.coverUrl
|
coverImageURL = queryResult.coverUrl
|
||||||
coverName = result.title
|
coverName = queryResult.title
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadAllButton {
|
DownloadAllButton {
|
||||||
isActive = state.data.trackList.size > 1
|
isActive = state.model.trackList.size > 1
|
||||||
downloadAll = {
|
downloadAll = {
|
||||||
model.onDownloadAllClicked(state.data.trackList)
|
component.onDownloadAllClicked(state.model.trackList)
|
||||||
}
|
}
|
||||||
link = state.data.link
|
link = state.model.link
|
||||||
}
|
}
|
||||||
|
|
||||||
styledDiv {
|
styledDiv {
|
||||||
@ -74,10 +74,10 @@ class ListScreen(
|
|||||||
flexDirection = FlexDirection.column
|
flexDirection = FlexDirection.column
|
||||||
color = Color.white
|
color = Color.white
|
||||||
}
|
}
|
||||||
state.data.trackList.forEachIndexed{ _, trackDetails ->
|
state.model.trackList.forEachIndexed{ _, trackDetails ->
|
||||||
TrackItem {
|
TrackItem {
|
||||||
details = trackDetails
|
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.DownloadStatus
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
import kotlinx.css.Align
|
import kotlinx.css.*
|
||||||
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.html.id
|
import kotlinx.html.id
|
||||||
import react.RBuilder
|
import react.*
|
||||||
import react.RProps
|
import react.dom.attrs
|
||||||
import react.ReactElement
|
import styled.*
|
||||||
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
|
|
||||||
|
|
||||||
external interface TrackItemProps : RProps {
|
external interface TrackItemProps : RProps {
|
||||||
var details:TrackDetails
|
var details:TrackDetails
|
||||||
|
@ -16,33 +16,13 @@
|
|||||||
|
|
||||||
package navbar
|
package navbar
|
||||||
|
|
||||||
import kotlinx.css.Align
|
import kotlinx.css.*
|
||||||
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.html.id
|
import kotlinx.html.id
|
||||||
import kotlinx.html.js.onBlurFunction
|
import kotlinx.html.js.onBlurFunction
|
||||||
import kotlinx.html.js.onClickFunction
|
import kotlinx.html.js.onClickFunction
|
||||||
import react.RBuilder
|
import react.*
|
||||||
import react.RProps
|
import react.dom.attrs
|
||||||
import react.ReactElement
|
import styled.*
|
||||||
import react.child
|
|
||||||
import react.functionalComponent
|
|
||||||
import styled.css
|
|
||||||
import styled.styledA
|
|
||||||
import styled.styledDiv
|
|
||||||
import styled.styledH1
|
|
||||||
import styled.styledImg
|
|
||||||
import styled.styledNav
|
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
@Suppress("FunctionName")
|
||||||
fun RBuilder.NavBar(handler: NavBarProps.() -> Unit): ReactElement{
|
fun RBuilder.NavBar(handler: NavBarProps.() -> Unit): ReactElement{
|
||||||
|
@ -19,7 +19,8 @@ package root
|
|||||||
import com.arkivanov.decompose.RouterState
|
import com.arkivanov.decompose.RouterState
|
||||||
import com.shabinder.common.root.SpotiFlyerRoot
|
import com.shabinder.common.root.SpotiFlyerRoot
|
||||||
import com.shabinder.common.root.SpotiFlyerRoot.Child
|
import com.shabinder.common.root.SpotiFlyerRoot.Child
|
||||||
import extras.RenderableRootComponent
|
import extras.Props
|
||||||
|
import extras.RenderableComponent
|
||||||
import extras.renderableChild
|
import extras.renderableChild
|
||||||
import home.HomeScreen
|
import home.HomeScreen
|
||||||
import list.ListScreen
|
import list.ListScreen
|
||||||
@ -27,31 +28,34 @@ import navbar.NavBar
|
|||||||
import react.RBuilder
|
import react.RBuilder
|
||||||
import react.RState
|
import react.RState
|
||||||
|
|
||||||
class RootR(props: Props<SpotiFlyerRoot>) : RenderableRootComponent<SpotiFlyerRoot, RootR.State>(
|
class RootR(props: Props<SpotiFlyerRoot>) : RenderableComponent<SpotiFlyerRoot, State>(
|
||||||
props = props,
|
props = props,
|
||||||
initialState = State(routerState = props.model.routerState.value)
|
initialState = State(routerState = props.component.routerState.value)
|
||||||
) {
|
) {
|
||||||
private val component: Child
|
private val child: Child
|
||||||
get() = model.routerState.value.activeChild.instance
|
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() {
|
override fun RBuilder.render() {
|
||||||
NavBar {
|
NavBar {
|
||||||
isBackVisible = (component is Child.List)
|
isBackVisible = (child is Child.List)
|
||||||
popBackToHomeScreen = callBacks::popBackToHomeScreen
|
popBackToHomeScreen = callBacks::popBackToHomeScreen
|
||||||
}
|
}
|
||||||
when(component){
|
when(child){
|
||||||
is Child.Main -> renderableChild(HomeScreen::class, (component as Child.Main).component)
|
is Child.Main -> renderableChild(HomeScreen::class, (child as Child.Main).component)
|
||||||
is Child.List -> renderableChild(ListScreen::class, (component as Child.List).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