mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-25 02:14:32 +01:00
web-app and business logic integration (WIP) & gradle scripts refactor.
This commit is contained in:
parent
9920885b3b
commit
42a24ce412
@ -72,7 +72,7 @@ dependencies {
|
|||||||
implementation(project(":common:data-models"))
|
implementation(project(":common:data-models"))
|
||||||
|
|
||||||
implementation(Koin.android)
|
implementation(Koin.android)
|
||||||
implementation(Koin.androidViewModel)
|
implementation(Koin.compose)
|
||||||
|
|
||||||
//DECOMPOSE
|
//DECOMPOSE
|
||||||
implementation(Decompose.decompose)
|
implementation(Decompose.decompose)
|
||||||
|
@ -48,6 +48,7 @@ import com.tonyodev.fetch2.Status
|
|||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
|
import com.shabinder.common.uikit.showPopUpMessage as uikitShowPopUpMessage
|
||||||
|
|
||||||
const val disableDozeCode = 1223
|
const val disableDozeCode = 1223
|
||||||
|
|
||||||
@ -117,6 +118,7 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
|
|||||||
override val database = this@MainActivity.database
|
override val database = this@MainActivity.database
|
||||||
override val fetchPlatformQueryResult = this@MainActivity.fetcher
|
override val fetchPlatformQueryResult = this@MainActivity.fetcher
|
||||||
override val directories: Dir = this@MainActivity.dir
|
override val directories: Dir = this@MainActivity.dir
|
||||||
|
override val showPopUpMessage: (String) -> Unit = ::uikitShowPopUpMessage
|
||||||
override val downloadProgressReport: MutableSharedFlow<HashMap<String, DownloadStatus>> = trackStatusFlow
|
override val downloadProgressReport: MutableSharedFlow<HashMap<String, DownloadStatus>> = trackStatusFlow
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -234,7 +236,7 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
|
|||||||
|
|
||||||
override fun onPaymentError(errorCode: Int, response: String?) {
|
override fun onPaymentError(errorCode: Int, response: String?) {
|
||||||
try{
|
try{
|
||||||
showPopUpMessage("Payment Failed, Response:$response")
|
uikitShowPopUpMessage("Payment Failed, Response:$response")
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
Log.d("Razorpay Payment","Exception in onPaymentSuccess $response")
|
Log.d("Razorpay Payment","Exception in onPaymentSuccess $response")
|
||||||
}
|
}
|
||||||
@ -242,9 +244,9 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
|
|||||||
|
|
||||||
override fun onPaymentSuccess(razorpayPaymentId: String?) {
|
override fun onPaymentSuccess(razorpayPaymentId: String?) {
|
||||||
try{
|
try{
|
||||||
showPopUpMessage("Payment Successful, ThankYou!")
|
uikitShowPopUpMessage("Payment Successful, ThankYou!")
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
showPopUpMessage("Razorpay Payment, Error Occurred.")
|
uikitShowPopUpMessage("Razorpay Payment, Error Occurred.")
|
||||||
Log.d("Razorpay Payment","Exception in onPaymentSuccess, ${e.message}")
|
Log.d("Razorpay Payment","Exception in onPaymentSuccess, ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,13 @@ object Versions {
|
|||||||
|
|
||||||
const val coilVersion = "0.4.1"
|
const val coilVersion = "0.4.1"
|
||||||
//DI
|
//DI
|
||||||
const val koin = "3.0.0-alpha-4"
|
const val koin = "3.0.1-beta-1"
|
||||||
|
|
||||||
//Logger
|
//Logger
|
||||||
const val kermit = "0.1.8"
|
const val kermit = "0.1.8"
|
||||||
|
|
||||||
//Internet
|
//Internet
|
||||||
const val ktor = "1.5.1"
|
const val ktor = "1.5.2"
|
||||||
|
|
||||||
const val kotlinxSerialization = "1.1.0-RC"
|
const val kotlinxSerialization = "1.1.0-RC"
|
||||||
//Database
|
//Database
|
||||||
@ -32,10 +32,10 @@ object Versions {
|
|||||||
const val androidLifecycle = "2.3.0"
|
const val androidLifecycle = "2.3.0"
|
||||||
}
|
}
|
||||||
object Koin {
|
object Koin {
|
||||||
val core = "org.koin:koin-core:${Versions.koin}"
|
val core = "io.insert-koin:koin-core:${Versions.koin}"
|
||||||
val test = "org.koin:koin-test:${Versions.koin}"
|
val test = "io.insert-koin:koin-test:${Versions.koin}"
|
||||||
val android = "org.koin:koin-android:${Versions.koin}"
|
val android = "io.insert-koin:koin-android:${Versions.koin}"
|
||||||
val androidViewModel = "org.koin:koin-androidx-viewmodel:${Versions.koin}"
|
val compose = "io.insert-koin:koin-androidx-compose:${Versions.koin}"
|
||||||
}
|
}
|
||||||
object Androidx{
|
object Androidx{
|
||||||
const val androidxActivity = "androidx.activity:activity-compose:1.3.0-alpha02"
|
const val androidxActivity = "androidx.activity:activity-compose:1.3.0-alpha02"
|
||||||
|
@ -13,6 +13,7 @@ kotlin {
|
|||||||
implementation(compose.runtime)
|
implementation(compose.runtime)
|
||||||
implementation(compose.foundation)
|
implementation(compose.foundation)
|
||||||
implementation(compose.material)
|
implementation(compose.material)
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,9 +2,11 @@ import gradle.kotlin.dsl.accessors._2e23d8fadf0ed92ae13e19db3d83f86d.compose
|
|||||||
import gradle.kotlin.dsl.accessors._2e23d8fadf0ed92ae13e19db3d83f86d.kotlin
|
import gradle.kotlin.dsl.accessors._2e23d8fadf0ed92ae13e19db3d83f86d.kotlin
|
||||||
import gradle.kotlin.dsl.accessors._2e23d8fadf0ed92ae13e19db3d83f86d.sourceSets
|
import gradle.kotlin.dsl.accessors._2e23d8fadf0ed92ae13e19db3d83f86d.sourceSets
|
||||||
import org.gradle.kotlin.dsl.withType
|
import org.gradle.kotlin.dsl.withType
|
||||||
|
import org.jetbrains.compose.compose
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.library")
|
// id("com.android.library")
|
||||||
|
id("android-setup")
|
||||||
id("kotlin-multiplatform")
|
id("kotlin-multiplatform")
|
||||||
id("org.jetbrains.compose")
|
id("org.jetbrains.compose")
|
||||||
}
|
}
|
||||||
@ -19,9 +21,7 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
named("commonMain") {
|
named("commonMain") {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(compose.runtime)
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3")
|
||||||
implementation(compose.foundation)
|
|
||||||
implementation(compose.material)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,12 +29,22 @@ kotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation("androidx.appcompat:appcompat:1.2.0")
|
implementation("androidx.appcompat:appcompat:1.2.0")
|
||||||
implementation(Androidx.core)
|
implementation(Androidx.core)
|
||||||
|
implementation(compose.runtime)
|
||||||
|
implementation(compose.material)
|
||||||
|
implementation(compose.foundation)
|
||||||
|
implementation(compose.materialIconsExtended)
|
||||||
|
implementation(Decompose.extensionsCompose)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
named("desktopMain") {
|
named("desktopMain") {
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation(compose.runtime)
|
||||||
|
implementation(compose.foundation)
|
||||||
|
implementation(compose.material)
|
||||||
implementation(compose.desktop.common)
|
implementation(compose.desktop.common)
|
||||||
|
implementation(compose.materialIconsExtended)
|
||||||
|
implementation(Decompose.extensionsCompose)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
named("jsMain") {
|
named("jsMain") {
|
||||||
|
@ -16,6 +16,7 @@ import androidx.compose.ui.text.font.Font
|
|||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import com.shabinder.common.database.R
|
import com.shabinder.common.database.R
|
||||||
|
import com.shabinder.common.database.appContext
|
||||||
import com.shabinder.common.di.Picture
|
import com.shabinder.common.di.Picture
|
||||||
import com.shabinder.common.di.dispatcherIO
|
import com.shabinder.common.di.dispatcherIO
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@ -102,4 +103,16 @@ actual fun YoutubeMusicLogo() = vectorResource(R.drawable.ic_youtube_music_logo)
|
|||||||
actual fun GithubLogo() = vectorResource(R.drawable.ic_github)
|
actual fun GithubLogo() = vectorResource(R.drawable.ic_github)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun vectorResource(@DrawableRes id: Int) = ImageVector.Companion.vectorResource(id)
|
fun vectorResource(@DrawableRes id: Int) = ImageVector.Companion.vectorResource(id)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
actual fun Toast(
|
||||||
|
text: String,
|
||||||
|
visibility: MutableState<Boolean>,
|
||||||
|
duration: ToastDuration
|
||||||
|
){
|
||||||
|
//We Have Android's Implementation of Toast so its just Empty
|
||||||
|
}
|
||||||
|
actual fun showPopUpMessage(text: String){
|
||||||
|
android.widget.Toast.makeText(appContext,text, android.widget.Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
@ -85,7 +85,6 @@ fun MainScreen(modifier: Modifier = Modifier, topPadding: Dp = 0.dp,statusBarHei
|
|||||||
Spacer(Modifier.padding(top = topPadding))
|
Spacer(Modifier.padding(top = topPadding))
|
||||||
Children(
|
Children(
|
||||||
routerState = component.routerState,
|
routerState = component.routerState,
|
||||||
//TODO animation = crossfade()
|
|
||||||
) { child, _ ->
|
) { child, _ ->
|
||||||
when (child) {
|
when (child) {
|
||||||
is Child.Main -> SpotiFlyerMainContent(component = child.component)
|
is Child.Main -> SpotiFlyerMainContent(component = child.component)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package com.shabinder.common.di
|
package com.shabinder.common.uikit
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
@ -8,6 +8,8 @@ enum class ToastDuration(val value: Int) {
|
|||||||
Short(1000), Long(3000)
|
Short(1000), Long(3000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expect fun showPopUpMessage(text: String)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
expect fun Toast(
|
expect fun Toast(
|
||||||
text: String,
|
text: String,
|
@ -1,4 +1,4 @@
|
|||||||
package com.shabinder.common.di
|
package com.shabinder.common.uikit
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@ -15,13 +15,10 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
actual val dispatcherIO = Dispatchers.IO
|
|
||||||
|
|
||||||
private val message: MutableState<String> = mutableStateOf("")
|
private val message: MutableState<String> = mutableStateOf("")
|
||||||
private val state: MutableState<Boolean> = mutableStateOf(false)
|
private val state: MutableState<Boolean> = mutableStateOf(false)
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("multiplatform-compose-setup")
|
id("multiplatform-setup")
|
||||||
id("android-setup")
|
id("android-setup")
|
||||||
id("kotlin-parcelize")
|
id("kotlin-parcelize")
|
||||||
kotlin("plugin.serialization")
|
kotlin("plugin.serialization")
|
||||||
@ -10,7 +10,7 @@ kotlin {
|
|||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
api("dev.icerock.moko:parcelize:0.6.0")
|
api("dev.icerock.moko:parcelize:0.6.0")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1")
|
api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,15 +10,14 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(compose.materialIconsExtended)
|
implementation(project(":common:data-models"))
|
||||||
api(project(":common:data-models"))
|
implementation(project(":common:database"))
|
||||||
api(project(":common:database"))
|
|
||||||
implementation(project(":fuzzywuzzy:app"))
|
implementation(project(":fuzzywuzzy:app"))
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2")
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.1.1")
|
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.1.1")
|
||||||
implementation(Ktor.clientCore)
|
implementation(Ktor.clientCore)
|
||||||
implementation(Ktor.clientCio)
|
//implementation(Ktor.clientCio)
|
||||||
implementation(Ktor.clientSerialization)
|
implementation(Ktor.clientSerialization)
|
||||||
implementation(Ktor.clientLogging)
|
implementation(Ktor.clientLogging)
|
||||||
implementation(Ktor.clientJson)
|
implementation(Ktor.clientJson)
|
||||||
@ -34,6 +33,7 @@ kotlin {
|
|||||||
}
|
}
|
||||||
androidMain {
|
androidMain {
|
||||||
dependencies{
|
dependencies{
|
||||||
|
implementation(compose.materialIconsExtended)
|
||||||
implementation(Ktor.clientAndroid)
|
implementation(Ktor.clientAndroid)
|
||||||
implementation(Extras.Android.fetch)
|
implementation(Extras.Android.fetch)
|
||||||
implementation(Koin.android)
|
implementation(Koin.android)
|
||||||
@ -43,12 +43,14 @@ kotlin {
|
|||||||
}
|
}
|
||||||
desktopMain {
|
desktopMain {
|
||||||
dependencies{
|
dependencies{
|
||||||
|
implementation(compose.materialIconsExtended)
|
||||||
implementation(Ktor.clientApache)
|
implementation(Ktor.clientApache)
|
||||||
implementation(Ktor.slf4j)
|
implementation(Ktor.slf4j)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsMain {
|
jsMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation(Ktor.clientJs)
|
||||||
implementation(project(":common:data-models"))
|
implementation(project(":common:data-models"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,21 +34,9 @@ actual fun openPlatform(packageID:String, platformLink:String){
|
|||||||
}
|
}
|
||||||
actual val dispatcherIO = Dispatchers.IO
|
actual val dispatcherIO = Dispatchers.IO
|
||||||
|
|
||||||
@Composable
|
|
||||||
actual fun Toast(
|
|
||||||
text: String,
|
|
||||||
visibility: MutableState<Boolean>,
|
|
||||||
duration: ToastDuration
|
|
||||||
){
|
|
||||||
//We Have Android's Implementation of Toast so its just Empty
|
|
||||||
}
|
|
||||||
|
|
||||||
actual val isInternetAvailable:Boolean
|
actual val isInternetAvailable:Boolean
|
||||||
get() = internetAvailability.value ?: true
|
get() = internetAvailability.value ?: true
|
||||||
|
|
||||||
actual fun showPopUpMessage(text: String){
|
|
||||||
android.widget.Toast.makeText(appContext,text, android.widget.Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
actual fun shareApp(){
|
actual fun shareApp(){
|
||||||
val sendIntent: Intent = Intent().apply {
|
val sendIntent: Intent = Intent().apply {
|
||||||
action = Intent.ACTION_SEND
|
action = Intent.ACTION_SEND
|
||||||
|
@ -11,10 +11,6 @@ import io.ktor.client.*
|
|||||||
import io.ktor.client.features.json.*
|
import io.ktor.client.features.json.*
|
||||||
import io.ktor.client.features.json.serializer.*
|
import io.ktor.client.features.json.serializer.*
|
||||||
import io.ktor.client.features.logging.*
|
import io.ktor.client.features.logging.*
|
||||||
import io.ktor.client.request.*
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import org.koin.core.context.startKoin
|
import org.koin.core.context.startKoin
|
||||||
import org.koin.dsl.KoinAppDeclaration
|
import org.koin.dsl.KoinAppDeclaration
|
||||||
|
@ -9,8 +9,6 @@ expect fun shareApp()
|
|||||||
|
|
||||||
expect fun giveDonation()
|
expect fun giveDonation()
|
||||||
|
|
||||||
expect fun showPopUpMessage(text: String)
|
|
||||||
|
|
||||||
expect val dispatcherIO: CoroutineDispatcher
|
expect val dispatcherIO: CoroutineDispatcher
|
||||||
|
|
||||||
expect val isInternetAvailable:Boolean
|
expect val isInternetAvailable:Boolean
|
||||||
|
@ -51,7 +51,7 @@ class SpotifyProvider(
|
|||||||
override suspend fun authenticateSpotify(): HttpClient?{
|
override suspend fun authenticateSpotify(): HttpClient?{
|
||||||
val token = tokenStore.getToken()
|
val token = tokenStore.getToken()
|
||||||
return if(token == null) {
|
return if(token == null) {
|
||||||
showPopUpMessage("Please Check your Network Connection")
|
logger.d{ "Please Check your Network Connection" }
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
@ -12,6 +12,7 @@ import com.shabinder.common.models.DownloadResult
|
|||||||
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 io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
@ -22,6 +23,8 @@ actual fun openPlatform(packageID:String, platformLink:String){
|
|||||||
//TODO
|
//TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actual val dispatcherIO = Dispatchers.IO
|
||||||
|
|
||||||
actual fun shareApp(){
|
actual fun shareApp(){
|
||||||
//TODO
|
//TODO
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ actual fun giveDonation(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
actual fun queryActiveTracks(){}
|
actual fun queryActiveTracks(){}
|
||||||
actual fun showPopUpMessage(text:String){}
|
|
||||||
|
|
||||||
actual val dispatcherIO: CoroutineDispatcher = Dispatchers.Default
|
actual val dispatcherIO: CoroutineDispatcher = Dispatchers.Default
|
||||||
|
|
||||||
@ -40,6 +39,7 @@ private suspend fun isInternetAvailable(): Boolean {
|
|||||||
|
|
||||||
actual val isInternetAvailable:Boolean
|
actual val isInternetAvailable:Boolean
|
||||||
get(){
|
get(){
|
||||||
|
return true
|
||||||
var result = false
|
var result = false
|
||||||
val job = GlobalScope.launch { result = isInternetAvailable() }
|
val job = GlobalScope.launch { result = isInternetAvailable() }
|
||||||
while(job.isActive){}
|
while(job.isActive){}
|
||||||
@ -52,11 +52,11 @@ actual suspend fun downloadTracks(
|
|||||||
list: List<TrackDetails>,
|
list: List<TrackDetails>,
|
||||||
getYTIDBestMatch:suspend (String, TrackDetails)->String?,
|
getYTIDBestMatch:suspend (String, TrackDetails)->String?,
|
||||||
saveFileWithMetaData:suspend (mp3ByteArray:ByteArray, trackDetails: TrackDetails) -> Unit
|
saveFileWithMetaData:suspend (mp3ByteArray:ByteArray, trackDetails: TrackDetails) -> Unit
|
||||||
){
|
){/*
|
||||||
list.forEach {
|
list.forEach {
|
||||||
if (!it.videoID.isNullOrBlank()) {//Video ID already known!
|
if (!it.videoID.isNullOrBlank()) {//Video ID already known!
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.shabinder.common.di
|
||||||
|
|
||||||
|
import co.touchlab.kermit.Kermit
|
||||||
|
import com.shabinder.common.models.PlatformQueryResult
|
||||||
|
import com.shabinder.database.Database
|
||||||
|
import io.ktor.client.*
|
||||||
|
|
||||||
|
actual class YoutubeProvider actual constructor(
|
||||||
|
httpClient: HttpClient,
|
||||||
|
database: Database,
|
||||||
|
logger: Kermit,
|
||||||
|
dir: Dir
|
||||||
|
) {
|
||||||
|
actual suspend fun query(fullLink: String): PlatformQueryResult? {
|
||||||
|
return null // TODO
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,6 @@ kotlin {
|
|||||||
implementation(MVIKotlin.coroutines)
|
implementation(MVIKotlin.coroutines)
|
||||||
implementation(MVIKotlin.mvikotlin)
|
implementation(MVIKotlin.mvikotlin)
|
||||||
implementation(Decompose.decompose)
|
implementation(Decompose.decompose)
|
||||||
implementation(Decompose.extensionsCompose)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@ interface SpotiFlyerList {
|
|||||||
val dir: Dir
|
val dir: Dir
|
||||||
val link: String
|
val link: String
|
||||||
val listOutput: Consumer<Output>
|
val listOutput: Consumer<Output>
|
||||||
|
val showPopUpMessage:(String)->Unit
|
||||||
val downloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>>
|
val downloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>>
|
||||||
}
|
}
|
||||||
sealed class Output {
|
sealed class Output {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.shabinder.common.list.integration
|
package com.shabinder.common.list.integration
|
||||||
|
|
||||||
import androidx.compose.ui.graphics.ImageBitmap
|
|
||||||
import com.arkivanov.decompose.ComponentContext
|
import com.arkivanov.decompose.ComponentContext
|
||||||
import com.arkivanov.mvikotlin.extensions.coroutines.states
|
import com.arkivanov.mvikotlin.extensions.coroutines.states
|
||||||
import com.shabinder.common.di.Picture
|
import com.shabinder.common.di.Picture
|
||||||
@ -24,8 +23,9 @@ internal class SpotiFlyerListImpl(
|
|||||||
dir = this.dir,
|
dir = this.dir,
|
||||||
storeFactory = storeFactory,
|
storeFactory = storeFactory,
|
||||||
fetchQuery = fetchQuery,
|
fetchQuery = fetchQuery,
|
||||||
|
downloadProgressFlow = downloadProgressFlow,
|
||||||
link = link,
|
link = link,
|
||||||
downloadProgressFlow = downloadProgressFlow
|
showPopUpMessage = showPopUpMessage
|
||||||
).provide()
|
).provide()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ internal class SpotiFlyerListStoreProvider(
|
|||||||
private val storeFactory: StoreFactory,
|
private val storeFactory: StoreFactory,
|
||||||
private val fetchQuery: FetchPlatformQueryResult,
|
private val fetchQuery: FetchPlatformQueryResult,
|
||||||
private val link: String,
|
private val link: String,
|
||||||
|
private val showPopUpMessage: (String) -> Unit,
|
||||||
private val downloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>>
|
private val downloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>>
|
||||||
) {
|
) {
|
||||||
val logger = getLogger()
|
val logger = getLogger()
|
||||||
|
@ -10,16 +10,13 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(compose.materialIconsExtended)
|
|
||||||
implementation(project(":common:dependency-injection"))
|
implementation(project(":common:dependency-injection"))
|
||||||
//implementation("com.alialbaali.kamel:kamel-image:0.1.0")
|
|
||||||
implementation(project(":common:data-models"))
|
implementation(project(":common:data-models"))
|
||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
implementation(SqlDelight.coroutineExtensions)
|
implementation(SqlDelight.coroutineExtensions)
|
||||||
implementation(MVIKotlin.coroutines)
|
implementation(MVIKotlin.coroutines)
|
||||||
implementation(MVIKotlin.mvikotlin)
|
implementation(MVIKotlin.mvikotlin)
|
||||||
implementation(Decompose.decompose)
|
implementation(Decompose.decompose)
|
||||||
implementation(Decompose.extensionsCompose)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.shabinder.common.main
|
package com.shabinder.common.main
|
||||||
|
|
||||||
import androidx.compose.ui.graphics.ImageBitmap
|
|
||||||
import com.arkivanov.decompose.ComponentContext
|
import com.arkivanov.decompose.ComponentContext
|
||||||
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
||||||
import com.shabinder.common.di.Dir
|
import com.shabinder.common.di.Dir
|
||||||
@ -39,8 +38,9 @@ interface SpotiFlyerMain {
|
|||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
val mainOutput: Consumer<Output>
|
val mainOutput: Consumer<Output>
|
||||||
val storeFactory: StoreFactory
|
val storeFactory: StoreFactory
|
||||||
val database: Database
|
val database: Database?
|
||||||
val dir: Dir
|
val dir: Dir
|
||||||
|
val showPopUpMessage:(String)->Unit
|
||||||
}
|
}
|
||||||
sealed class Output {
|
sealed class Output {
|
||||||
data class Search(val link: String) : Output()
|
data class Search(val link: String) : Output()
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package com.shabinder.common.main.integration
|
package com.shabinder.common.main.integration
|
||||||
|
|
||||||
import androidx.compose.ui.graphics.ImageBitmap
|
|
||||||
import com.arkivanov.decompose.ComponentContext
|
import com.arkivanov.decompose.ComponentContext
|
||||||
import com.arkivanov.mvikotlin.extensions.coroutines.states
|
import com.arkivanov.mvikotlin.extensions.coroutines.states
|
||||||
import com.shabinder.common.di.Picture
|
import com.shabinder.common.di.Picture
|
||||||
import com.shabinder.common.di.isInternetAvailable
|
import com.shabinder.common.di.isInternetAvailable
|
||||||
import com.shabinder.common.di.showPopUpMessage
|
|
||||||
import com.shabinder.common.main.SpotiFlyerMain
|
import com.shabinder.common.main.SpotiFlyerMain
|
||||||
import com.shabinder.common.main.SpotiFlyerMain.*
|
import com.shabinder.common.main.SpotiFlyerMain.*
|
||||||
import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent
|
import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent
|
||||||
@ -22,7 +20,8 @@ internal class SpotiFlyerMainImpl(
|
|||||||
instanceKeeper.getStore {
|
instanceKeeper.getStore {
|
||||||
SpotiFlyerMainStoreProvider(
|
SpotiFlyerMainStoreProvider(
|
||||||
storeFactory = storeFactory,
|
storeFactory = storeFactory,
|
||||||
database = database
|
database = database,
|
||||||
|
showPopUpMessage = showPopUpMessage
|
||||||
).provide()
|
).provide()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,8 @@ import kotlinx.coroutines.flow.map
|
|||||||
|
|
||||||
internal class SpotiFlyerMainStoreProvider(
|
internal class SpotiFlyerMainStoreProvider(
|
||||||
private val storeFactory: StoreFactory,
|
private val storeFactory: StoreFactory,
|
||||||
database: Database
|
private val showPopUpMessage: (String)->Unit,
|
||||||
|
private val database: Database?
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun provide(): SpotiFlyerMainStore =
|
fun provide(): SpotiFlyerMainStore =
|
||||||
@ -34,12 +35,12 @@ internal class SpotiFlyerMainStoreProvider(
|
|||||||
reducer = ReducerImpl
|
reducer = ReducerImpl
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
val updates: Flow<List<DownloadRecord>> =
|
val updates: Flow<List<DownloadRecord>>? =
|
||||||
database.downloadRecordDatabaseQueries
|
database?.downloadRecordDatabaseQueries
|
||||||
.selectAll()
|
?.selectAll()
|
||||||
.asFlow()
|
?.asFlow()
|
||||||
.mapToList(Dispatchers.Default)
|
?.mapToList(Dispatchers.Default)
|
||||||
.map {
|
?.map {
|
||||||
it.map { record ->
|
it.map { record ->
|
||||||
record.run{
|
record.run{
|
||||||
DownloadRecord(id, type, name, link, coverUrl, totalFiles)
|
DownloadRecord(id, type, name, link, coverUrl, totalFiles)
|
||||||
@ -56,7 +57,7 @@ internal class SpotiFlyerMainStoreProvider(
|
|||||||
|
|
||||||
private inner class ExecutorImpl : SuspendExecutor<Intent, Unit, State, Result, Nothing>() {
|
private inner class ExecutorImpl : SuspendExecutor<Intent, Unit, State, Result, Nothing>() {
|
||||||
override suspend fun executeAction(action: Unit, getState: () -> State) {
|
override suspend fun executeAction(action: Unit, getState: () -> State) {
|
||||||
updates.collect {
|
updates?.collect {
|
||||||
dispatch(Result.ItemsLoaded(it))
|
dispatch(Result.ItemsLoaded(it))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(compose.materialIconsExtended)
|
|
||||||
implementation(project(":common:dependency-injection"))
|
implementation(project(":common:dependency-injection"))
|
||||||
implementation(project(":common:data-models"))
|
implementation(project(":common:data-models"))
|
||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
@ -20,7 +19,6 @@ kotlin {
|
|||||||
implementation(MVIKotlin.coroutines)
|
implementation(MVIKotlin.coroutines)
|
||||||
implementation(MVIKotlin.mvikotlin)
|
implementation(MVIKotlin.mvikotlin)
|
||||||
implementation(Decompose.decompose)
|
implementation(Decompose.decompose)
|
||||||
implementation(Decompose.extensionsCompose)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,9 +28,10 @@ interface SpotiFlyerRoot {
|
|||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
val storeFactory: StoreFactory
|
val storeFactory: StoreFactory
|
||||||
val database: Database
|
val database: Database?
|
||||||
val fetchPlatformQueryResult: FetchPlatformQueryResult
|
val fetchPlatformQueryResult: FetchPlatformQueryResult
|
||||||
val directories: Dir
|
val directories: Dir
|
||||||
|
val showPopUpMessage:(String)->Unit
|
||||||
val downloadProgressReport: MutableSharedFlow<HashMap<String, DownloadStatus>>
|
val downloadProgressReport: MutableSharedFlow<HashMap<String, DownloadStatus>>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,7 @@ import com.shabinder.common.di.initKoin
|
|||||||
import com.shabinder.common.root.SpotiFlyerRoot
|
import com.shabinder.common.root.SpotiFlyerRoot
|
||||||
import com.shabinder.common.uikit.*
|
import com.shabinder.common.uikit.*
|
||||||
import com.shabinder.database.Database
|
import com.shabinder.database.Database
|
||||||
import kotlinx.coroutines.flow.collect
|
import com.shabinder.common.uikit.showPopUpMessage as uikitShowPopUpMessage
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
private val koin = initKoin(enableNetworkLogs = true).koin
|
private val koin = initKoin(enableNetworkLogs = true).koin
|
||||||
|
|
||||||
@ -49,9 +48,10 @@ private fun spotiFlyerRoot(componentContext: ComponentContext): SpotiFlyerRoot =
|
|||||||
componentContext = componentContext,
|
componentContext = componentContext,
|
||||||
dependencies = object : SpotiFlyerRoot.Dependencies {
|
dependencies = object : SpotiFlyerRoot.Dependencies {
|
||||||
override val storeFactory = DefaultStoreFactory
|
override val storeFactory = DefaultStoreFactory
|
||||||
override val database: Database = koin.get()
|
override val database: Database? = koin.get()
|
||||||
override val fetchPlatformQueryResult: FetchPlatformQueryResult = koin.get()
|
override val fetchPlatformQueryResult: FetchPlatformQueryResult = koin.get()
|
||||||
override val directories: Dir = koin.get()
|
override val directories: Dir = koin.get()
|
||||||
|
override val showPopUpMessage: (String) -> Unit = ::uikitShowPopUpMessage
|
||||||
override val downloadProgressReport = DownloadProgressFlow
|
override val downloadProgressReport = DownloadProgressFlow
|
||||||
}
|
}
|
||||||
)
|
)
|
@ -14,7 +14,8 @@ final pathSeparator = System.properties["path.separator"]
|
|||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvm()
|
jvm()
|
||||||
/*js() {
|
js() {
|
||||||
|
browser()
|
||||||
[compileKotlinJs, compileTestKotlinJs].each { configuration ->
|
[compileKotlinJs, compileTestKotlinJs].each { configuration ->
|
||||||
configuration.kotlinOptions {
|
configuration.kotlinOptions {
|
||||||
moduleKind = 'umd'
|
moduleKind = 'umd'
|
||||||
@ -22,7 +23,7 @@ kotlin {
|
|||||||
metaInfo = true
|
metaInfo = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
wasm32("wasm")
|
wasm32("wasm")
|
||||||
@ -63,7 +64,7 @@ kotlin {
|
|||||||
implementation 'junit:junit:4.12'
|
implementation 'junit:junit:4.12'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*jsMain {
|
jsMain {
|
||||||
kotlin.srcDir('src/jsMain/kotlin')
|
kotlin.srcDir('src/jsMain/kotlin')
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation kotlin("stdlib-js")
|
implementation kotlin("stdlib-js")
|
||||||
@ -77,7 +78,7 @@ kotlin {
|
|||||||
kotlinOptions.moduleKind = "umd"
|
kotlinOptions.moduleKind = "umd"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsTest {
|
/*jsTest {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation kotlin("test-js")
|
implementation kotlin("test-js")
|
||||||
implementation kotlin("stdlib-js")
|
implementation kotlin("stdlib-js")
|
||||||
|
@ -14,6 +14,16 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(kotlin("stdlib-js"))
|
implementation(kotlin("stdlib-js"))
|
||||||
|
implementation(Decompose.decompose)
|
||||||
|
implementation(Koin.core)
|
||||||
|
implementation(MVIKotlin.mvikotlin)
|
||||||
|
implementation(MVIKotlin.mvikotlinMain)
|
||||||
|
implementation(project(":common:root"))
|
||||||
|
implementation(project(":common:main"))
|
||||||
|
implementation(project(":common:list"))
|
||||||
|
implementation(project(":common:database"))
|
||||||
|
implementation(project(":common:data-models"))
|
||||||
|
implementation(project(":common:dependency-injection"))
|
||||||
implementation("org.jetbrains:kotlin-react:17.0.1-pre.148-kotlin-1.4.30")
|
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-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-styled:1.0.0-pre.115-kotlin-1.4.10")
|
||||||
|
58
web-app/src/main/kotlin/App.kt
Normal file
58
web-app/src/main/kotlin/App.kt
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import co.touchlab.kermit.Kermit
|
||||||
|
import com.arkivanov.decompose.DefaultComponentContext
|
||||||
|
import com.arkivanov.decompose.lifecycle.LifecycleRegistry
|
||||||
|
import com.arkivanov.decompose.lifecycle.destroy
|
||||||
|
import com.arkivanov.decompose.lifecycle.resume
|
||||||
|
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
||||||
|
import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory
|
||||||
|
import com.shabinder.common.models.DownloadStatus
|
||||||
|
import com.shabinder.common.root.SpotiFlyerRoot
|
||||||
|
import com.shabinder.database.Database
|
||||||
|
import extras.renderableChild
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import react.*
|
||||||
|
import root.RootR
|
||||||
|
|
||||||
|
external interface AppProps : RProps {
|
||||||
|
var dependencies: AppDependencies
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RBuilder.app(attrs: AppProps.() -> Unit): ReactElement {
|
||||||
|
return child(App::class){
|
||||||
|
this.attrs(attrs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class App(props: AppProps): RComponent<AppProps, RState>(props) {
|
||||||
|
|
||||||
|
private val lifecycle = LifecycleRegistry()
|
||||||
|
private val ctx = DefaultComponentContext(lifecycle = lifecycle)
|
||||||
|
private val dependencies = props.dependencies
|
||||||
|
private val logger:Kermit
|
||||||
|
get() = dependencies.logger
|
||||||
|
|
||||||
|
private val root = SpotiFlyerRoot(ctx,
|
||||||
|
object : SpotiFlyerRoot.Dependencies{
|
||||||
|
override val storeFactory: StoreFactory = DefaultStoreFactory
|
||||||
|
override val database: Database? = null
|
||||||
|
override val fetchPlatformQueryResult = dependencies.fetchPlatformQueryResult
|
||||||
|
override val directories = dependencies.directories
|
||||||
|
override val showPopUpMessage: (String) -> Unit = {}//TODO
|
||||||
|
override val downloadProgressReport: MutableSharedFlow<HashMap<String, DownloadStatus>>
|
||||||
|
= MutableSharedFlow(1)
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun componentDidMount() {
|
||||||
|
lifecycle.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun componentWillUnmount() {
|
||||||
|
lifecycle.destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun RBuilder.render() {
|
||||||
|
renderableChild(RootR::class, root)
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,34 @@
|
|||||||
import home.homeScreen
|
import co.touchlab.kermit.Kermit
|
||||||
|
import com.shabinder.common.di.Dir
|
||||||
|
import com.shabinder.common.di.FetchPlatformQueryResult
|
||||||
|
import com.shabinder.common.di.initKoin
|
||||||
import react.dom.render
|
import react.dom.render
|
||||||
import kotlinx.browser.document
|
import kotlinx.browser.document
|
||||||
import kotlinx.browser.window
|
import kotlinx.browser.window
|
||||||
import navbar.navBar
|
import navbar.navBar
|
||||||
|
import org.koin.core.component.KoinComponent
|
||||||
|
import org.koin.core.component.get
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
window.onload = {
|
window.onload = {
|
||||||
render(document.getElementById("root")) {
|
render(document.getElementById("root")) {
|
||||||
navBar {}
|
navBar {}
|
||||||
homeScreen {
|
app {
|
||||||
link = ""
|
dependencies = AppDependencies
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
object AppDependencies : KoinComponent {
|
||||||
|
val logger: Kermit
|
||||||
|
val directories: Dir
|
||||||
|
val fetchPlatformQueryResult: FetchPlatformQueryResult
|
||||||
|
init {
|
||||||
|
initKoin()
|
||||||
|
directories = get()
|
||||||
|
logger = get()
|
||||||
|
fetchPlatformQueryResult = get()
|
||||||
|
}
|
||||||
}
|
}
|
27
web-app/src/main/kotlin/extras/RenderableComponent.kt
Normal file
27
web-app/src/main/kotlin/extras/RenderableComponent.kt
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package extras
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import react.RState
|
||||||
|
import react.setState
|
||||||
|
|
||||||
|
abstract class RenderableComponent<
|
||||||
|
T : Any,
|
||||||
|
S : RState
|
||||||
|
>(
|
||||||
|
props: Props<T>,
|
||||||
|
initialState: S
|
||||||
|
) : RenderableRootComponent<T, S>(props,initialState) {
|
||||||
|
|
||||||
|
protected abstract val stateFlow: Flow<S>
|
||||||
|
|
||||||
|
override fun componentDidMount() {
|
||||||
|
super.componentDidMount()
|
||||||
|
scope.launch {
|
||||||
|
stateFlow.collectLatest {
|
||||||
|
setState { state = it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
63
web-app/src/main/kotlin/extras/RenderableRootComponent.kt
Normal file
63
web-app/src/main/kotlin/extras/RenderableRootComponent.kt
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package extras
|
||||||
|
|
||||||
|
import com.arkivanov.decompose.value.Value
|
||||||
|
import com.arkivanov.decompose.value.ValueObserver
|
||||||
|
import extras.RenderableRootComponent.Props
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.cancel
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import 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 lateinit var scope: CoroutineScope
|
||||||
|
|
||||||
|
init {
|
||||||
|
state = initialState
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun componentDidMount() {
|
||||||
|
subscriptions.forEach { subscribe(it) }
|
||||||
|
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>
|
||||||
|
)
|
||||||
|
}
|
16
web-app/src/main/kotlin/extras/UniqueId.kt
Normal file
16
web-app/src/main/kotlin/extras/UniqueId.kt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package extras
|
||||||
|
|
||||||
|
import kotlinext.js.Object
|
||||||
|
import kotlinext.js.jsObject
|
||||||
|
|
||||||
|
var uniqueId: Long = 0L
|
||||||
|
|
||||||
|
internal fun Any.uniqueId(): Long {
|
||||||
|
var id: dynamic = asDynamic().__unique_id
|
||||||
|
if (id == undefined) {
|
||||||
|
id = ++uniqueId
|
||||||
|
Object.defineProperty<Any, Long>(this, "__unique_id", jsObject { value = id })
|
||||||
|
}
|
||||||
|
|
||||||
|
return id
|
||||||
|
}
|
11
web-app/src/main/kotlin/extras/Utils.kt
Normal file
11
web-app/src/main/kotlin/extras/Utils.kt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -1,24 +1,22 @@
|
|||||||
package home
|
package home
|
||||||
|
|
||||||
|
import com.shabinder.common.main.SpotiFlyerMain
|
||||||
|
import extras.RenderableComponent
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.css.*
|
import kotlinx.css.*
|
||||||
import react.*
|
import react.*
|
||||||
import styled.css
|
import styled.css
|
||||||
import styled.styledDiv
|
import styled.styledDiv
|
||||||
|
|
||||||
|
class HomeScreen(
|
||||||
|
props: Props<SpotiFlyerMain>,
|
||||||
|
override val stateFlow: Flow<State> = props.model.models.map { State(it) }
|
||||||
|
) : RenderableComponent<SpotiFlyerMain, HomeScreen.State>(
|
||||||
|
props,
|
||||||
|
initialState = State(data = SpotiFlyerMain.State())
|
||||||
|
) {
|
||||||
|
|
||||||
data class HomeScreenState(var link:String): RState
|
|
||||||
|
|
||||||
external interface HomeScreenProps : RProps {
|
|
||||||
var link: String
|
|
||||||
}
|
|
||||||
|
|
||||||
fun RBuilder.homeScreen(attrs:HomeScreenProps.() -> Unit): ReactElement {
|
|
||||||
return child(HomeScreen::class){
|
|
||||||
this.attrs(attrs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class HomeScreen(props:HomeScreenProps):RComponent<HomeScreenProps,HomeScreenState>(props) {
|
|
||||||
override fun RBuilder.render() {
|
override fun RBuilder.render() {
|
||||||
styledDiv{
|
styledDiv{
|
||||||
css {
|
css {
|
||||||
@ -28,24 +26,34 @@ class HomeScreen(props:HomeScreenProps):RComponent<HomeScreenProps,HomeScreenSta
|
|||||||
justifyContent = JustifyContent.center
|
justifyContent = JustifyContent.center
|
||||||
alignItems = Align.center
|
alignItems = Align.center
|
||||||
}
|
}
|
||||||
message {}
|
|
||||||
searchBar {
|
Message {
|
||||||
link = props.link
|
text = "Your Gateway to Nirvana, for FREE!"
|
||||||
}
|
}
|
||||||
iconList {
|
|
||||||
iconsAndPlatforms = iconList
|
SearchBar {
|
||||||
|
link = state.data.link
|
||||||
|
search = model::onLinkSearch
|
||||||
|
}
|
||||||
|
|
||||||
|
IconList {
|
||||||
|
iconsAndPlatforms = platformIconList
|
||||||
isBadge = false
|
isBadge = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
iconList{
|
IconList {
|
||||||
iconsAndPlatforms = badges
|
iconsAndPlatforms = badges
|
||||||
isBadge = true
|
isBadge = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class State(
|
||||||
|
var data: SpotiFlyerMain.State
|
||||||
|
):RState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private val iconList = mapOf(
|
private val platformIconList = mapOf(
|
||||||
"spotify.svg" to "https://open.spotify.com/",
|
"spotify.svg" to "https://open.spotify.com/",
|
||||||
"gaana.svg" to "https://www.gaana.com/",
|
"gaana.svg" to "https://www.gaana.com/",
|
||||||
"youtube.svg" to "https://www.youtube.com/",
|
"youtube.svg" to "https://www.youtube.com/",
|
||||||
|
@ -12,36 +12,37 @@ external interface IconListProps : RProps {
|
|||||||
var isBadge:Boolean
|
var isBadge:Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
fun RBuilder.iconList(attrs:IconListProps.() -> Unit): ReactElement {
|
@Suppress("FunctionName")
|
||||||
return child(IconList::class){
|
fun RBuilder.IconList(handler:IconListProps.() -> Unit): ReactElement {
|
||||||
this.attrs(attrs)
|
return child(iconList){
|
||||||
|
attrs {
|
||||||
|
handler()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class IconList(props: IconListProps):RComponent<IconListProps,RState>(props) {
|
private val iconList = functionalComponent<IconListProps>("IconList") { props ->
|
||||||
override fun RBuilder.render() {
|
styledDiv {
|
||||||
styledDiv {
|
css {
|
||||||
css {
|
+Styles.makeRow
|
||||||
+Styles.makeRow
|
margin(18.px)
|
||||||
margin(18.px)
|
if(props.isBadge) {
|
||||||
if(props.isBadge) {
|
alignItems = Align.end
|
||||||
alignItems = Align.end
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for((icon,platformLink) in props.iconsAndPlatforms){
|
}
|
||||||
styledA(href = platformLink){
|
for((icon,platformLink) in props.iconsAndPlatforms){
|
||||||
styledImg {
|
styledA(href = platformLink){
|
||||||
attrs {
|
styledImg {
|
||||||
src = icon
|
attrs {
|
||||||
}
|
src = icon
|
||||||
css {
|
}
|
||||||
classes = mutableListOf("glow-button")
|
css {
|
||||||
margin(8.px)
|
classes = mutableListOf("glow-button")
|
||||||
if (!props.isBadge) {
|
margin(8.px)
|
||||||
height = 42.px
|
if (!props.isBadge) {
|
||||||
width = 42.px
|
height = 42.px
|
||||||
borderRadius = 50.px
|
width = 42.px
|
||||||
}
|
borderRadius = 50.px
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,28 +6,26 @@ import styled.css
|
|||||||
import styled.styledDiv
|
import styled.styledDiv
|
||||||
import styled.styledH1
|
import styled.styledH1
|
||||||
|
|
||||||
|
|
||||||
data class MessageState(var link:String): RState
|
|
||||||
|
|
||||||
external interface MessageProps : RProps {
|
external interface MessageProps : RProps {
|
||||||
var text: String
|
var text: String
|
||||||
}
|
}
|
||||||
|
|
||||||
fun RBuilder.message(attrs:MessageProps.() -> Unit): ReactElement {
|
@Suppress("FunctionName")
|
||||||
return child(Message::class){
|
fun RBuilder.Message(handler:MessageProps.() -> Unit): ReactElement {
|
||||||
this.attrs(attrs)
|
return child(message){
|
||||||
|
attrs {
|
||||||
|
handler()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Message(props:MessageProps): RComponent<MessageProps,MessageState>(props) {
|
private val message = functionalComponent<MessageProps>("Message") { props->
|
||||||
override fun RBuilder.render() {
|
styledDiv {
|
||||||
styledDiv {
|
styledH1 {
|
||||||
styledH1 {
|
+ props.text
|
||||||
+"Your Gateway to Nirvana, for FREE!"
|
css {
|
||||||
css {
|
classes = mutableListOf("headingTitle")
|
||||||
classes = mutableListOf("headingTitle")
|
fontSize = 3.2.rem
|
||||||
fontSize = 3.2.rem
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,59 +7,54 @@ import org.w3c.dom.HTMLInputElement
|
|||||||
import react.*
|
import react.*
|
||||||
import styled.*
|
import styled.*
|
||||||
|
|
||||||
data class SearchbarState(var link:String):RState
|
|
||||||
|
|
||||||
external interface SearchbarProps : RProps {
|
external interface SearchbarProps : RProps {
|
||||||
var link: String
|
var link: String
|
||||||
|
var search:(String)->Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
fun RBuilder.searchBar(attrs:SearchbarProps.() -> Unit): ReactElement {
|
@Suppress("FunctionName")
|
||||||
return child(Searchbar::class){
|
fun RBuilder.SearchBar(handler:SearchbarProps.() -> Unit) = child(searchbar){
|
||||||
this.attrs(attrs)
|
attrs {
|
||||||
|
handler()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Searchbar(props: SearchbarProps): RComponent<SearchbarProps,SearchbarState>(props) {
|
|
||||||
init {
|
|
||||||
state = SearchbarState(props.link)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun RBuilder.render() {
|
|
||||||
styledDiv{
|
val searchbar = functionalComponent<SearchbarProps>("SearchBar"){ props ->
|
||||||
|
val (link,setLink) = useState(props.link)
|
||||||
|
|
||||||
|
styledDiv{
|
||||||
|
css {
|
||||||
|
classes = mutableListOf("searchBox")
|
||||||
|
}
|
||||||
|
styledInput(type = InputType.url){
|
||||||
|
attrs {
|
||||||
|
placeholder = "Search"
|
||||||
|
onChangeFunction = {
|
||||||
|
val target = it.target as HTMLInputElement
|
||||||
|
setLink(target.value)
|
||||||
|
}
|
||||||
|
value = link
|
||||||
|
}
|
||||||
css {
|
css {
|
||||||
classes = mutableListOf("searchBox")
|
classes = mutableListOf("searchInput")
|
||||||
}
|
}
|
||||||
styledInput(type = InputType.url){
|
}
|
||||||
attrs {
|
styledButton {
|
||||||
placeholder = "Search"
|
attrs {
|
||||||
onChangeFunction = {
|
onClickFunction = {
|
||||||
val target = it.target as HTMLInputElement
|
props.search(link)
|
||||||
setState{
|
|
||||||
link = target.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
value = state.link
|
|
||||||
}
|
|
||||||
css {
|
|
||||||
classes = mutableListOf("searchInput")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
styledButton {
|
css {
|
||||||
attrs {
|
classes = mutableListOf("searchButton")
|
||||||
onClickFunction = {
|
}
|
||||||
|
styledImg(src = "search.svg") {
|
||||||
}
|
|
||||||
}
|
|
||||||
css {
|
css {
|
||||||
classes = mutableListOf("searchButton")
|
classes = mutableListOf("search-icon")
|
||||||
}
|
|
||||||
styledImg(src = "search.svg") {
|
|
||||||
css {
|
|
||||||
classes = mutableListOf("search-icon")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
45
web-app/src/main/kotlin/list/CoverImage.kt
Normal file
45
web-app/src/main/kotlin/list/CoverImage.kt
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package list
|
||||||
|
|
||||||
|
import kotlinx.css.*
|
||||||
|
import kotlinx.html.id
|
||||||
|
import react.RProps
|
||||||
|
import react.rFunction
|
||||||
|
import react.useState
|
||||||
|
import styled.css
|
||||||
|
import styled.styledDiv
|
||||||
|
import styled.styledH1
|
||||||
|
import styled.styledImg
|
||||||
|
|
||||||
|
|
||||||
|
external interface CoverImageProps : RProps {
|
||||||
|
var coverImageURL: String
|
||||||
|
var coverName: String
|
||||||
|
}
|
||||||
|
|
||||||
|
val CoverImage = rFunction<CoverImageProps>("CoverImage"){ props ->
|
||||||
|
val (coverURL,setCoverURL) = useState(props.coverImageURL)
|
||||||
|
val (coverName,setCoverName) = useState(props.coverName)
|
||||||
|
|
||||||
|
styledDiv {
|
||||||
|
styledImg(src=coverURL){
|
||||||
|
css {
|
||||||
|
height = 300.px
|
||||||
|
width = 300.px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
styledH1 {
|
||||||
|
+coverName
|
||||||
|
css {
|
||||||
|
textAlign = TextAlign.center
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attrs {
|
||||||
|
id = "cover-image"
|
||||||
|
}
|
||||||
|
css {
|
||||||
|
display = Display.flex
|
||||||
|
alignItems = Align.center
|
||||||
|
flexDirection = FlexDirection.column
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
web-app/src/main/kotlin/list/ListScreen.kt
Normal file
39
web-app/src/main/kotlin/list/ListScreen.kt
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package list
|
||||||
|
|
||||||
|
import com.shabinder.common.list.SpotiFlyerList
|
||||||
|
import extras.RenderableComponent
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.css.*
|
||||||
|
import kotlinx.html.id
|
||||||
|
import react.RBuilder
|
||||||
|
import react.RState
|
||||||
|
import styled.css
|
||||||
|
import styled.styledDiv
|
||||||
|
|
||||||
|
class ListScreen(
|
||||||
|
props: Props<SpotiFlyerList>,
|
||||||
|
override val stateFlow: Flow<State> = props.model.models.map { State(it) }
|
||||||
|
) : RenderableComponent<SpotiFlyerList, ListScreen.State>(props,initialState = State(SpotiFlyerList.State())) {
|
||||||
|
|
||||||
|
override fun RBuilder.render() {
|
||||||
|
styledDiv {
|
||||||
|
attrs {
|
||||||
|
id = "list-screen-div"
|
||||||
|
}
|
||||||
|
css {
|
||||||
|
classes = mutableListOf("list-screen")
|
||||||
|
display = Display.flex
|
||||||
|
flexDirection = FlexDirection.column
|
||||||
|
flexGrow = 1.0
|
||||||
|
justifyContent = JustifyContent.center
|
||||||
|
alignItems = Align.center
|
||||||
|
backgroundColor = Color.white
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class State(
|
||||||
|
var data: SpotiFlyerList.State
|
||||||
|
):RState
|
||||||
|
}
|
13
web-app/src/main/kotlin/list/TrackItem.kt
Normal file
13
web-app/src/main/kotlin/list/TrackItem.kt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package list
|
||||||
|
|
||||||
|
import react.RProps
|
||||||
|
import react.rFunction
|
||||||
|
|
||||||
|
external interface TrackItemProps : RProps {
|
||||||
|
var coverImageURL: String
|
||||||
|
var coverName: String
|
||||||
|
}
|
||||||
|
|
||||||
|
val trackItem = rFunction<TrackItemProps>("Track-Item"){
|
||||||
|
|
||||||
|
}
|
34
web-app/src/main/kotlin/root/RootR.kt
Normal file
34
web-app/src/main/kotlin/root/RootR.kt
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package root
|
||||||
|
|
||||||
|
import com.arkivanov.decompose.RouterState
|
||||||
|
import com.shabinder.common.root.SpotiFlyerRoot
|
||||||
|
import com.shabinder.common.root.SpotiFlyerRoot.*
|
||||||
|
import extras.RenderableRootComponent
|
||||||
|
import extras.renderableChild
|
||||||
|
import home.HomeScreen
|
||||||
|
import list.ListScreen
|
||||||
|
import react.RBuilder
|
||||||
|
import react.RState
|
||||||
|
|
||||||
|
class RootR(props: Props<SpotiFlyerRoot>) : RenderableRootComponent<SpotiFlyerRoot, RootR.State>(
|
||||||
|
props = props,
|
||||||
|
initialState = State(routerState = props.model.routerState.value)
|
||||||
|
) {
|
||||||
|
private val component: Child
|
||||||
|
get() = model.routerState.value.activeChild.component
|
||||||
|
|
||||||
|
override fun RBuilder.render() {
|
||||||
|
when(component){
|
||||||
|
is Child.Main -> renderableChild(HomeScreen::class, (component as Child.Main).component)
|
||||||
|
is Child.List -> renderableChild(ListScreen::class, (component as Child.List).component)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
model.routerState.bindToState { routerState = it }
|
||||||
|
}
|
||||||
|
class State(
|
||||||
|
var routerState: RouterState<*, Child>,
|
||||||
|
) : RState
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user