mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-25 10:24:31 +01:00
List & Main Stores and Models
This commit is contained in:
parent
a8f5941050
commit
97f9606863
@ -22,3 +22,12 @@ buildscript {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.21")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs = listOf("-Xallow-jvm-ir-dependencies", "-Xskip-prerelease-check",
|
||||
"-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||
"-Xuse-experimental=kotlinx.coroutines.TheAnnotationYouWantToDisable"
|
||||
)
|
||||
}
|
||||
}
|
@ -75,6 +75,8 @@ object MVIKotlin {
|
||||
const val rx = "com.arkivanov.mvikotlin:rx:$VERSION"
|
||||
const val mvikotlin = "com.arkivanov.mvikotlin:mvikotlin:$VERSION"
|
||||
const val mvikotlinMain = "com.arkivanov.mvikotlin:mvikotlin-main:$VERSION"
|
||||
const val coroutines = "com.arkivanov.mvikotlin:mvikotlin-extensions-coroutines:$VERSION"
|
||||
const val keepers = "com.arkivanov.mvikotlin:keepers:$VERSION"
|
||||
const val mvikotlinMainIosX64 = "com.arkivanov.mvikotlin:mvikotlin-main-iosx64:$VERSION"
|
||||
const val mvikotlinMainIosArm64 = "com.arkivanov.mvikotlin:mvikotlin-main-iosarm64:$VERSION"
|
||||
const val mvikotlinLogging = "com.arkivanov.mvikotlin:mvikotlin-logging:$VERSION"
|
||||
|
@ -10,12 +10,24 @@ kotlin {
|
||||
implementation(project(":common:dependency-injection"))
|
||||
implementation(project(":common:data-models"))
|
||||
implementation(project(":common:database"))
|
||||
//implementation(MVIKotlin.rx)
|
||||
implementation(SqlDelight.coroutineExtensions)
|
||||
implementation(MVIKotlin.coroutines)
|
||||
implementation(MVIKotlin.mvikotlin)
|
||||
implementation(MVIKotlin.mvikotlinExtensionsReaktive)
|
||||
implementation(Badoo.Reaktive.reaktive)
|
||||
//implementation(MVIKotlin.mvikotlinExtensionsReaktive)
|
||||
//implementation(Badoo.Reaktive.reaktive)
|
||||
implementation(Decompose.decompose)
|
||||
implementation(Decompose.extensionsCompose)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs = listOf("-Xallow-jvm-ir-dependencies", "-Xskip-prerelease-check",
|
||||
"-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||
"-Xuse-experimental=kotlinx.coroutines.TheAnnotationYouWantToDisable"
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package com.shabinder.common.list
|
||||
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
||||
import com.shabinder.common.PlatformQueryResult
|
||||
import com.shabinder.common.TrackDetails
|
||||
import com.shabinder.common.list.integration.SpotiFlyerListImpl
|
||||
import com.shabinder.database.Database
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface SpotiFlyerList {
|
||||
|
||||
val models: Flow<State>
|
||||
|
||||
/*
|
||||
* For Single Track Download -> list(that track)
|
||||
* For Download All -> Model.tracks
|
||||
* */
|
||||
fun onDownloadClicked(trackList:List<TrackDetails>)
|
||||
|
||||
/*
|
||||
* To Pop and return back to Main Screen
|
||||
* */
|
||||
fun onBackPressed()
|
||||
|
||||
interface Dependencies {
|
||||
val storeFactory: StoreFactory
|
||||
val database: Database
|
||||
val link: String
|
||||
fun listOutput(finished: Output.Finished)
|
||||
}
|
||||
sealed class Output {
|
||||
object Finished : Output()
|
||||
}
|
||||
data class State(
|
||||
val result:PlatformQueryResult? = null,
|
||||
val link:String = ""
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("FunctionName") // Factory function
|
||||
fun SpotiFlyerList(componentContext: ComponentContext, dependencies: SpotiFlyerList.Dependencies): SpotiFlyerList =
|
||||
SpotiFlyerListImpl(componentContext, dependencies)
|
@ -0,0 +1,37 @@
|
||||
package com.shabinder.common.list.integration
|
||||
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.mvikotlin.extensions.coroutines.states
|
||||
import com.shabinder.common.TrackDetails
|
||||
import com.shabinder.common.list.SpotiFlyerList
|
||||
import com.shabinder.common.list.SpotiFlyerList.Dependencies
|
||||
import com.shabinder.common.list.SpotiFlyerList.State
|
||||
import com.shabinder.common.list.store.SpotiFlyerListStore.Intent
|
||||
import com.shabinder.common.list.store.SpotiFlyerListStoreProvider
|
||||
import com.shabinder.common.utils.getStore
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
internal class SpotiFlyerListImpl(
|
||||
componentContext: ComponentContext,
|
||||
dependencies: Dependencies
|
||||
): SpotiFlyerList,ComponentContext by componentContext, Dependencies by dependencies {
|
||||
|
||||
private val store =
|
||||
instanceKeeper.getStore {
|
||||
SpotiFlyerListStoreProvider(
|
||||
storeFactory = storeFactory,
|
||||
database = database,
|
||||
link = link
|
||||
).provide()
|
||||
}
|
||||
|
||||
override val models: Flow<State> = store.states
|
||||
|
||||
override fun onDownloadClicked(trackList: List<TrackDetails>) {
|
||||
store.accept(Intent.StartDownload(trackList))
|
||||
}
|
||||
|
||||
override fun onBackPressed(){
|
||||
listOutput(SpotiFlyerList.Output.Finished)
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.shabinder.common.list.store
|
||||
|
||||
import com.arkivanov.mvikotlin.core.store.Store
|
||||
import com.shabinder.common.PlatformQueryResult
|
||||
import com.shabinder.common.TrackDetails
|
||||
import com.shabinder.common.list.SpotiFlyerList
|
||||
import com.shabinder.common.list.SpotiFlyerList.State
|
||||
import com.shabinder.common.list.store.SpotiFlyerListStore.*
|
||||
|
||||
internal interface SpotiFlyerListStore: Store<Intent, State, Nothing> {
|
||||
sealed class Intent {
|
||||
data class StartDownload(val trackList: List<TrackDetails>): Intent()
|
||||
data class SearchLink(val link: String): Intent()
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package com.shabinder.common.list.store
|
||||
|
||||
import com.arkivanov.mvikotlin.core.store.*
|
||||
import com.arkivanov.mvikotlin.extensions.coroutines.SuspendExecutor
|
||||
import com.shabinder.common.FetchPlatformQueryResult
|
||||
import com.shabinder.common.PlatformQueryResult
|
||||
import com.shabinder.common.list.SpotiFlyerList.State
|
||||
import com.shabinder.common.list.store.SpotiFlyerListStore.Intent
|
||||
import com.shabinder.database.Database
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal class SpotiFlyerListStoreProvider(
|
||||
private val storeFactory: StoreFactory,
|
||||
private val database: Database,
|
||||
private val link: String
|
||||
) {
|
||||
fun provide(): SpotiFlyerListStore =
|
||||
object : SpotiFlyerListStore, Store<Intent, State, Nothing> by storeFactory.create(
|
||||
name = "SpotiFlyerListStore",
|
||||
initialState = State(),
|
||||
bootstrapper = SimpleBootstrapper(Unit),
|
||||
executorFactory = ::ExecutorImpl,
|
||||
reducer = ReducerImpl
|
||||
) {}
|
||||
|
||||
private sealed class Result {
|
||||
data class ResultFetched(val result: PlatformQueryResult) : Result()
|
||||
data class SearchLink(val link: String) : Result()
|
||||
}
|
||||
|
||||
private inner class ExecutorImpl : SuspendExecutor<Intent, Unit, State, Result, Nothing>() {
|
||||
override suspend fun executeAction(action: Unit, getState: () -> State) {
|
||||
FetchPlatformQueryResult().query(link)?.let{
|
||||
dispatch(Result.ResultFetched(it))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun executeIntent(intent: Intent, getState: () -> State) {
|
||||
when (intent) {//TODO: Add Dispatchers where needed
|
||||
is Intent.StartDownload -> {}//TODO()
|
||||
is Intent.SearchLink -> FetchPlatformQueryResult().query(link)?.let{
|
||||
dispatch((Result.ResultFetched(it)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object ReducerImpl : Reducer<State, Result> {
|
||||
override fun State.reduce(result: Result): State =
|
||||
when (result) {
|
||||
is Result.ResultFetched -> copy(result = result.result)
|
||||
is Result.SearchLink -> copy(link = result.link)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,30 +1,43 @@
|
||||
package com.shabinder.common.main
|
||||
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
||||
import com.badoo.reaktive.base.Consumer
|
||||
import com.shabinder.common.DownloadRecord
|
||||
import com.shabinder.common.main.integration.SpotiFlyerMainImpl
|
||||
import com.shabinder.common.utils.Consumer
|
||||
import com.shabinder.database.Database
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface SpotiFlyerMain {
|
||||
|
||||
val models: Value<Model>
|
||||
val models: Flow<State>
|
||||
|
||||
fun onDownloadRecordClicked(link: String)
|
||||
/*
|
||||
* We Intend to Move to List Screen
|
||||
* Note: Implementation in Root
|
||||
* */
|
||||
fun onLinkSearch(link: String)
|
||||
|
||||
/*
|
||||
* Update TextBox's Text
|
||||
* */
|
||||
fun onInputLinkChanged(link: String)
|
||||
|
||||
interface Dependencies {
|
||||
fun mainOutput(searched: Output): Consumer<Output>
|
||||
val storeFactory: StoreFactory
|
||||
val database: Database
|
||||
val mainOutput: Consumer<Output>
|
||||
}
|
||||
sealed class Output {
|
||||
data class Search(val link: String) : Output()
|
||||
}
|
||||
|
||||
data class Model(
|
||||
val record: List<DownloadRecord>,
|
||||
val link: String
|
||||
data class State(
|
||||
val records: List<DownloadRecord> = emptyList(),
|
||||
val link: String = ""
|
||||
)
|
||||
sealed class Output {
|
||||
data class Searched(val link: String) : Output()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("FunctionName") // Factory function
|
||||
fun SpotiFlyerMain(componentContext: ComponentContext, dependencies: SpotiFlyerMain.Dependencies): SpotiFlyerMain =
|
||||
SpotiFlyerMainImpl(componentContext, dependencies)
|
||||
|
@ -1,23 +1,34 @@
|
||||
package com.shabinder.common.main.integration
|
||||
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.arkivanov.mvikotlin.extensions.coroutines.states
|
||||
import com.shabinder.common.main.SpotiFlyerMain
|
||||
import com.shabinder.common.main.SpotiFlyerMain.Dependencies
|
||||
import com.shabinder.common.main.SpotiFlyerMain.*
|
||||
import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent
|
||||
import com.shabinder.common.main.store.SpotiFlyerMainStoreProvider
|
||||
import com.shabinder.common.utils.getStore
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
internal class SpotiFlyerMainImpl(
|
||||
componentContext: ComponentContext,
|
||||
dependencies: Dependencies
|
||||
): SpotiFlyerMain,ComponentContext by componentContext, Dependencies by dependencies {
|
||||
override val models: Value<SpotiFlyerMain.Model>
|
||||
get() = TODO("Not yet implemented")
|
||||
|
||||
override fun onDownloadRecordClicked(link: String) {
|
||||
TODO("Not yet implemented")
|
||||
private val store =
|
||||
instanceKeeper.getStore {
|
||||
SpotiFlyerMainStoreProvider(
|
||||
storeFactory = storeFactory,
|
||||
database = database
|
||||
).provide()
|
||||
}
|
||||
|
||||
override val models: Flow<State> = store.states
|
||||
|
||||
override fun onLinkSearch(link: String) {
|
||||
mainOutput(Output.Search(link = link))
|
||||
}
|
||||
|
||||
override fun onInputLinkChanged(link: String) {
|
||||
TODO("Not yet implemented")
|
||||
store.accept(Intent.SetLink(link))
|
||||
}
|
||||
|
||||
}
|
@ -2,17 +2,14 @@ package com.shabinder.common.main.store
|
||||
|
||||
import com.arkivanov.mvikotlin.core.store.Store
|
||||
import com.shabinder.common.DownloadRecord
|
||||
import com.shabinder.common.main.SpotiFlyerMain
|
||||
import com.shabinder.common.main.store.SpotiFlyerMainStore.*
|
||||
|
||||
internal interface SpotiFlyerMainStore: Store<Intent, State, Nothing> {
|
||||
internal interface SpotiFlyerMainStore: Store<Intent, SpotiFlyerMain.State, Nothing> {
|
||||
sealed class Intent {
|
||||
data class OpenPlatform(val platformID:String,val platformLink:String):Intent()
|
||||
data class SetLink(val link:String):Intent()
|
||||
object GiveDonation : Intent()
|
||||
object ShareApp: Intent()
|
||||
}
|
||||
|
||||
data class State(
|
||||
val records: List<DownloadRecord> = emptyList(),
|
||||
val link: String = ""
|
||||
)
|
||||
}
|
@ -1,52 +1,79 @@
|
||||
package com.shabinder.common.main.store
|
||||
|
||||
import com.arkivanov.mvikotlin.core.store.Reducer
|
||||
import com.arkivanov.mvikotlin.core.store.SimpleBootstrapper
|
||||
import com.arkivanov.mvikotlin.core.store.Store
|
||||
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
||||
import com.arkivanov.mvikotlin.extensions.reaktive.ReaktiveExecutor
|
||||
import com.badoo.reaktive.observable.Observable
|
||||
import com.badoo.reaktive.observable.map
|
||||
import com.badoo.reaktive.observable.mapIterable
|
||||
import com.badoo.reaktive.observable.observeOn
|
||||
import com.badoo.reaktive.scheduler.mainScheduler
|
||||
import com.arkivanov.mvikotlin.extensions.coroutines.SuspendExecutor
|
||||
import com.shabinder.common.DownloadRecord
|
||||
import com.shabinder.common.database.asObservable
|
||||
import com.shabinder.common.giveDonation
|
||||
import com.shabinder.common.main.SpotiFlyerMain.State
|
||||
import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent
|
||||
import com.shabinder.common.main.store.SpotiFlyerMainStore.State
|
||||
import com.shabinder.common.openPlatform
|
||||
import com.shabinder.common.shareApp
|
||||
import com.shabinder.database.Database
|
||||
import com.squareup.sqldelight.Query
|
||||
import com.squareup.sqldelight.runtime.coroutines.asFlow
|
||||
import com.squareup.sqldelight.runtime.coroutines.mapToList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
internal class SpotiFlyerMainStoreProvider(
|
||||
private val storeFactory: StoreFactory,
|
||||
private val database: Database
|
||||
database: Database
|
||||
) {
|
||||
|
||||
fun provide(): SpotiFlyerMainStore =
|
||||
object : SpotiFlyerMainStore, Store<Intent, State, Nothing> by storeFactory.create(
|
||||
name = "SpotiFlyerHomeStore",
|
||||
initialState = State(),
|
||||
bootstrapper = SimpleBootstrapper(Unit),
|
||||
executorFactory = ::ExecutorImpl,
|
||||
reducer = ReducerImpl
|
||||
) {}
|
||||
|
||||
val updates: Flow<List<DownloadRecord>> =
|
||||
database.downloadRecordDatabaseQueries
|
||||
.selectAll()
|
||||
.asFlow()
|
||||
.mapToList(Dispatchers.Default)
|
||||
.map {
|
||||
it.map { record ->
|
||||
record.run{
|
||||
DownloadRecord(id, type, name, link, coverUrl, totalFiles)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private sealed class Result {
|
||||
data class ItemsLoaded(val items: List<DownloadRecord>) : Result()
|
||||
data class TextChanged(val text: String) : Result()
|
||||
data class LinkChanged(val link: String) : Result()
|
||||
}
|
||||
|
||||
private inner class ExecutorImpl : ReaktiveExecutor<Intent, Unit, State, Result, Nothing>() {
|
||||
override fun executeAction(action: Unit, getState: () -> State) {
|
||||
val updates: Observable<List<DownloadRecord>> =
|
||||
database.downloadRecordDatabaseQueries
|
||||
.selectAll()
|
||||
.asObservable(Query<com.shabinder.common.database.DownloadRecord>::executeAsList)
|
||||
.mapIterable { it.run {
|
||||
DownloadRecord(
|
||||
id, type, name, link, coverUrl, totalFiles
|
||||
)
|
||||
} }
|
||||
|
||||
|
||||
updates
|
||||
.observeOn(mainScheduler)
|
||||
.map(Result::ItemsLoaded)
|
||||
.subscribeScoped(onNext = ::dispatch)
|
||||
private inner class ExecutorImpl : SuspendExecutor<Intent, Unit, State, Result, Nothing>() {
|
||||
override suspend fun executeAction(action: Unit, getState: () -> State) {
|
||||
updates.collect {
|
||||
dispatch(Result.ItemsLoaded(it))
|
||||
}
|
||||
}
|
||||
override fun executeIntent(intent: Intent, getState: () -> State) {
|
||||
when (intent) {//TODO
|
||||
is Intent.OpenPlatform -> {}
|
||||
is Intent.GiveDonation -> {}
|
||||
is Intent.ShareApp -> {}
|
||||
|
||||
override suspend fun executeIntent(intent: Intent, getState: () -> State) {
|
||||
when (intent) {//TODO: Add Dispatchers where needed
|
||||
is Intent.OpenPlatform -> openPlatform(intent.platformID, intent.platformLink)
|
||||
is Intent.GiveDonation -> giveDonation()
|
||||
is Intent.ShareApp -> shareApp()
|
||||
is Intent.SetLink -> dispatch(Result.LinkChanged(link = intent.link))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object ReducerImpl : Reducer<State, Result> {
|
||||
override fun State.reduce(result: Result): State =
|
||||
when (result) {
|
||||
is Result.ItemsLoaded -> copy(records = result.items)
|
||||
is Result.LinkChanged -> copy(link = result.link)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.shabinder.common.utils
|
||||
|
||||
/*
|
||||
* Callback Utility
|
||||
* */
|
||||
interface Consumer<in T> {
|
||||
fun onCall(value: T)
|
||||
}
|
||||
|
||||
@Suppress("FunctionName") // Factory function
|
||||
inline fun <T> Consumer(crossinline block: (T) -> Unit): Consumer<T> =
|
||||
object : Consumer<T> {
|
||||
override fun onCall(value: T) {
|
||||
block(value)
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.shabinder.common.utils
|
||||
|
||||
import com.arkivanov.decompose.instancekeeper.InstanceKeeper
|
||||
import com.arkivanov.decompose.instancekeeper.getOrCreate
|
||||
import com.arkivanov.mvikotlin.core.store.Store
|
||||
|
||||
fun <T : Store<*, *, *>> InstanceKeeper.getStore(key: Any, factory: () -> T): T =
|
||||
getOrCreate(key) { StoreHolder(factory()) }
|
||||
.store
|
||||
|
||||
inline fun <reified T : Store<*, *, *>> InstanceKeeper.getStore(noinline factory: () -> T): T =
|
||||
getStore(T::class, factory)
|
||||
|
||||
private class StoreHolder<T : Store<*, *, *>>(
|
||||
val store: T
|
||||
) : InstanceKeeper.Instance {
|
||||
override fun onDestroy() {
|
||||
store.dispose()
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package com.shabinder.common.database
|
||||
|
||||
import com.badoo.reaktive.base.setCancellable
|
||||
import com.badoo.reaktive.observable.Observable
|
||||
import com.badoo.reaktive.observable.map
|
||||
import com.badoo.reaktive.observable.observable
|
||||
import com.badoo.reaktive.observable.observeOn
|
||||
import com.badoo.reaktive.scheduler.ioScheduler
|
||||
import com.squareup.sqldelight.Query
|
||||
|
||||
fun <T : Any, R> Query<T>.asObservable(execute: (Query<T>) -> R): Observable<R> =
|
||||
asObservable()
|
||||
.observeOn(ioScheduler)
|
||||
.map(execute)
|
||||
|
||||
fun <T : Any> Query<T>.asObservable(): Observable<Query<T>> =
|
||||
observable { emitter ->
|
||||
val listener =
|
||||
object : Query.Listener {
|
||||
override fun queryResultsChanged() {
|
||||
emitter.onNext(this@asObservable)
|
||||
}
|
||||
}
|
||||
|
||||
emitter.onNext(this@asObservable)
|
||||
addListener(listener)
|
||||
emitter.setCancellable { removeListener(listener) }
|
||||
}
|
@ -2,10 +2,11 @@ package com.shabinder.common
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Environment
|
||||
import co.touchlab.kermit.Kermit
|
||||
import com.shabinder.common.database.appContext
|
||||
import java.io.File
|
||||
|
||||
actual open class Dir {
|
||||
actual open class Dir actual constructor(logger: Kermit) {
|
||||
|
||||
private val context:Context
|
||||
get() = appContext
|
||||
@ -21,4 +22,6 @@ actual open class Dir {
|
||||
"SpotiFlyer"+ File.separator
|
||||
|
||||
actual fun isPresent(path: String): Boolean = File(path).exists()
|
||||
actual fun createDirectory(dirPath: String) {
|
||||
}
|
||||
}
|
@ -3,6 +3,13 @@ package com.shabinder.common
|
||||
import co.touchlab.kermit.Kermit
|
||||
import com.shabinder.common.utils.removeIllegalChars
|
||||
|
||||
expect fun openPlatform(platformID:String ,platformLink:String)
|
||||
|
||||
expect fun shareApp()
|
||||
|
||||
expect fun giveDonation()
|
||||
|
||||
|
||||
expect open class Dir(
|
||||
logger: Kermit
|
||||
) {
|
||||
|
@ -0,0 +1,8 @@
|
||||
package com.shabinder.common
|
||||
|
||||
//TODO
|
||||
class FetchPlatformQueryResult {
|
||||
suspend fun query(link:String): PlatformQueryResult?{
|
||||
return null
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user