almost complete rework of ui up to module ui
This commit is contained in:
client
features
auth
client
common
client
content
binary
client
src
jsMain
kotlin
dev
inmo
postssystem
features
content
binary
client
src
commonMain
kotlin
dev
inmo
postssystem
features
content
client
text
client
src
jsMain
kotlin
dev
inmo
postssystem
features
content
text
files
client
roles
client
manager
common
src
commonMain
kotlin
dev
inmo
postssystem
features
roles
manager
common
users
client
gradle
publicators/simple/client
services/posts/client
build.gradle
src
commonMain
kotlin
dev
inmo
postssystem
services
jsMain
kotlin
dev
inmo
postssystem
services
posts
client
targets/telegram/loader/client/src/commonMain/kotlin/dev/inmo/postssystem/targets/telegram/loader/client
@ -32,9 +32,6 @@ kotlin {
|
||||
api project(":postssystem.features.content.binary.client")
|
||||
|
||||
api project(":postssystem.services.posts.client")
|
||||
|
||||
api libs.microutils.fsm.common
|
||||
api libs.microutils.fsm.repos.common
|
||||
api libs.microutils.crypto
|
||||
|
||||
implementation compose.runtime
|
||||
|
@ -1,148 +0,0 @@
|
||||
package dev.inmo.postssystem.client
|
||||
|
||||
import dev.inmo.postssystem.client.ui.fsm.*
|
||||
import dev.inmo.postssystem.features.auth.client.installClientAuthenticator
|
||||
import dev.inmo.postssystem.features.auth.common.*
|
||||
import dev.inmo.postssystem.features.files.client.ClientReadFilesStorage
|
||||
import dev.inmo.postssystem.features.files.common.storage.ReadFilesStorage
|
||||
import dev.inmo.postssystem.features.roles.common.Role
|
||||
import dev.inmo.postssystem.features.roles.common.RolesStorage
|
||||
import dev.inmo.postssystem.features.roles.client.ClientRolesStorage
|
||||
import dev.inmo.postssystem.features.roles.manager.common.RolesManagerRoleSerializer
|
||||
import dev.inmo.postssystem.features.users.client.UsersStorageKtorClient
|
||||
import dev.inmo.postssystem.features.users.common.ReadUsersStorage
|
||||
import dev.inmo.postssystem.features.users.common.User
|
||||
import dev.inmo.micro_utils.common.Either
|
||||
import dev.inmo.micro_utils.coroutines.LinkedSupervisorScope
|
||||
import dev.inmo.micro_utils.fsm.common.StatesMachine
|
||||
import dev.inmo.micro_utils.fsm.common.dsl.FSMBuilder
|
||||
import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManagerRepo
|
||||
import dev.inmo.micro_utils.ktor.client.UnifiedRequester
|
||||
import dev.inmo.micro_utils.repos.KeyValueRepo
|
||||
import dev.inmo.postssystem.client.settings.DefaultSettings
|
||||
import dev.inmo.postssystem.client.settings.Settings
|
||||
import dev.inmo.postssystem.client.settings.auth.AuthSettings
|
||||
import dev.inmo.postssystem.client.settings.auth.DefaultAuthSettings
|
||||
import dev.inmo.postssystem.features.common.common.*
|
||||
import dev.inmo.postssystem.features.content.common.ContentSerializersModuleConfigurator
|
||||
import dev.inmo.postssystem.features.content.common.OtherContentSerializerModuleConfigurator
|
||||
import dev.inmo.postssystem.features.content.text.common.TextContentSerializerModuleConfigurator
|
||||
import dev.inmo.postssystem.features.status.client.StatusFeatureClient
|
||||
import dev.inmo.postssystem.publicators.simple.client.SimplePublicatorService
|
||||
import dev.inmo.postssystem.publicators.simple.client.SimplePublicatorServiceClient
|
||||
import dev.inmo.postssystem.services.posts.client.ClientPostsService
|
||||
import dev.inmo.postssystem.services.posts.common.*
|
||||
import io.ktor.client.HttpClient
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.serialization.BinaryFormat
|
||||
import kotlinx.serialization.StringFormat
|
||||
import kotlinx.serialization.cbor.Cbor
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
import org.koin.core.Koin
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.core.module.Module
|
||||
import org.koin.core.qualifier.*
|
||||
import org.koin.core.scope.Scope
|
||||
import org.koin.dsl.binds
|
||||
import org.koin.dsl.module
|
||||
|
||||
val UIScopeQualifier = StringQualifier("CoroutineScopeUI")
|
||||
val SettingsQualifier = StringQualifier("Settings")
|
||||
val RolesQualifier = StringQualifier("Roles")
|
||||
private val FSMHandlersBuilderQualifier = StringQualifier("FSMHandlersBuilder")
|
||||
|
||||
val defaultSerialFormat = Json {
|
||||
ignoreUnknownKeys = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Entrypoint for getting [org.koin.core.Koin] DI for the client
|
||||
*
|
||||
* @param repoFactory Factory for creating of [DefaultStatesManagerRepo] for [dev.inmo.postssystem.client.ui.fsm.UIFSM]
|
||||
*/
|
||||
fun baseKoin(
|
||||
defaultScope: CoroutineScope,
|
||||
settingsFactory: Scope.() -> KeyValueRepo<String, Any>,
|
||||
repoFactory: Scope.() -> DefaultStatesManagerRepo<UIFSMState>,
|
||||
handlersSetter: Pair<Scope, FSMBuilder<UIFSMState>>.() -> Unit
|
||||
): Koin = startKoin {
|
||||
modules(
|
||||
module {
|
||||
singleWithRandomQualifier<ContentSerializersModuleConfigurator.Element> { OtherContentSerializerModuleConfigurator }
|
||||
singleWithRandomQualifier<ContentSerializersModuleConfigurator.Element> { TextContentSerializerModuleConfigurator }
|
||||
singleWithRandomQualifier<SerializersModuleConfigurator.Element> { ContentSerializersModuleConfigurator(getAll()) }
|
||||
single { SerializersModuleConfigurator(getAll()) }
|
||||
|
||||
single {
|
||||
Json {
|
||||
ignoreUnknownKeys = true
|
||||
serializersModule = SerializersModule { get<SerializersModuleConfigurator>().apply { invoke() } }
|
||||
}
|
||||
}
|
||||
single<StringFormat> { get<Json>() }
|
||||
|
||||
single(SettingsQualifier) { settingsFactory() }
|
||||
single { DBDropper(get(SettingsQualifier)) }
|
||||
single(FSMHandlersBuilderQualifier) { handlersSetter }
|
||||
single { repoFactory() }
|
||||
single { defaultScope }
|
||||
single(UIScopeQualifier) { get<CoroutineScope>().LinkedSupervisorScope(Dispatchers.Main) }
|
||||
single<StatesMachine<UIFSMState>>(UIFSMQualifier) { UIFSM(get()) { (this@single to this@UIFSM).apply(get(
|
||||
FSMHandlersBuilderQualifier
|
||||
)) } }
|
||||
single<AuthSettings> { DefaultAuthSettings(get(SettingsQualifier), get(), koin, get()) }
|
||||
single<Settings> { DefaultSettings(get()) }
|
||||
|
||||
AdditionalModules.modules.forEach {
|
||||
it.apply { load() }
|
||||
}
|
||||
}
|
||||
)
|
||||
}.koin.apply {
|
||||
loadModules(
|
||||
listOf(
|
||||
module { single<Koin> { this@apply } }
|
||||
)
|
||||
)
|
||||
RolesManagerRoleSerializer // Just to activate it in JS client
|
||||
}
|
||||
|
||||
fun getAuthorizedFeaturesDIModule(
|
||||
serverUrl: String,
|
||||
initialAuthKey: Either<AuthKey, AuthTokenInfo>,
|
||||
onAuthKeyUpdated: suspend (AuthTokenInfo) -> Unit,
|
||||
onUserRetrieved: suspend (User?) -> Unit,
|
||||
onAuthKeyInvalidated: suspend () -> Unit
|
||||
): Module {
|
||||
val serverUrlQualifier = StringQualifier("serverUrl")
|
||||
val credsQualifier = StringQualifier("creds")
|
||||
|
||||
return module {
|
||||
single(createdAtStart = true) {
|
||||
HttpClient {
|
||||
installClientAuthenticator(serverUrl, get(), get(credsQualifier), onAuthKeyUpdated, onUserRetrieved, onAuthKeyInvalidated)
|
||||
}
|
||||
}
|
||||
single(credsQualifier) { initialAuthKey }
|
||||
single(serverUrlQualifier) { serverUrl }
|
||||
single<BinaryFormat> {
|
||||
Cbor {
|
||||
serializersModule = SerializersModule { get<SerializersModuleConfigurator>().apply { invoke() } }
|
||||
}
|
||||
}
|
||||
single { UnifiedRequester(get(), get()) }
|
||||
|
||||
single { StatusFeatureClient(get(serverUrlQualifier), get()) }
|
||||
|
||||
single<ReadFilesStorage> { ClientReadFilesStorage(get(serverUrlQualifier), get(), get()) }
|
||||
single<ReadUsersStorage> { UsersStorageKtorClient(get(serverUrlQualifier), get()) }
|
||||
single<RolesStorage<Role>> { ClientRolesStorage(get(serverUrlQualifier), get(), Role.serializer()) }
|
||||
single<PostsService> { ClientPostsService(get(serverUrlQualifier), get()) } binds arrayOf(
|
||||
ReadPostsService::class,
|
||||
WritePostsService::class
|
||||
)
|
||||
single<SimplePublicatorService> { SimplePublicatorServiceClient(get(serverUrlQualifier), get()) }
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package dev.inmo.postssystem.client.settings
|
||||
|
||||
import dev.inmo.postssystem.client.settings.auth.AuthSettings
|
||||
|
||||
|
||||
data class DefaultSettings(
|
||||
override val authSettings: AuthSettings
|
||||
) : Settings
|
@ -1,12 +0,0 @@
|
||||
package dev.inmo.postssystem.client.settings
|
||||
|
||||
import dev.inmo.postssystem.client.settings.auth.AuthSettings
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import org.koin.core.module.Module
|
||||
|
||||
interface Settings {
|
||||
val authSettings: AuthSettings
|
||||
|
||||
val authorizedDIModule: StateFlow<Module?>
|
||||
get() = authSettings.authorizedDIModule
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package dev.inmo.postssystem.client.ui.fsm
|
||||
|
||||
import dev.inmo.postssystem.features.auth.client.AuthUnavailableException
|
||||
import dev.inmo.micro_utils.fsm.common.*
|
||||
|
||||
interface UIFSMHandler<T : UIFSMState> : StatesHandler<T, UIFSMState> {
|
||||
suspend fun StatesMachine<in UIFSMState>.safeHandleState(state: T): UIFSMState?
|
||||
override suspend fun StatesMachine<in UIFSMState>.handleState(state: T): UIFSMState? {
|
||||
return runCatching {
|
||||
safeHandleState(state).also(::println)
|
||||
}.getOrElse {
|
||||
errorToNextStep(state, it) ?.let { return it } ?: throw it
|
||||
}.also(::println)
|
||||
}
|
||||
|
||||
suspend fun errorToNextStep(
|
||||
currentState: T,
|
||||
e: Throwable
|
||||
): UIFSMState? = when (e) {
|
||||
is AuthUnavailableException -> if (currentState is AuthUIFSMState) {
|
||||
currentState
|
||||
} else {
|
||||
AuthUIFSMState(currentState)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package dev.inmo.postssystem.client.ui.fsm
|
||||
|
||||
import dev.inmo.micro_utils.fsm.common.State
|
||||
import dev.inmo.micro_utils.serialization.typed_serializer.TypedSerializer
|
||||
import kotlinx.serialization.*
|
||||
|
||||
@Serializable(UIFSMStateSerializer::class)
|
||||
sealed interface UIFSMState : State {
|
||||
val from: UIFSMState?
|
||||
get() = null
|
||||
override val context: String
|
||||
get() = "main"
|
||||
}
|
||||
|
||||
object UIFSMStateSerializer : KSerializer<UIFSMState> by TypedSerializer(
|
||||
"auth" to AuthUIFSMState.serializer(),
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class AuthUIFSMState(
|
||||
override val from: UIFSMState? = CreatePostUIFSMState(),
|
||||
override val context: String = "main"
|
||||
) : UIFSMState
|
||||
val DefaultAuthUIFSMState = AuthUIFSMState()
|
||||
|
||||
@Serializable
|
||||
data class CreatePostUIFSMState(
|
||||
override val from: UIFSMState? = null,
|
||||
override val context: String = "main"
|
||||
) : UIFSMState
|
@ -1,23 +1,23 @@
|
||||
package dev.inmo.postssystem.client
|
||||
|
||||
import dev.inmo.postssystem.client.fsm.ui.*
|
||||
import dev.inmo.postssystem.client.ui.*
|
||||
import dev.inmo.postssystem.client.ui.fsm.*
|
||||
import dev.inmo.postssystem.client.ui.fsm.UIFSMStateSerializer
|
||||
import dev.inmo.postssystem.features.auth.client.ui.AuthUIModel
|
||||
import dev.inmo.postssystem.features.auth.client.ui.AuthUIViewModel
|
||||
import dev.inmo.postssystem.features.auth.common.AuthTokenInfo
|
||||
import dev.inmo.micro_utils.coroutines.ContextSafelyExceptionHandler
|
||||
import dev.inmo.micro_utils.fsm.common.CheckableHandlerHolder
|
||||
import dev.inmo.micro_utils.fsm.common.StatesMachine
|
||||
import dev.inmo.micro_utils.repos.mappers.withMapper
|
||||
import dev.inmo.micro_utils.serialization.typed_serializer.TypedSerializer
|
||||
import dev.inmo.postssystem.client.settings.auth.AuthSettings
|
||||
import dev.inmo.postssystem.features.auth.client.settings.AuthSettings
|
||||
import dev.inmo.postssystem.features.auth.client.ui.*
|
||||
import dev.inmo.postssystem.features.common.common.baseKoin
|
||||
import dev.inmo.postssystem.features.common.common.getAllDistinct
|
||||
import dev.inmo.postssystem.features.common.common.ui.fsm.UIFSMHandler
|
||||
import dev.inmo.postssystem.features.common.common.ui.fsm.UIFSMStateSerializer
|
||||
import dev.inmo.postssystem.services.posts.client.ui.create.*
|
||||
import kotlinx.browser.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.serializer
|
||||
import org.koin.core.Koin
|
||||
import org.koin.core.context.loadKoinModules
|
||||
@ -38,6 +38,9 @@ val defaultTypedSerializer = TypedSerializer<Any>(
|
||||
"Double" to Double.serializer(),
|
||||
"UIFSMState" to UIFSMStateSerializer
|
||||
)
|
||||
val defaultSerialFormat = Json {
|
||||
ignoreUnknownKeys = true
|
||||
}
|
||||
|
||||
fun baseKoin(): Koin {
|
||||
val anyToString: suspend Any.() -> String = {
|
||||
@ -81,66 +84,19 @@ fun baseKoin(): Koin {
|
||||
JSUIFSMStatesRepo(window.history)
|
||||
}
|
||||
) {
|
||||
val scope = first
|
||||
first.apply {
|
||||
second.apply {
|
||||
loadKoinModules(
|
||||
module {
|
||||
factory { document.getElementById("main") as HTMLElement }
|
||||
|
||||
factory<AuthUIModel> { DefaultAuthUIModel(get(), get()) }
|
||||
factory { AuthUIViewModel(get()) }
|
||||
factory { AuthView(get(), get(UIScopeQualifier)) }
|
||||
|
||||
factory<PostCreateUIModel> { DefaultPostCreateUIModel(get(), get()) }
|
||||
factory { PostCreateUIViewModel(get()) }
|
||||
factory { PostCreateView(get(), getAllDistinct(), get(UIScopeQualifier)) }
|
||||
}
|
||||
)
|
||||
strictlyOn<AuthUIFSMState>(get<AuthView>())
|
||||
|
||||
// Костыль, в JS на момент Пн дек 6 14:19:29 +06 2021 если использовать strictlyOn генерируются
|
||||
// некорректные безымянные классы (у них отсутствует метод handleState)
|
||||
class DefaultStateHandlerWrapper<T : UIFSMState>(
|
||||
private val klass: KClass<out UIFSMHandler<T>>,
|
||||
private val stateKlass: KClass<T>,
|
||||
private val qualifier: Qualifier? = null,
|
||||
private val parameters: ((T) -> ParametersHolder)? = null
|
||||
) : CheckableHandlerHolder<UIFSMState, UIFSMState> {
|
||||
override suspend fun StatesMachine<in UIFSMState>.handleState(state: UIFSMState): UIFSMState? {
|
||||
@Suppress("UNCHECKED_CAST", "NAME_SHADOWING")
|
||||
val state = state as T
|
||||
return runCatching {
|
||||
val authSettings = get<AuthSettings>()
|
||||
authSettings.loadingJob.join()
|
||||
if (authSettings.authorizedDIModule.value == null) {
|
||||
error("Can't perform state $state: Auth module was not initialized")
|
||||
} else {
|
||||
get<UIFSMHandler<T>>(klass, qualifier, parameters ?.let { { it(state) } }).run {
|
||||
handleState(state)
|
||||
getAllDistinct<UIFSMHandler.Registrator>().forEach {
|
||||
with(it) {
|
||||
include()
|
||||
}
|
||||
}
|
||||
}.getOrElse { e ->
|
||||
e.printStackTrace()
|
||||
AuthUIFSMState(state)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun checkHandleable(state: UIFSMState): Boolean = stateKlass.isInstance(state)
|
||||
}
|
||||
inline fun <reified T : UIFSMState> registerHandler(
|
||||
klass: KClass<out UIFSMHandler<T>>,
|
||||
qualifier: Qualifier? = null,
|
||||
parameters: ((T) -> ParametersHolder)? = null
|
||||
) = add(
|
||||
DefaultStateHandlerWrapper<T>(
|
||||
klass,
|
||||
T::class,
|
||||
qualifier,
|
||||
parameters
|
||||
)
|
||||
)
|
||||
|
||||
registerHandler(PostCreateView::class)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package dev.inmo.postssystem.client
|
||||
|
||||
import dev.inmo.postssystem.client.ui.fsm.*
|
||||
import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManagerRepo
|
||||
import dev.inmo.postssystem.features.common.common.ui.fsm.UIFSMState
|
||||
import kotlinx.browser.window
|
||||
import kotlinx.serialization.StringFormat
|
||||
import org.w3c.dom.*
|
||||
|
@ -1,14 +1,13 @@
|
||||
package dev.inmo.postssystem.client
|
||||
|
||||
import dev.inmo.postssystem.client.ui.fsm.UIFSMQualifier
|
||||
import dev.inmo.postssystem.client.ui.fsm.UIFSMState
|
||||
import dev.inmo.micro_utils.fsm.common.StatesMachine
|
||||
import dev.inmo.postssystem.features.common.common.DefaultQualifiers
|
||||
import dev.inmo.postssystem.features.common.common.ui.fsm.UIFSMState
|
||||
import kotlinx.browser.window
|
||||
|
||||
fun main() {
|
||||
window.addEventListener("load", {
|
||||
val koin = baseKoin()
|
||||
val uiStatesMachine = koin.get<StatesMachine<UIFSMState>>(UIFSMQualifier)
|
||||
val uiStatesMachine = koin.get<StatesMachine<UIFSMState>>(DefaultQualifiers.UIFSMQualifier)
|
||||
uiStatesMachine.start(koin.get())
|
||||
})
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
package dev.inmo.postssystem.client.fsm.ui
|
||||
|
||||
import kotlinx.browser.document
|
||||
import org.w3c.dom.Element
|
||||
|
||||
val mainContainer: Element
|
||||
get() = document.getElementById("main")!!
|
@ -1,19 +0,0 @@
|
||||
package dev.inmo.postssystem.client.utils
|
||||
|
||||
import dev.inmo.postssystem.features.files.common.FullFileInfo
|
||||
import dev.inmo.micro_utils.common.toArrayBuffer
|
||||
import io.ktor.utils.io.core.readBytes
|
||||
import kotlinx.browser.document
|
||||
import org.w3c.dom.HTMLAnchorElement
|
||||
import org.w3c.dom.url.URL
|
||||
import org.w3c.files.Blob
|
||||
|
||||
fun triggerDownloadFile(fullFileInfo: FullFileInfo) {
|
||||
val hiddenElement = document.createElement("a") as HTMLAnchorElement
|
||||
|
||||
val url = URL.createObjectURL(Blob(arrayOf(fullFileInfo.inputProvider().readBytes().toArrayBuffer())))
|
||||
hiddenElement.href = url
|
||||
hiddenElement.target = "_blank"
|
||||
hiddenElement.download = fullFileInfo.name.name
|
||||
hiddenElement.click()
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package dev.inmo.postssystem.client.utils
|
||||
|
||||
import androidx.compose.runtime.*
|
||||
import kotlinx.coroutines.*
|
||||
import org.jetbrains.compose.web.dom.DOMScope
|
||||
import org.w3c.dom.Element
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
fun Composition.linkWithJob(job: Job) {
|
||||
job.invokeOnCompletion {
|
||||
this@linkWithJob.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
fun Composition.linkWithContext(coroutineContext: CoroutineContext) = linkWithJob(coroutineContext.job)
|
||||
|
||||
suspend fun <TElement : Element> renderComposableAndLinkToContext(
|
||||
root: TElement,
|
||||
monotonicFrameClock: MonotonicFrameClock = DefaultMonotonicFrameClock,
|
||||
content: @Composable DOMScope<TElement>.() -> Unit
|
||||
): Composition = org.jetbrains.compose.web.renderComposable(root, monotonicFrameClock, content).apply {
|
||||
linkWithContext(
|
||||
currentCoroutineContext()
|
||||
)
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
package dev.inmo.postssystem.client.utils
|
||||
|
||||
import androidx.compose.runtime.MutableState
|
||||
import dev.inmo.postssystem.features.files.common.FullFileInfo
|
||||
import dev.inmo.micro_utils.common.*
|
||||
import dev.inmo.micro_utils.mime_types.KnownMimeTypes
|
||||
import dev.inmo.micro_utils.mime_types.findBuiltinMimeType
|
||||
import dev.inmo.postssystem.features.common.common.BytesBasedInputProvider
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import org.khronos.webgl.ArrayBuffer
|
||||
import org.w3c.dom.HTMLInputElement
|
||||
import org.w3c.dom.events.Event
|
||||
import org.w3c.files.FileReader
|
||||
import org.w3c.files.get
|
||||
|
||||
fun uploadFileCallbackForHTMLInputChange(
|
||||
onSet: (FullFileInfo) -> Unit
|
||||
): (Event) -> Unit = {
|
||||
(it.target as? HTMLInputElement) ?.apply {
|
||||
files ?.also { files ->
|
||||
files[0] ?.also { file ->
|
||||
val reader: FileReader = FileReader()
|
||||
|
||||
reader.onload = {
|
||||
val bytes = ((it.target.asDynamic()).result as ArrayBuffer).toByteArray()
|
||||
onSet(
|
||||
FullFileInfo(
|
||||
FileName(file.name),
|
||||
findBuiltinMimeType(file.type) ?: KnownMimeTypes.Any,
|
||||
BytesBasedInputProvider(bytes)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
reader.readAsArrayBuffer(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun fileCallbackForHTMLInputChange(
|
||||
onSet: (MPPFile) -> Unit
|
||||
): (Event) -> Unit = {
|
||||
(it.target as? HTMLInputElement) ?.apply {
|
||||
files ?.also { files ->
|
||||
files[0] ?.also { file ->
|
||||
onSet(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun uploadFileCallbackForHTMLInputChange(
|
||||
output: MutableState<FullFileInfo?>
|
||||
): (Event) -> Unit = uploadFileCallbackForHTMLInputChange {
|
||||
output.value = it
|
||||
}
|
||||
|
||||
fun uploadFileCallbackForHTMLInputChange(
|
||||
output: MutableStateFlow<FullFileInfo?>
|
||||
): (Event) -> Unit = uploadFileCallbackForHTMLInputChange {
|
||||
output.value = it
|
||||
}
|
||||
|
||||
fun fileCallbackForHTMLInputChange(
|
||||
output: MutableState<MPPFile?>
|
||||
): (Event) -> Unit = fileCallbackForHTMLInputChange {
|
||||
output.value = it
|
||||
}
|
||||
|
||||
fun fileCallbackForHTMLInputChange(
|
||||
output: MutableStateFlow<MPPFile?>
|
||||
): (Event) -> Unit = fileCallbackForHTMLInputChange {
|
||||
output.value = it
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
alias(libs.plugins.compose)
|
||||
}
|
||||
|
||||
apply from: "$mppProjectWithSerializationPresetPath"
|
||||
@ -11,8 +12,14 @@ kotlin {
|
||||
commonMain {
|
||||
dependencies {
|
||||
api project(":postssystem.features.common.client")
|
||||
api project(":postssystem.features.roles.client")
|
||||
api project(":postssystem.features.status.client")
|
||||
api project(":postssystem.features.auth.common")
|
||||
}
|
||||
}
|
||||
clientMain {
|
||||
dependencies {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
39
features/auth/client/src/commonMain/kotlin/dev/inmo/postssystem/features/auth/client/AuthorizedFeaturesModules.kt
Normal file
39
features/auth/client/src/commonMain/kotlin/dev/inmo/postssystem/features/auth/client/AuthorizedFeaturesModules.kt
Normal file
@ -0,0 +1,39 @@
|
||||
package dev.inmo.postssystem.features.auth.client
|
||||
|
||||
import dev.inmo.micro_utils.common.Either
|
||||
import dev.inmo.micro_utils.ktor.client.UnifiedRequester
|
||||
import dev.inmo.postssystem.features.auth.common.AuthKey
|
||||
import dev.inmo.postssystem.features.auth.common.AuthTokenInfo
|
||||
import dev.inmo.postssystem.features.common.common.AdditionalModules
|
||||
import dev.inmo.postssystem.features.status.client.StatusFeatureClient
|
||||
import dev.inmo.postssystem.features.users.common.User
|
||||
import io.ktor.client.HttpClient
|
||||
import org.koin.core.module.Module
|
||||
import org.koin.dsl.module
|
||||
|
||||
fun createAuthorizedFeaturesDIModule(
|
||||
serverUrl: String,
|
||||
initialAuthKey: Either<AuthKey, AuthTokenInfo>,
|
||||
onAuthKeyUpdated: suspend (AuthTokenInfo) -> Unit,
|
||||
onUserRetrieved: suspend (User?) -> Unit,
|
||||
onAuthKeyInvalidated: suspend () -> Unit
|
||||
): Module {
|
||||
return module {
|
||||
single(AuthorizedQualifiers.CredsQualifier) { initialAuthKey }
|
||||
single(AuthorizedQualifiers.ServerUrlQualifier) { serverUrl }
|
||||
|
||||
single (createdAtStart = true) {
|
||||
HttpClient {
|
||||
installClientAuthenticator(serverUrl, get(), get(AuthorizedQualifiers.CredsQualifier), onAuthKeyUpdated, onUserRetrieved, onAuthKeyInvalidated)
|
||||
}
|
||||
}
|
||||
single { UnifiedRequester(get(), get()) }
|
||||
single { StatusFeatureClient(get(AuthorizedQualifiers.ServerUrlQualifier), get()) }
|
||||
|
||||
AdditionalModules.Authorized.modules.forEach {
|
||||
with(it) {
|
||||
load()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
features/auth/client/src/commonMain/kotlin/dev/inmo/postssystem/features/auth/client/AuthorizedModuleLoader.kt
Normal file
15
features/auth/client/src/commonMain/kotlin/dev/inmo/postssystem/features/auth/client/AuthorizedModuleLoader.kt
Normal file
@ -0,0 +1,15 @@
|
||||
package dev.inmo.postssystem.features.auth.client
|
||||
|
||||
import dev.inmo.postssystem.features.common.common.AdditionalModules
|
||||
import dev.inmo.postssystem.features.common.common.ModuleLoader
|
||||
import org.koin.core.module.Module
|
||||
|
||||
private val AuthorizedAdditionalModules = AdditionalModules()
|
||||
val AdditionalModules.Companion.Authorized: AdditionalModules
|
||||
get() = AuthorizedAdditionalModules
|
||||
|
||||
fun AuthorizedModuleLoader(loadingBlock: Module.() -> Unit): ModuleLoader.ByCallback {
|
||||
val newModuleLoader = ModuleLoader.ByCallback(loadingBlock)
|
||||
AdditionalModules.Authorized.addModule(newModuleLoader)
|
||||
return newModuleLoader
|
||||
}
|
8
features/auth/client/src/commonMain/kotlin/dev/inmo/postssystem/features/auth/client/AuthorizedQualifiers.kt
Normal file
8
features/auth/client/src/commonMain/kotlin/dev/inmo/postssystem/features/auth/client/AuthorizedQualifiers.kt
Normal file
@ -0,0 +1,8 @@
|
||||
package dev.inmo.postssystem.features.auth.client
|
||||
|
||||
import org.koin.core.qualifier.StringQualifier
|
||||
|
||||
object AuthorizedQualifiers {
|
||||
val CredsQualifier = StringQualifier("creds")
|
||||
val ServerUrlQualifier = StringQualifier("serverUrl")
|
||||
}
|
59
features/auth/client/src/commonMain/kotlin/dev/inmo/postssystem/features/auth/client/BehindAuthUIFSMHandler.kt
Normal file
59
features/auth/client/src/commonMain/kotlin/dev/inmo/postssystem/features/auth/client/BehindAuthUIFSMHandler.kt
Normal file
@ -0,0 +1,59 @@
|
||||
package dev.inmo.postssystem.features.auth.client
|
||||
|
||||
import dev.inmo.micro_utils.fsm.common.CheckableHandlerHolder
|
||||
import dev.inmo.micro_utils.fsm.common.StatesMachine
|
||||
import dev.inmo.micro_utils.fsm.common.dsl.FSMBuilder
|
||||
import dev.inmo.postssystem.features.auth.client.settings.AuthSettings
|
||||
import dev.inmo.postssystem.features.auth.client.ui.AuthUIFSMState
|
||||
import dev.inmo.postssystem.features.common.common.ui.fsm.UIFSMHandler
|
||||
import dev.inmo.postssystem.features.common.common.ui.fsm.UIFSMState
|
||||
import org.koin.core.Koin
|
||||
import org.koin.core.parameter.ParametersHolder
|
||||
import org.koin.core.qualifier.Qualifier
|
||||
import org.koin.core.scope.Scope
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
// Костыль, в JS на момент Пн дек 6 14:19:29 +06 2021 если использовать strictlyOn генерируются
|
||||
// некорректные безымянные классы (у них отсутствует метод handleState)
|
||||
class DefaultStateHandlerWrapper<T : UIFSMState>(
|
||||
private val klass: KClass<out UIFSMHandler<T>>,
|
||||
private val koin: Koin,
|
||||
private val stateKlass: KClass<T>,
|
||||
private val qualifier: Qualifier? = null,
|
||||
private val parameters: ((T) -> ParametersHolder)? = null
|
||||
) : CheckableHandlerHolder<UIFSMState, UIFSMState> {
|
||||
override suspend fun StatesMachine<in UIFSMState>.handleState(state: UIFSMState): UIFSMState? {
|
||||
@Suppress("UNCHECKED_CAST", "NAME_SHADOWING")
|
||||
val state = state as T
|
||||
return runCatching {
|
||||
val authSettings = koin.get<AuthSettings>()
|
||||
authSettings.loadingJob.join()
|
||||
if (authSettings.authorizedDIModule.value == null) {
|
||||
error("Can't perform state $state: Auth module was not initialized")
|
||||
} else {
|
||||
koin.get<UIFSMHandler<T>>(klass, qualifier, parameters ?.let { { it(state) } }).run {
|
||||
handleState(state)
|
||||
}
|
||||
}
|
||||
}.getOrElse { e ->
|
||||
e.printStackTrace()
|
||||
AuthUIFSMState(state)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun checkHandleable(state: UIFSMState): Boolean = stateKlass.isInstance(state)
|
||||
}
|
||||
inline fun <reified T : UIFSMState> FSMBuilder<UIFSMState>.registerAfterAuthHandler(
|
||||
koin: Koin,
|
||||
klass: KClass<out UIFSMHandler<T>>,
|
||||
qualifier: Qualifier? = null,
|
||||
noinline parameters: ((T) -> ParametersHolder)? = null
|
||||
) = add(
|
||||
DefaultStateHandlerWrapper<T>(
|
||||
klass,
|
||||
koin,
|
||||
T::class,
|
||||
qualifier,
|
||||
parameters
|
||||
)
|
||||
)
|
27
features/auth/client/src/commonMain/kotlin/dev/inmo/postssystem/features/auth/client/ModuleLoader.kt
Normal file
27
features/auth/client/src/commonMain/kotlin/dev/inmo/postssystem/features/auth/client/ModuleLoader.kt
Normal file
@ -0,0 +1,27 @@
|
||||
package dev.inmo.postssystem.features.auth.client
|
||||
|
||||
import dev.inmo.micro_utils.common.Optional
|
||||
import dev.inmo.micro_utils.common.optional
|
||||
import dev.inmo.postssystem.features.auth.client.settings.AuthSettings
|
||||
import dev.inmo.postssystem.features.auth.client.settings.DefaultAuthSettings
|
||||
import dev.inmo.postssystem.features.auth.client.ui.*
|
||||
import dev.inmo.postssystem.features.common.common.*
|
||||
import dev.inmo.postssystem.features.common.common.ui.fsm.UIFSMExceptionHandler
|
||||
import dev.inmo.postssystem.features.common.common.ui.fsm.UIFSMState
|
||||
|
||||
val defaultModuleLoader = DefaultModuleLoader {
|
||||
single<AuthSettings> { DefaultAuthSettings(get(DefaultQualifiers.SettingsQualifier), get(), getKoin(), get()) }
|
||||
|
||||
singleWithRandomQualifier {
|
||||
UIFSMExceptionHandler { currentState, exception ->
|
||||
if (exception is AuthUnavailableException) {
|
||||
Optional.presented(AuthUIFSMState(currentState))
|
||||
} else {
|
||||
Optional.absent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
factory<AuthUIModel> { DefaultAuthUIModel(get(), get()) }
|
||||
factory { AuthUIViewModel(get()) }
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package dev.inmo.postssystem.client.settings.auth
|
||||
package dev.inmo.postssystem.features.auth.client.settings
|
||||
|
||||
import dev.inmo.postssystem.features.auth.client.ui.AuthUIError
|
||||
import dev.inmo.postssystem.features.auth.common.AuthCreds
|
@ -1,7 +1,5 @@
|
||||
package dev.inmo.postssystem.client.settings.auth
|
||||
package dev.inmo.postssystem.features.auth.client.settings
|
||||
|
||||
import dev.inmo.postssystem.client.DBDropper
|
||||
import dev.inmo.postssystem.client.getAuthorizedFeaturesDIModule
|
||||
import dev.inmo.postssystem.features.auth.client.AuthUnavailableException
|
||||
import dev.inmo.postssystem.features.auth.client.ui.*
|
||||
import dev.inmo.postssystem.features.auth.common.*
|
||||
@ -14,6 +12,8 @@ import dev.inmo.micro_utils.common.either
|
||||
import dev.inmo.micro_utils.coroutines.plus
|
||||
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||
import dev.inmo.micro_utils.repos.*
|
||||
import dev.inmo.postssystem.features.auth.client.createAuthorizedFeaturesDIModule
|
||||
import dev.inmo.postssystem.features.common.common.DBDropper
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.koin.core.Koin
|
||||
@ -73,7 +73,7 @@ data class DefaultAuthSettings(
|
||||
initialAuthKey: Either<AuthKey, AuthTokenInfo>,
|
||||
): AuthUIError? {
|
||||
val currentModule = authorizedDIModule.value
|
||||
val newModule = getAuthorizedFeaturesDIModule(
|
||||
val newModule = createAuthorizedFeaturesDIModule(
|
||||
serverUrl,
|
||||
initialAuthKey,
|
||||
{
|
14
features/auth/client/src/commonMain/kotlin/dev/inmo/postssystem/features/auth/client/ui/AuthUIFSMState.kt
Normal file
14
features/auth/client/src/commonMain/kotlin/dev/inmo/postssystem/features/auth/client/ui/AuthUIFSMState.kt
Normal file
@ -0,0 +1,14 @@
|
||||
package dev.inmo.postssystem.features.auth.client.ui
|
||||
|
||||
import dev.inmo.postssystem.features.common.common.ui.fsm.UIFSMState
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class AuthUIFSMState(
|
||||
override val from: UIFSMState?,
|
||||
override val context: String = "main"
|
||||
) : UIFSMState {
|
||||
companion object {
|
||||
val default = AuthUIFSMState(null)
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package dev.inmo.postssystem.features.auth.client.ui
|
||||
|
||||
import dev.inmo.postssystem.features.auth.common.AuthCreds
|
||||
import dev.inmo.postssystem.features.common.common.UIModel
|
||||
import dev.inmo.postssystem.features.common.common.ui.UIModel
|
||||
|
||||
interface AuthUIModel : UIModel<AuthUIState> {
|
||||
suspend fun initAuth(serverUrl: String, creds: AuthCreds)
|
||||
|
@ -1,7 +1,7 @@
|
||||
package dev.inmo.postssystem.features.auth.client.ui
|
||||
|
||||
import dev.inmo.postssystem.features.auth.common.AuthCreds
|
||||
import dev.inmo.postssystem.features.common.common.UIViewModel
|
||||
import dev.inmo.postssystem.features.common.common.ui.UIViewModel
|
||||
import dev.inmo.postssystem.features.users.common.Username
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
package dev.inmo.postssystem.client.ui
|
||||
package dev.inmo.postssystem.features.auth.client.ui
|
||||
|
||||
import dev.inmo.postssystem.client.settings.auth.AuthSettings
|
||||
import dev.inmo.postssystem.features.auth.client.ui.*
|
||||
import dev.inmo.postssystem.features.auth.client.settings.AuthSettings
|
||||
import dev.inmo.postssystem.features.auth.common.AuthCreds
|
||||
import dev.inmo.postssystem.features.common.common.AbstractUIModel
|
||||
import dev.inmo.postssystem.features.common.common.ui.AbstractUIModel
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -1,28 +1,38 @@
|
||||
package dev.inmo.postssystem.client.fsm.ui
|
||||
package dev.inmo.postssystem.features.auth.client
|
||||
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import dev.inmo.jsuikit.elements.*
|
||||
import dev.inmo.jsuikit.modifiers.*
|
||||
import dev.inmo.jsuikit.utils.Attrs
|
||||
import dev.inmo.postssystem.client.ui.fsm.*
|
||||
import dev.inmo.micro_utils.coroutines.compose.renderComposableAndLinkToContextAndRoot
|
||||
import dev.inmo.postssystem.features.auth.client.ui.*
|
||||
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
|
||||
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||
import dev.inmo.micro_utils.fsm.common.StatesMachine
|
||||
import dev.inmo.postssystem.client.utils.renderComposableAndLinkToContext
|
||||
import dev.inmo.postssystem.features.common.common.*
|
||||
import dev.inmo.postssystem.features.common.common.ui.JSView
|
||||
import dev.inmo.postssystem.features.common.common.ui.fsm.*
|
||||
import kotlinx.browser.document
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.dom.*
|
||||
import org.jetbrains.compose.web.attributes.InputType
|
||||
import org.jetbrains.compose.web.dom.*
|
||||
import org.jetbrains.compose.web.dom.Text
|
||||
import org.w3c.dom.*
|
||||
|
||||
val loader = DefaultModuleLoader {
|
||||
factory { AuthView(get(), get(DefaultQualifiers.UIScopeQualifier), getAllDistinct()) }
|
||||
singleWithRandomQualifier<UIFSMHandler.Registrator> {
|
||||
UIFSMHandler.Registrator {
|
||||
strictlyOn(get<AuthView>())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AuthView(
|
||||
private val viewModel: AuthUIViewModel,
|
||||
private val uiScope: CoroutineScope
|
||||
) : JSView<AuthUIFSMState>() {
|
||||
private val uiScope: CoroutineScope,
|
||||
defaultExceptionsHandlers: Iterable<UIFSMExceptionHandler>
|
||||
) : JSView<AuthUIFSMState>(defaultExceptionsHandlers) {
|
||||
|
||||
override suspend fun StatesMachine<in UIFSMState>.safeHandleState(
|
||||
htmlElement: HTMLElement,
|
||||
@ -36,7 +46,7 @@ class AuthView(
|
||||
val errorText = mutableStateOf<String?>(null)
|
||||
|
||||
val root = htmlElement.appendElement("div") {}
|
||||
val composition = renderComposableAndLinkToContext(root) {
|
||||
val composition = renderComposableAndLinkToContextAndRoot(root) {
|
||||
val authBtnDisabled = usernameState.value.isBlank() || passwordState.value.isBlank()
|
||||
|
||||
Flex(
|
@ -16,6 +16,11 @@ kotlin {
|
||||
api "io.ktor:ktor-client-auth:$ktor_version"
|
||||
api "io.ktor:ktor-client-logging:$ktor_version"
|
||||
|
||||
api libs.microutils.common.compose
|
||||
api libs.microutils.coroutines.compose
|
||||
api libs.microutils.fsm.common
|
||||
api libs.microutils.fsm.repos.common
|
||||
|
||||
api compose.runtime
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package dev.inmo.postssystem.features.common.common
|
||||
|
||||
object AdditionalModules {
|
||||
class AdditionalModules {
|
||||
private val additionalModules = mutableListOf<ModuleLoader>()
|
||||
val modules: List<ModuleLoader>
|
||||
get() = additionalModules.toList()
|
||||
@ -8,4 +8,8 @@ object AdditionalModules {
|
||||
fun addModule(moduleLoader: ModuleLoader): Boolean {
|
||||
return additionalModules.add(moduleLoader)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val Default = AdditionalModules()
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package dev.inmo.postssystem.client
|
||||
package dev.inmo.postssystem.features.common.common
|
||||
|
||||
import dev.inmo.micro_utils.pagination.utils.getAllByWithNextPaging
|
||||
import dev.inmo.micro_utils.repos.KeyValueRepo
|
67
features/common/client/src/commonMain/kotlin/dev/inmo/postssystem/features/common/common/DefaultModules.kt
Normal file
67
features/common/client/src/commonMain/kotlin/dev/inmo/postssystem/features/common/common/DefaultModules.kt
Normal file
@ -0,0 +1,67 @@
|
||||
package dev.inmo.postssystem.features.common.common
|
||||
|
||||
import dev.inmo.micro_utils.coroutines.LinkedSupervisorScope
|
||||
import dev.inmo.micro_utils.fsm.common.StatesMachine
|
||||
import dev.inmo.micro_utils.fsm.common.dsl.FSMBuilder
|
||||
import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManagerRepo
|
||||
import dev.inmo.micro_utils.repos.KeyValueRepo
|
||||
import dev.inmo.postssystem.features.common.common.ui.fsm.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.serialization.BinaryFormat
|
||||
import kotlinx.serialization.StringFormat
|
||||
import kotlinx.serialization.cbor.Cbor
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
import org.koin.core.Koin
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.core.scope.Scope
|
||||
import org.koin.dsl.module
|
||||
|
||||
/**
|
||||
* Entrypoint for getting [org.koin.core.Koin] DI for the client
|
||||
*
|
||||
* @param repoFactory Factory for creating of [DefaultStatesManagerRepo] for [dev.inmo.postssystem.client.ui.fsm.UIFSM]
|
||||
*/
|
||||
fun baseKoin(
|
||||
defaultScope: CoroutineScope,
|
||||
settingsFactory: Scope.() -> KeyValueRepo<String, Any>,
|
||||
repoFactory: Scope.() -> DefaultStatesManagerRepo<UIFSMState>,
|
||||
handlersSetter: Pair<Scope, FSMBuilder<UIFSMState>>.() -> Unit
|
||||
): Koin = startKoin {
|
||||
modules(
|
||||
module {
|
||||
single {
|
||||
Json {
|
||||
ignoreUnknownKeys = true
|
||||
serializersModule = SerializersModule { get<SerializersModuleConfigurator>().apply { invoke() } }
|
||||
}
|
||||
}
|
||||
single<StringFormat> { get<Json>() }
|
||||
single {
|
||||
Cbor { serializersModule = SerializersModule { get<SerializersModuleConfigurator>().apply { invoke() } } }
|
||||
}
|
||||
single<BinaryFormat> { get<Cbor>() }
|
||||
|
||||
single(DefaultQualifiers.SettingsQualifier) { settingsFactory() }
|
||||
single { DBDropper(get(DefaultQualifiers.SettingsQualifier)) }
|
||||
single(DefaultQualifiers.FSMHandlersBuilderQualifier) { handlersSetter }
|
||||
single { repoFactory() }
|
||||
single { defaultScope }
|
||||
single(DefaultQualifiers.UIScopeQualifier) { get<CoroutineScope>().LinkedSupervisorScope(Dispatchers.Main) }
|
||||
single<StatesMachine<UIFSMState>>(UIFSMQualifier) { UIFSM(get()) { (this@single to this@UIFSM).apply(get(
|
||||
DefaultQualifiers.FSMHandlersBuilderQualifier
|
||||
)) } }
|
||||
|
||||
AdditionalModules.Default.modules.forEach {
|
||||
it.apply { load() }
|
||||
}
|
||||
}
|
||||
)
|
||||
}.koin.apply {
|
||||
loadModules(
|
||||
listOf(
|
||||
module { single<Koin> { this@apply } }
|
||||
)
|
||||
)
|
||||
}
|
10
features/common/client/src/commonMain/kotlin/dev/inmo/postssystem/features/common/common/DefaultQualifiers.kt
Normal file
10
features/common/client/src/commonMain/kotlin/dev/inmo/postssystem/features/common/common/DefaultQualifiers.kt
Normal file
@ -0,0 +1,10 @@
|
||||
package dev.inmo.postssystem.features.common.common
|
||||
|
||||
import org.koin.core.qualifier.StringQualifier
|
||||
|
||||
object DefaultQualifiers {
|
||||
val SettingsQualifier = StringQualifier("Settings")
|
||||
val FSMHandlersBuilderQualifier = StringQualifier("FSMHandlersBuilder")
|
||||
val UIScopeQualifier = StringQualifier("CoroutineScopeUI")
|
||||
val UIFSMQualifier = StringQualifier("FSM")
|
||||
}
|
@ -2,6 +2,18 @@ package dev.inmo.postssystem.features.common.common
|
||||
|
||||
import org.koin.core.module.Module
|
||||
|
||||
fun interface ModuleLoader {
|
||||
interface ModuleLoader {
|
||||
fun Module.load()
|
||||
|
||||
class ByCallback(private val loadingBlock: Module.() -> Unit) : ModuleLoader {
|
||||
override fun Module.load() {
|
||||
loadingBlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun DefaultModuleLoader(loadingBlock: Module.() -> Unit): ModuleLoader.ByCallback {
|
||||
val newModuleLoader = ModuleLoader.ByCallback(loadingBlock)
|
||||
AdditionalModules.Default.addModule(newModuleLoader)
|
||||
return newModuleLoader
|
||||
}
|
||||
|
4
features/common/client/src/commonMain/kotlin/dev/inmo/postssystem/features/common/common/UIView.kt
4
features/common/client/src/commonMain/kotlin/dev/inmo/postssystem/features/common/common/UIView.kt
@ -1,4 +0,0 @@
|
||||
package dev.inmo.postssystem.features.common.common
|
||||
|
||||
interface UIView {
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package dev.inmo.postssystem.features.common.common
|
||||
package dev.inmo.postssystem.features.common.common.ui
|
||||
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
@ -1,4 +1,4 @@
|
||||
package dev.inmo.postssystem.features.common.common
|
||||
package dev.inmo.postssystem.features.common.common.ui
|
||||
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
4
features/common/client/src/commonMain/kotlin/dev/inmo/postssystem/features/common/common/ui/UIView.kt
Normal file
4
features/common/client/src/commonMain/kotlin/dev/inmo/postssystem/features/common/common/ui/UIView.kt
Normal file
@ -0,0 +1,4 @@
|
||||
package dev.inmo.postssystem.features.common.common.ui
|
||||
|
||||
interface UIView {
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package dev.inmo.postssystem.features.common.common
|
||||
package dev.inmo.postssystem.features.common.common.ui
|
||||
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
@ -7,9 +7,7 @@ interface UIViewModel<StateType> {
|
||||
val currentState: StateFlow<StateType>
|
||||
}
|
||||
|
||||
abstract class AbstractUIViewModel<StateType> : UIViewModel<StateType> {
|
||||
protected val _currentState = DefaultMVVMStateFlow(initState())
|
||||
abstract class AbstractUIViewModel<StateType>(initState: StateType) : UIViewModel<StateType> {
|
||||
protected val _currentState = DefaultMVVMStateFlow(initState)
|
||||
override val currentState: StateFlow<StateType> = _currentState.asStateFlow()
|
||||
|
||||
abstract fun initState(): StateType
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package dev.inmo.postssystem.client.ui.fsm
|
||||
package dev.inmo.postssystem.features.common.common.ui.fsm
|
||||
|
||||
import dev.inmo.micro_utils.fsm.common.dsl.FSMBuilder
|
||||
import dev.inmo.micro_utils.fsm.common.dsl.buildFSM
|
7
features/common/client/src/commonMain/kotlin/dev/inmo/postssystem/features/common/common/ui/fsm/UIFSMExceptionHandler.kt
Normal file
7
features/common/client/src/commonMain/kotlin/dev/inmo/postssystem/features/common/common/ui/fsm/UIFSMExceptionHandler.kt
Normal file
@ -0,0 +1,7 @@
|
||||
package dev.inmo.postssystem.features.common.common.ui.fsm
|
||||
|
||||
import dev.inmo.micro_utils.common.Optional
|
||||
|
||||
fun interface UIFSMExceptionHandler {
|
||||
suspend fun handle(currentState: UIFSMState, exception: Throwable): Optional<UIFSMState?>
|
||||
}
|
34
features/common/client/src/commonMain/kotlin/dev/inmo/postssystem/features/common/common/ui/fsm/UIFSMHandler.kt
Normal file
34
features/common/client/src/commonMain/kotlin/dev/inmo/postssystem/features/common/common/ui/fsm/UIFSMHandler.kt
Normal file
@ -0,0 +1,34 @@
|
||||
package dev.inmo.postssystem.features.common.common.ui.fsm
|
||||
|
||||
import dev.inmo.micro_utils.common.onPresented
|
||||
import dev.inmo.micro_utils.fsm.common.*
|
||||
import dev.inmo.micro_utils.fsm.common.dsl.FSMBuilder
|
||||
import org.koin.core.scope.Scope
|
||||
|
||||
interface UIFSMHandler<T : UIFSMState> : StatesHandler<T, UIFSMState> {
|
||||
fun interface Registrator {
|
||||
fun FSMBuilder<UIFSMState>.include()
|
||||
}
|
||||
val defaultExceptionsHandlers: Iterable<UIFSMExceptionHandler>
|
||||
|
||||
suspend fun StatesMachine<in UIFSMState>.safeHandleState(state: T): UIFSMState?
|
||||
override suspend fun StatesMachine<in UIFSMState>.handleState(state: T): UIFSMState? {
|
||||
return runCatching {
|
||||
safeHandleState(state).also(::println)
|
||||
}.getOrElse {
|
||||
errorToNextStep(state, it) ?.let { return it } ?: throw it
|
||||
}.also(::println)
|
||||
}
|
||||
|
||||
suspend fun errorToNextStep(
|
||||
currentState: T,
|
||||
e: Throwable
|
||||
): UIFSMState? {
|
||||
defaultExceptionsHandlers.forEach {
|
||||
it.handle(currentState, e).onPresented { state ->
|
||||
return state
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
17
features/common/client/src/commonMain/kotlin/dev/inmo/postssystem/features/common/common/ui/fsm/UIFSMState.kt
Normal file
17
features/common/client/src/commonMain/kotlin/dev/inmo/postssystem/features/common/common/ui/fsm/UIFSMState.kt
Normal file
@ -0,0 +1,17 @@
|
||||
package dev.inmo.postssystem.features.common.common.ui.fsm
|
||||
|
||||
import dev.inmo.micro_utils.fsm.common.State
|
||||
import dev.inmo.micro_utils.serialization.typed_serializer.TypedSerializer
|
||||
import kotlinx.serialization.*
|
||||
|
||||
@Serializable(UIFSMStateSerializer::class)
|
||||
interface UIFSMState : State {
|
||||
val from: UIFSMState?
|
||||
get() = null
|
||||
override val context: String
|
||||
get() = "main"
|
||||
}
|
||||
|
||||
object UIFSMStateSerializer : KSerializer<UIFSMState>, TypedSerializer<UIFSMState>(
|
||||
UIFSMState::class
|
||||
)
|
@ -1,12 +1,13 @@
|
||||
package dev.inmo.postssystem.client.fsm.ui
|
||||
package dev.inmo.postssystem.features.common.common.ui
|
||||
|
||||
import dev.inmo.postssystem.client.ui.fsm.UIFSMHandler
|
||||
import dev.inmo.postssystem.client.ui.fsm.UIFSMState
|
||||
import dev.inmo.micro_utils.fsm.common.StatesMachine
|
||||
import dev.inmo.postssystem.features.common.common.ui.fsm.*
|
||||
import kotlinx.browser.document
|
||||
import org.w3c.dom.HTMLElement
|
||||
|
||||
abstract class JSView<T : UIFSMState> : UIFSMHandler<T> {
|
||||
abstract class JSView<T : UIFSMState>(
|
||||
override val defaultExceptionsHandlers: Iterable<UIFSMExceptionHandler>
|
||||
) : UIFSMHandler<T> {
|
||||
open suspend fun StatesMachine<in UIFSMState>.safeHandleState(
|
||||
htmlElement: HTMLElement,
|
||||
state: T
|
@ -11,7 +11,7 @@ import org.koin.core.module.Module
|
||||
|
||||
object LoadingClientModule : ModuleLoader {
|
||||
init {
|
||||
AdditionalModules.addModule(this)
|
||||
AdditionalModules.Default.addModule(this)
|
||||
}
|
||||
|
||||
override fun Module.load() {
|
||||
|
11
features/content/client/src/commonMain/kotlin/dev/inmo/postssystem/features/content/client/ModuleLoader.kt
Normal file
11
features/content/client/src/commonMain/kotlin/dev/inmo/postssystem/features/content/client/ModuleLoader.kt
Normal file
@ -0,0 +1,11 @@
|
||||
package dev.inmo.postssystem.features.content.client
|
||||
|
||||
import dev.inmo.postssystem.features.common.common.*
|
||||
import dev.inmo.postssystem.features.content.common.ContentSerializersModuleConfigurator
|
||||
import dev.inmo.postssystem.features.content.common.OtherContentSerializerModuleConfigurator
|
||||
|
||||
val loader = DefaultModuleLoader {
|
||||
singleWithRandomQualifier<ContentSerializersModuleConfigurator.Element> { OtherContentSerializerModuleConfigurator }
|
||||
singleWithRandomQualifier<SerializersModuleConfigurator.Element> { ContentSerializersModuleConfigurator(getAll()) }
|
||||
single { SerializersModuleConfigurator(getAll()) }
|
||||
}
|
@ -12,7 +12,7 @@ import org.koin.core.module.Module
|
||||
|
||||
object LoadingClientModule : ModuleLoader {
|
||||
init {
|
||||
AdditionalModules.addModule(this)
|
||||
AdditionalModules.Default.addModule(this)
|
||||
}
|
||||
|
||||
override fun Module.load() {
|
||||
|
@ -12,6 +12,7 @@ kotlin {
|
||||
dependencies {
|
||||
api project(":postssystem.features.files.common")
|
||||
api project(":postssystem.features.common.client")
|
||||
api project(":postssystem.features.auth.client")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
9
features/files/client/src/commonMain/kotlin/dev/inmo/postssystem/features/files/client/ModuleLoader.kt
Normal file
9
features/files/client/src/commonMain/kotlin/dev/inmo/postssystem/features/files/client/ModuleLoader.kt
Normal file
@ -0,0 +1,9 @@
|
||||
package dev.inmo.postssystem.features.files.client
|
||||
|
||||
import dev.inmo.postssystem.features.auth.client.AuthorizedModuleLoader
|
||||
import dev.inmo.postssystem.features.auth.client.AuthorizedQualifiers
|
||||
import dev.inmo.postssystem.features.files.common.storage.ReadFilesStorage
|
||||
|
||||
val loader = AuthorizedModuleLoader {
|
||||
single<ReadFilesStorage> { ClientReadFilesStorage(get(AuthorizedQualifiers.ServerUrlQualifier), get(), get()) }
|
||||
}
|
@ -12,6 +12,7 @@ kotlin {
|
||||
dependencies {
|
||||
api project(":postssystem.features.roles.common")
|
||||
api project(":postssystem.features.common.client")
|
||||
api project(":postssystem.features.auth.client")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
10
features/roles/client/src/commonMain/kotlin/dev/inmo/postssystem/features/roles/client/ModuleLoader.kt
Normal file
10
features/roles/client/src/commonMain/kotlin/dev/inmo/postssystem/features/roles/client/ModuleLoader.kt
Normal file
@ -0,0 +1,10 @@
|
||||
package dev.inmo.postssystem.features.roles.client
|
||||
|
||||
import dev.inmo.postssystem.features.auth.client.AuthorizedModuleLoader
|
||||
import dev.inmo.postssystem.features.auth.client.AuthorizedQualifiers
|
||||
import dev.inmo.postssystem.features.roles.common.Role
|
||||
import dev.inmo.postssystem.features.roles.common.RolesStorage
|
||||
|
||||
val loader = AuthorizedModuleLoader {
|
||||
single<RolesStorage<Role>> { ClientRolesStorage(get(AuthorizedQualifiers.ServerUrlQualifier), get(), Role.serializer()) }
|
||||
}
|
@ -5,6 +5,8 @@ import dev.inmo.postssystem.features.roles.common.RoleSerializer
|
||||
import dev.inmo.micro_utils.serialization.typed_serializer.TypedSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
private val justForLoading = RolesManagerRoleSerializer
|
||||
|
||||
@Serializable(RolesManagerRoleSerializer::class)
|
||||
interface RolesManagerRole : Role {
|
||||
companion object {
|
||||
|
@ -12,6 +12,7 @@ kotlin {
|
||||
dependencies {
|
||||
api project(":postssystem.features.users.common")
|
||||
api project(":postssystem.features.common.client")
|
||||
api project(":postssystem.features.auth.client")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
9
features/users/client/src/commonMain/kotlin/dev/inmo/postssystem/features/users/client/ModuleLoader.kt
Normal file
9
features/users/client/src/commonMain/kotlin/dev/inmo/postssystem/features/users/client/ModuleLoader.kt
Normal file
@ -0,0 +1,9 @@
|
||||
package dev.inmo.postssystem.features.users.client
|
||||
|
||||
import dev.inmo.postssystem.features.auth.client.AuthorizedModuleLoader
|
||||
import dev.inmo.postssystem.features.auth.client.AuthorizedQualifiers
|
||||
import dev.inmo.postssystem.features.users.common.ReadUsersStorage
|
||||
|
||||
val loader = AuthorizedModuleLoader {
|
||||
single<ReadUsersStorage> { UsersStorageKtorClient(get(AuthorizedQualifiers.ServerUrlQualifier), get()) }
|
||||
}
|
@ -5,7 +5,7 @@ kotlin-serialization = "1.3.2"
|
||||
jsuikit = "0.0.45"
|
||||
compose = "1.1.1"
|
||||
microutils = "0.9.16"
|
||||
tgbotapi = "0.38.7"
|
||||
tgbotapi = "0.38.9"
|
||||
ktor = "1.6.8"
|
||||
klock = "2.6.3"
|
||||
exposed = "0.37.3"
|
||||
@ -44,6 +44,7 @@ microutils-repos-ktor-server = { module = "dev.inmo:micro_utils.repos.ktor.serve
|
||||
microutils-repos-exposed = { module = "dev.inmo:micro_utils.repos.exposed", version.ref = "microutils" }
|
||||
microutils-mimetypes = { module = "dev.inmo:micro_utils.mime_types", version.ref = "microutils" }
|
||||
microutils-coroutines = { module = "dev.inmo:micro_utils.coroutines", version.ref = "microutils" }
|
||||
microutils-coroutines-compose = { module = "dev.inmo:micro_utils.coroutines.compose", version.ref = "microutils" }
|
||||
microutils-ktor-server = { module = "dev.inmo:micro_utils.ktor.server", version.ref = "microutils" }
|
||||
microutils-serialization-typedserializer = { module = "dev.inmo:micro_utils.serialization.typed_serializer", version.ref = "microutils" }
|
||||
|
||||
|
@ -11,6 +11,7 @@ kotlin {
|
||||
commonMain {
|
||||
dependencies {
|
||||
api project(":postssystem.publicators.simple.common")
|
||||
api project(":postssystem.features.auth.client")
|
||||
api project(":postssystem.features.publication.client")
|
||||
}
|
||||
}
|
||||
|
8
publicators/simple/client/src/commonMain/kotlin/dev/inmo/postssystem/publicators/simple/client/ModuleLoader.kt
Normal file
8
publicators/simple/client/src/commonMain/kotlin/dev/inmo/postssystem/publicators/simple/client/ModuleLoader.kt
Normal file
@ -0,0 +1,8 @@
|
||||
package dev.inmo.postssystem.publicators.simple.client
|
||||
|
||||
import dev.inmo.postssystem.features.auth.client.AuthorizedModuleLoader
|
||||
import dev.inmo.postssystem.features.auth.client.AuthorizedQualifiers
|
||||
|
||||
val loader = AuthorizedModuleLoader {
|
||||
single<SimplePublicatorService> { SimplePublicatorServiceClient(get(AuthorizedQualifiers.ServerUrlQualifier), get()) }
|
||||
}
|
@ -13,6 +13,8 @@ kotlin {
|
||||
api project(":postssystem.features.common.client")
|
||||
api project(":postssystem.services.posts.common")
|
||||
api project(":postssystem.features.posts.client")
|
||||
api project(":postssystem.features.auth.client")
|
||||
api project(":postssystem.features.content.client")
|
||||
api project(":postssystem.publicators.simple.client")
|
||||
}
|
||||
}
|
||||
|
20
services/posts/client/src/commonMain/kotlin/dev/inmo/postssystem/services/posts/client/ModuleLoader.kt
Normal file
20
services/posts/client/src/commonMain/kotlin/dev/inmo/postssystem/services/posts/client/ModuleLoader.kt
Normal file
@ -0,0 +1,20 @@
|
||||
package dev.inmo.postssystem.services.posts.client
|
||||
|
||||
import dev.inmo.postssystem.features.auth.client.AuthorizedModuleLoader
|
||||
import dev.inmo.postssystem.features.auth.client.AuthorizedQualifiers
|
||||
import dev.inmo.postssystem.features.common.common.DefaultQualifiers
|
||||
import dev.inmo.postssystem.features.common.common.getAllDistinct
|
||||
import dev.inmo.postssystem.services.posts.client.ui.create.*
|
||||
import dev.inmo.postssystem.services.posts.common.*
|
||||
import org.koin.core.qualifier.Qualifier
|
||||
import org.koin.dsl.binds
|
||||
|
||||
val loader = AuthorizedModuleLoader {
|
||||
single<PostsService> { ClientPostsService(get(AuthorizedQualifiers.ServerUrlQualifier), get()) } binds arrayOf(
|
||||
ReadPostsService::class,
|
||||
WritePostsService::class
|
||||
)
|
||||
|
||||
factory<PostCreateUIModel> { DefaultPostCreateUIModel(get(), get()) }
|
||||
factory { PostCreateUIViewModel(get()) }
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package dev.inmo.postssystem.services.posts.client.ui.create
|
||||
|
||||
import dev.inmo.postssystem.features.common.common.AbstractUIModel
|
||||
import dev.inmo.postssystem.features.common.common.ui.AbstractUIModel
|
||||
import dev.inmo.postssystem.features.content.common.Content
|
||||
import dev.inmo.postssystem.publicators.simple.client.SimplePublicatorService
|
||||
import dev.inmo.postssystem.services.posts.common.FullNewPost
|
||||
|
@ -1,7 +1,7 @@
|
||||
package dev.inmo.postssystem.services.posts.client.ui.create
|
||||
|
||||
import dev.inmo.postssystem.features.common.common.UIModel
|
||||
import dev.inmo.postssystem.features.common.common.UIViewModel
|
||||
import dev.inmo.postssystem.features.common.common.ui.UIModel
|
||||
import dev.inmo.postssystem.features.common.common.ui.UIViewModel
|
||||
import dev.inmo.postssystem.features.content.common.Content
|
||||
|
||||
interface PostCreateUIModel : UIModel<PostCreateUIState>, UIViewModel<PostCreateUIState> {
|
||||
|
@ -1,6 +1,6 @@
|
||||
package dev.inmo.postssystem.services.posts.client.ui.create
|
||||
|
||||
import dev.inmo.postssystem.features.common.common.*
|
||||
import dev.inmo.postssystem.features.common.common.ui.UIViewModel
|
||||
import dev.inmo.postssystem.features.content.common.Content
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
|
@ -1,28 +1,48 @@
|
||||
package dev.inmo.postssystem.client.fsm.ui
|
||||
package dev.inmo.postssystem.services.posts.client
|
||||
|
||||
import androidx.compose.runtime.*
|
||||
import dev.inmo.jsuikit.elements.*
|
||||
import dev.inmo.jsuikit.modifiers.*
|
||||
import dev.inmo.jsuikit.utils.Attrs
|
||||
import dev.inmo.micro_utils.coroutines.compose.renderComposableAndLinkToContextAndRoot
|
||||
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
|
||||
import dev.inmo.micro_utils.fsm.common.StatesMachine
|
||||
import dev.inmo.postssystem.client.ui.fsm.CreatePostUIFSMState
|
||||
import dev.inmo.postssystem.client.ui.fsm.UIFSMState
|
||||
import dev.inmo.postssystem.client.utils.renderComposableAndLinkToContext
|
||||
import dev.inmo.postssystem.features.auth.client.registerAfterAuthHandler
|
||||
import dev.inmo.postssystem.features.common.common.*
|
||||
import dev.inmo.postssystem.features.common.common.ui.JSView
|
||||
import dev.inmo.postssystem.features.common.common.ui.fsm.*
|
||||
import dev.inmo.postssystem.features.content.client.ContentClientProvider
|
||||
import dev.inmo.postssystem.features.content.common.Content
|
||||
import dev.inmo.postssystem.services.posts.client.ui.create.PostCreateUIViewModel
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.jetbrains.compose.web.dom.Div
|
||||
import org.jetbrains.compose.web.dom.Text
|
||||
import org.w3c.dom.HTMLElement
|
||||
|
||||
val jsLoader = DefaultModuleLoader {
|
||||
factory { PostCreateView(get(), getAllDistinct(), get(DefaultQualifiers.UIScopeQualifier), getAllDistinct()) }
|
||||
|
||||
singleWithRandomQualifier<UIFSMHandler.Registrator> {
|
||||
UIFSMHandler.Registrator {
|
||||
registerAfterAuthHandler(getKoin(), PostCreateView::class)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class CreatePostUIFSMState(
|
||||
override val from: UIFSMState? = null,
|
||||
override val context: String = "main"
|
||||
) : UIFSMState
|
||||
|
||||
class PostCreateView(
|
||||
private val createPostCreateUIModel: PostCreateUIViewModel,
|
||||
private val contentClientProviders: List<ContentClientProvider>,
|
||||
private val uiScope: CoroutineScope
|
||||
) : JSView<CreatePostUIFSMState>() {
|
||||
private val uiScope: CoroutineScope,
|
||||
defaultExceptionsHandlers: Iterable<UIFSMExceptionHandler>
|
||||
) : JSView<CreatePostUIFSMState>(defaultExceptionsHandlers) {
|
||||
override suspend fun StatesMachine<in UIFSMState>.safeHandleState(
|
||||
htmlElement: HTMLElement,
|
||||
state: CreatePostUIFSMState
|
||||
@ -31,7 +51,7 @@ class PostCreateView(
|
||||
|
||||
val contentProvidersList = mutableStateListOf<Pair<ContentClientProvider, MutableState<Content?>>>()
|
||||
|
||||
renderComposableAndLinkToContext(htmlElement) {
|
||||
renderComposableAndLinkToContextAndRoot(htmlElement) {
|
||||
Flex(
|
||||
UIKitFlex.Alignment.Horizontal.Center
|
||||
) {
|
@ -9,7 +9,7 @@ val defaultTelegramTargetModuleLoader = TelegramTargetModuleLoader
|
||||
|
||||
object TelegramTargetModuleLoader : ModuleLoader {
|
||||
init {
|
||||
AdditionalModules.addModule(this)
|
||||
AdditionalModules.Default.addModule(this)
|
||||
}
|
||||
|
||||
override fun Module.load() {
|
||||
|
Reference in New Issue
Block a user