almost complete rework of ui up to module ui

This commit is contained in:
InsanusMokrassar 2022-03-23 23:21:02 +06:00
parent 72578f6b58
commit cb5de073fb
62 changed files with 499 additions and 471 deletions

View File

@ -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

View File

@ -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()) }
}
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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

View File

@ -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)
}
}
}.getOrElse { e ->
e.printStackTrace()
AuthUIFSMState(state)
}
getAllDistinct<UIFSMHandler.Registrator>().forEach {
with(it) {
include()
}
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)
}
}
}

View File

@ -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.*

View File

@ -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())
})
}

View File

@ -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")!!

View File

@ -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()
}

View File

@ -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()
)
}

View File

@ -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
}

View File

@ -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 {
}
}
}
}

View 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()
}
}
}
}

View 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
}

View 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")
}

View 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
)
)

View 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()) }
}

View File

@ -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

View File

@ -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,
{

View 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)
}
}

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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(

View File

@ -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
}
}

View File

@ -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()
}
}

View File

@ -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

View 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 } }
)
)
}

View 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")
}

View File

@ -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
}

View File

@ -1,4 +0,0 @@
package dev.inmo.postssystem.features.common.common
interface UIView {
}

View File

@ -1,4 +1,4 @@
package dev.inmo.postssystem.features.common.common
package dev.inmo.postssystem.features.common.common.ui
import kotlinx.coroutines.flow.MutableStateFlow

View File

@ -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

View File

@ -0,0 +1,4 @@
package dev.inmo.postssystem.features.common.common.ui
interface UIView {
}

View File

@ -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
}

View File

@ -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

View 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?>
}

View 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
}
}

View 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
)

View File

@ -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

View File

@ -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() {

View 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()) }
}

View File

@ -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() {

View File

@ -12,6 +12,7 @@ kotlin {
dependencies {
api project(":postssystem.features.files.common")
api project(":postssystem.features.common.client")
api project(":postssystem.features.auth.client")
}
}
}

View 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()) }
}

View File

@ -12,6 +12,7 @@ kotlin {
dependencies {
api project(":postssystem.features.roles.common")
api project(":postssystem.features.common.client")
api project(":postssystem.features.auth.client")
}
}
}

View 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()) }
}

View File

@ -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 {

View File

@ -12,6 +12,7 @@ kotlin {
dependencies {
api project(":postssystem.features.users.common")
api project(":postssystem.features.common.client")
api project(":postssystem.features.auth.client")
}
}
}

View 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()) }
}

View File

@ -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" }

View File

@ -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")
}
}

View 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()) }
}

View File

@ -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")
}
}

View 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()) }
}

View File

@ -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

View File

@ -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> {

View File

@ -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

View File

@ -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
) {

View File

@ -9,7 +9,7 @@ val defaultTelegramTargetModuleLoader = TelegramTargetModuleLoader
object TelegramTargetModuleLoader : ModuleLoader {
init {
AdditionalModules.addModule(this)
AdditionalModules.Default.addModule(this)
}
override fun Module.load() {