almost complete rework of ui up to module ui
This commit is contained in:
parent
72578f6b58
commit
cb5de073fb
@ -32,9 +32,6 @@ kotlin {
|
|||||||
api project(":postssystem.features.content.binary.client")
|
api project(":postssystem.features.content.binary.client")
|
||||||
|
|
||||||
api project(":postssystem.services.posts.client")
|
api project(":postssystem.services.posts.client")
|
||||||
|
|
||||||
api libs.microutils.fsm.common
|
|
||||||
api libs.microutils.fsm.repos.common
|
|
||||||
api libs.microutils.crypto
|
api libs.microutils.crypto
|
||||||
|
|
||||||
implementation compose.runtime
|
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
|
package dev.inmo.postssystem.client
|
||||||
|
|
||||||
import dev.inmo.postssystem.client.fsm.ui.*
|
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.postssystem.features.auth.common.AuthTokenInfo
|
||||||
import dev.inmo.micro_utils.coroutines.ContextSafelyExceptionHandler
|
import dev.inmo.micro_utils.coroutines.ContextSafelyExceptionHandler
|
||||||
import dev.inmo.micro_utils.fsm.common.CheckableHandlerHolder
|
import dev.inmo.micro_utils.fsm.common.CheckableHandlerHolder
|
||||||
import dev.inmo.micro_utils.fsm.common.StatesMachine
|
import dev.inmo.micro_utils.fsm.common.StatesMachine
|
||||||
import dev.inmo.micro_utils.repos.mappers.withMapper
|
import dev.inmo.micro_utils.repos.mappers.withMapper
|
||||||
import dev.inmo.micro_utils.serialization.typed_serializer.TypedSerializer
|
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.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 dev.inmo.postssystem.services.posts.client.ui.create.*
|
||||||
import kotlinx.browser.*
|
import kotlinx.browser.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.serialization.builtins.serializer
|
import kotlinx.serialization.builtins.serializer
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.serializer
|
import kotlinx.serialization.serializer
|
||||||
import org.koin.core.Koin
|
import org.koin.core.Koin
|
||||||
import org.koin.core.context.loadKoinModules
|
import org.koin.core.context.loadKoinModules
|
||||||
@ -38,6 +38,9 @@ val defaultTypedSerializer = TypedSerializer<Any>(
|
|||||||
"Double" to Double.serializer(),
|
"Double" to Double.serializer(),
|
||||||
"UIFSMState" to UIFSMStateSerializer
|
"UIFSMState" to UIFSMStateSerializer
|
||||||
)
|
)
|
||||||
|
val defaultSerialFormat = Json {
|
||||||
|
ignoreUnknownKeys = true
|
||||||
|
}
|
||||||
|
|
||||||
fun baseKoin(): Koin {
|
fun baseKoin(): Koin {
|
||||||
val anyToString: suspend Any.() -> String = {
|
val anyToString: suspend Any.() -> String = {
|
||||||
@ -81,66 +84,19 @@ fun baseKoin(): Koin {
|
|||||||
JSUIFSMStatesRepo(window.history)
|
JSUIFSMStatesRepo(window.history)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
val scope = first
|
||||||
first.apply {
|
first.apply {
|
||||||
second.apply {
|
second.apply {
|
||||||
loadKoinModules(
|
loadKoinModules(
|
||||||
module {
|
module {
|
||||||
factory { document.getElementById("main") as HTMLElement }
|
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>())
|
getAllDistinct<UIFSMHandler.Registrator>().forEach {
|
||||||
|
with(it) {
|
||||||
// Костыль, в JS на момент Пн дек 6 14:19:29 +06 2021 если использовать strictlyOn генерируются
|
include()
|
||||||
// некорректные безымянные классы (у них отсутствует метод 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
package dev.inmo.postssystem.client
|
||||||
|
|
||||||
import dev.inmo.postssystem.client.ui.fsm.*
|
|
||||||
import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManagerRepo
|
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.browser.window
|
||||||
import kotlinx.serialization.StringFormat
|
import kotlinx.serialization.StringFormat
|
||||||
import org.w3c.dom.*
|
import org.w3c.dom.*
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
package dev.inmo.postssystem.client
|
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.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
|
import kotlinx.browser.window
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
window.addEventListener("load", {
|
window.addEventListener("load", {
|
||||||
val koin = baseKoin()
|
val koin = baseKoin()
|
||||||
val uiStatesMachine = koin.get<StatesMachine<UIFSMState>>(UIFSMQualifier)
|
val uiStatesMachine = koin.get<StatesMachine<UIFSMState>>(DefaultQualifiers.UIFSMQualifier)
|
||||||
uiStatesMachine.start(koin.get())
|
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.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
id "com.android.library"
|
id "com.android.library"
|
||||||
|
alias(libs.plugins.compose)
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
@ -11,8 +12,14 @@ kotlin {
|
|||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
api project(":postssystem.features.common.client")
|
api project(":postssystem.features.common.client")
|
||||||
|
api project(":postssystem.features.roles.client")
|
||||||
|
api project(":postssystem.features.status.client")
|
||||||
api project(":postssystem.features.auth.common")
|
api project(":postssystem.features.auth.common")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
clientMain {
|
||||||
|
dependencies {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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")
|
||||||
|
}
|
@ -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
|
||||||
|
)
|
||||||
|
)
|
@ -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.client.ui.AuthUIError
|
||||||
import dev.inmo.postssystem.features.auth.common.AuthCreds
|
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.AuthUnavailableException
|
||||||
import dev.inmo.postssystem.features.auth.client.ui.*
|
import dev.inmo.postssystem.features.auth.client.ui.*
|
||||||
import dev.inmo.postssystem.features.auth.common.*
|
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.plus
|
||||||
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
import dev.inmo.micro_utils.repos.*
|
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.*
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import org.koin.core.Koin
|
import org.koin.core.Koin
|
||||||
@ -73,7 +73,7 @@ data class DefaultAuthSettings(
|
|||||||
initialAuthKey: Either<AuthKey, AuthTokenInfo>,
|
initialAuthKey: Either<AuthKey, AuthTokenInfo>,
|
||||||
): AuthUIError? {
|
): AuthUIError? {
|
||||||
val currentModule = authorizedDIModule.value
|
val currentModule = authorizedDIModule.value
|
||||||
val newModule = getAuthorizedFeaturesDIModule(
|
val newModule = createAuthorizedFeaturesDIModule(
|
||||||
serverUrl,
|
serverUrl,
|
||||||
initialAuthKey,
|
initialAuthKey,
|
||||||
{
|
{
|
@ -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
|
package dev.inmo.postssystem.features.auth.client.ui
|
||||||
|
|
||||||
import dev.inmo.postssystem.features.auth.common.AuthCreds
|
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> {
|
interface AuthUIModel : UIModel<AuthUIState> {
|
||||||
suspend fun initAuth(serverUrl: String, creds: AuthCreds)
|
suspend fun initAuth(serverUrl: String, creds: AuthCreds)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package dev.inmo.postssystem.features.auth.client.ui
|
package dev.inmo.postssystem.features.auth.client.ui
|
||||||
|
|
||||||
import dev.inmo.postssystem.features.auth.common.AuthCreds
|
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 dev.inmo.postssystem.features.users.common.Username
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
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.settings.AuthSettings
|
||||||
import dev.inmo.postssystem.features.auth.client.ui.*
|
|
||||||
import dev.inmo.postssystem.features.auth.common.AuthCreds
|
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.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
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.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import dev.inmo.jsuikit.elements.*
|
import dev.inmo.jsuikit.elements.*
|
||||||
import dev.inmo.jsuikit.modifiers.*
|
import dev.inmo.jsuikit.modifiers.*
|
||||||
import dev.inmo.jsuikit.utils.Attrs
|
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.postssystem.features.auth.client.ui.*
|
||||||
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
|
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
|
||||||
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
import dev.inmo.micro_utils.fsm.common.StatesMachine
|
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.browser.document
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.dom.*
|
import kotlinx.dom.*
|
||||||
import org.jetbrains.compose.web.attributes.InputType
|
import org.jetbrains.compose.web.attributes.InputType
|
||||||
import org.jetbrains.compose.web.dom.*
|
|
||||||
import org.jetbrains.compose.web.dom.Text
|
import org.jetbrains.compose.web.dom.Text
|
||||||
import org.w3c.dom.*
|
import org.w3c.dom.*
|
||||||
|
|
||||||
|
val loader = DefaultModuleLoader {
|
||||||
|
factory { AuthView(get(), get(DefaultQualifiers.UIScopeQualifier), getAllDistinct()) }
|
||||||
|
singleWithRandomQualifier<UIFSMHandler.Registrator> {
|
||||||
|
UIFSMHandler.Registrator {
|
||||||
|
strictlyOn(get<AuthView>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class AuthView(
|
class AuthView(
|
||||||
private val viewModel: AuthUIViewModel,
|
private val viewModel: AuthUIViewModel,
|
||||||
private val uiScope: CoroutineScope
|
private val uiScope: CoroutineScope,
|
||||||
) : JSView<AuthUIFSMState>() {
|
defaultExceptionsHandlers: Iterable<UIFSMExceptionHandler>
|
||||||
|
) : JSView<AuthUIFSMState>(defaultExceptionsHandlers) {
|
||||||
|
|
||||||
override suspend fun StatesMachine<in UIFSMState>.safeHandleState(
|
override suspend fun StatesMachine<in UIFSMState>.safeHandleState(
|
||||||
htmlElement: HTMLElement,
|
htmlElement: HTMLElement,
|
||||||
@ -36,7 +46,7 @@ class AuthView(
|
|||||||
val errorText = mutableStateOf<String?>(null)
|
val errorText = mutableStateOf<String?>(null)
|
||||||
|
|
||||||
val root = htmlElement.appendElement("div") {}
|
val root = htmlElement.appendElement("div") {}
|
||||||
val composition = renderComposableAndLinkToContext(root) {
|
val composition = renderComposableAndLinkToContextAndRoot(root) {
|
||||||
val authBtnDisabled = usernameState.value.isBlank() || passwordState.value.isBlank()
|
val authBtnDisabled = usernameState.value.isBlank() || passwordState.value.isBlank()
|
||||||
|
|
||||||
Flex(
|
Flex(
|
@ -16,6 +16,11 @@ kotlin {
|
|||||||
api "io.ktor:ktor-client-auth:$ktor_version"
|
api "io.ktor:ktor-client-auth:$ktor_version"
|
||||||
api "io.ktor:ktor-client-logging:$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
|
api compose.runtime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package dev.inmo.postssystem.features.common.common
|
package dev.inmo.postssystem.features.common.common
|
||||||
|
|
||||||
object AdditionalModules {
|
class AdditionalModules {
|
||||||
private val additionalModules = mutableListOf<ModuleLoader>()
|
private val additionalModules = mutableListOf<ModuleLoader>()
|
||||||
val modules: List<ModuleLoader>
|
val modules: List<ModuleLoader>
|
||||||
get() = additionalModules.toList()
|
get() = additionalModules.toList()
|
||||||
@ -8,4 +8,8 @@ object AdditionalModules {
|
|||||||
fun addModule(moduleLoader: ModuleLoader): Boolean {
|
fun addModule(moduleLoader: ModuleLoader): Boolean {
|
||||||
return additionalModules.add(moduleLoader)
|
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.pagination.utils.getAllByWithNextPaging
|
||||||
import dev.inmo.micro_utils.repos.KeyValueRepo
|
import dev.inmo.micro_utils.repos.KeyValueRepo
|
@ -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 } }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
@ -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
|
import org.koin.core.module.Module
|
||||||
|
|
||||||
fun interface ModuleLoader {
|
interface ModuleLoader {
|
||||||
fun Module.load()
|
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
|
||||||
}
|
}
|
||||||
|
@ -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
|
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.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
@ -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.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
@ -7,9 +7,7 @@ interface UIViewModel<StateType> {
|
|||||||
val currentState: StateFlow<StateType>
|
val currentState: StateFlow<StateType>
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class AbstractUIViewModel<StateType> : UIViewModel<StateType> {
|
abstract class AbstractUIViewModel<StateType>(initState: StateType) : UIViewModel<StateType> {
|
||||||
protected val _currentState = DefaultMVVMStateFlow(initState())
|
protected val _currentState = DefaultMVVMStateFlow(initState)
|
||||||
override val currentState: StateFlow<StateType> = _currentState.asStateFlow()
|
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.FSMBuilder
|
||||||
import dev.inmo.micro_utils.fsm.common.dsl.buildFSM
|
import dev.inmo.micro_utils.fsm.common.dsl.buildFSM
|
@ -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?>
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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.micro_utils.fsm.common.StatesMachine
|
||||||
|
import dev.inmo.postssystem.features.common.common.ui.fsm.*
|
||||||
import kotlinx.browser.document
|
import kotlinx.browser.document
|
||||||
import org.w3c.dom.HTMLElement
|
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(
|
open suspend fun StatesMachine<in UIFSMState>.safeHandleState(
|
||||||
htmlElement: HTMLElement,
|
htmlElement: HTMLElement,
|
||||||
state: T
|
state: T
|
@ -11,7 +11,7 @@ import org.koin.core.module.Module
|
|||||||
|
|
||||||
object LoadingClientModule : ModuleLoader {
|
object LoadingClientModule : ModuleLoader {
|
||||||
init {
|
init {
|
||||||
AdditionalModules.addModule(this)
|
AdditionalModules.Default.addModule(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Module.load() {
|
override fun Module.load() {
|
||||||
|
@ -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 {
|
object LoadingClientModule : ModuleLoader {
|
||||||
init {
|
init {
|
||||||
AdditionalModules.addModule(this)
|
AdditionalModules.Default.addModule(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Module.load() {
|
override fun Module.load() {
|
||||||
|
@ -12,6 +12,7 @@ kotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
api project(":postssystem.features.files.common")
|
api project(":postssystem.features.files.common")
|
||||||
api project(":postssystem.features.common.client")
|
api project(":postssystem.features.common.client")
|
||||||
|
api project(":postssystem.features.auth.client")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
dependencies {
|
||||||
api project(":postssystem.features.roles.common")
|
api project(":postssystem.features.roles.common")
|
||||||
api project(":postssystem.features.common.client")
|
api project(":postssystem.features.common.client")
|
||||||
|
api project(":postssystem.features.auth.client")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 dev.inmo.micro_utils.serialization.typed_serializer.TypedSerializer
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
private val justForLoading = RolesManagerRoleSerializer
|
||||||
|
|
||||||
@Serializable(RolesManagerRoleSerializer::class)
|
@Serializable(RolesManagerRoleSerializer::class)
|
||||||
interface RolesManagerRole : Role {
|
interface RolesManagerRole : Role {
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -12,6 +12,7 @@ kotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
api project(":postssystem.features.users.common")
|
api project(":postssystem.features.users.common")
|
||||||
api project(":postssystem.features.common.client")
|
api project(":postssystem.features.common.client")
|
||||||
|
api project(":postssystem.features.auth.client")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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"
|
jsuikit = "0.0.45"
|
||||||
compose = "1.1.1"
|
compose = "1.1.1"
|
||||||
microutils = "0.9.16"
|
microutils = "0.9.16"
|
||||||
tgbotapi = "0.38.7"
|
tgbotapi = "0.38.9"
|
||||||
ktor = "1.6.8"
|
ktor = "1.6.8"
|
||||||
klock = "2.6.3"
|
klock = "2.6.3"
|
||||||
exposed = "0.37.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-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-mimetypes = { module = "dev.inmo:micro_utils.mime_types", version.ref = "microutils" }
|
||||||
microutils-coroutines = { module = "dev.inmo:micro_utils.coroutines", 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-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" }
|
microutils-serialization-typedserializer = { module = "dev.inmo:micro_utils.serialization.typed_serializer", version.ref = "microutils" }
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ kotlin {
|
|||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
api project(":postssystem.publicators.simple.common")
|
api project(":postssystem.publicators.simple.common")
|
||||||
|
api project(":postssystem.features.auth.client")
|
||||||
api project(":postssystem.features.publication.client")
|
api project(":postssystem.features.publication.client")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.features.common.client")
|
||||||
api project(":postssystem.services.posts.common")
|
api project(":postssystem.services.posts.common")
|
||||||
api project(":postssystem.features.posts.client")
|
api project(":postssystem.features.posts.client")
|
||||||
|
api project(":postssystem.features.auth.client")
|
||||||
|
api project(":postssystem.features.content.client")
|
||||||
api project(":postssystem.publicators.simple.client")
|
api project(":postssystem.publicators.simple.client")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
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.features.content.common.Content
|
||||||
import dev.inmo.postssystem.publicators.simple.client.SimplePublicatorService
|
import dev.inmo.postssystem.publicators.simple.client.SimplePublicatorService
|
||||||
import dev.inmo.postssystem.services.posts.common.FullNewPost
|
import dev.inmo.postssystem.services.posts.common.FullNewPost
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package dev.inmo.postssystem.services.posts.client.ui.create
|
package dev.inmo.postssystem.services.posts.client.ui.create
|
||||||
|
|
||||||
import dev.inmo.postssystem.features.common.common.UIModel
|
import dev.inmo.postssystem.features.common.common.ui.UIModel
|
||||||
import dev.inmo.postssystem.features.common.common.UIViewModel
|
import dev.inmo.postssystem.features.common.common.ui.UIViewModel
|
||||||
import dev.inmo.postssystem.features.content.common.Content
|
import dev.inmo.postssystem.features.content.common.Content
|
||||||
|
|
||||||
interface PostCreateUIModel : UIModel<PostCreateUIState>, UIViewModel<PostCreateUIState> {
|
interface PostCreateUIModel : UIModel<PostCreateUIState>, UIViewModel<PostCreateUIState> {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package dev.inmo.postssystem.services.posts.client.ui.create
|
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 dev.inmo.postssystem.features.content.common.Content
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
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 androidx.compose.runtime.*
|
||||||
import dev.inmo.jsuikit.elements.*
|
import dev.inmo.jsuikit.elements.*
|
||||||
import dev.inmo.jsuikit.modifiers.*
|
import dev.inmo.jsuikit.modifiers.*
|
||||||
import dev.inmo.jsuikit.utils.Attrs
|
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.coroutines.launchSafelyWithoutExceptions
|
||||||
import dev.inmo.micro_utils.fsm.common.StatesMachine
|
import dev.inmo.micro_utils.fsm.common.StatesMachine
|
||||||
import dev.inmo.postssystem.client.ui.fsm.CreatePostUIFSMState
|
import dev.inmo.postssystem.features.auth.client.registerAfterAuthHandler
|
||||||
import dev.inmo.postssystem.client.ui.fsm.UIFSMState
|
import dev.inmo.postssystem.features.common.common.*
|
||||||
import dev.inmo.postssystem.client.utils.renderComposableAndLinkToContext
|
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.client.ContentClientProvider
|
||||||
import dev.inmo.postssystem.features.content.common.Content
|
import dev.inmo.postssystem.features.content.common.Content
|
||||||
import dev.inmo.postssystem.services.posts.client.ui.create.PostCreateUIViewModel
|
import dev.inmo.postssystem.services.posts.client.ui.create.PostCreateUIViewModel
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import org.jetbrains.compose.web.dom.Div
|
import org.jetbrains.compose.web.dom.Div
|
||||||
import org.jetbrains.compose.web.dom.Text
|
import org.jetbrains.compose.web.dom.Text
|
||||||
import org.w3c.dom.HTMLElement
|
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(
|
class PostCreateView(
|
||||||
private val createPostCreateUIModel: PostCreateUIViewModel,
|
private val createPostCreateUIModel: PostCreateUIViewModel,
|
||||||
private val contentClientProviders: List<ContentClientProvider>,
|
private val contentClientProviders: List<ContentClientProvider>,
|
||||||
private val uiScope: CoroutineScope
|
private val uiScope: CoroutineScope,
|
||||||
) : JSView<CreatePostUIFSMState>() {
|
defaultExceptionsHandlers: Iterable<UIFSMExceptionHandler>
|
||||||
|
) : JSView<CreatePostUIFSMState>(defaultExceptionsHandlers) {
|
||||||
override suspend fun StatesMachine<in UIFSMState>.safeHandleState(
|
override suspend fun StatesMachine<in UIFSMState>.safeHandleState(
|
||||||
htmlElement: HTMLElement,
|
htmlElement: HTMLElement,
|
||||||
state: CreatePostUIFSMState
|
state: CreatePostUIFSMState
|
||||||
@ -31,7 +51,7 @@ class PostCreateView(
|
|||||||
|
|
||||||
val contentProvidersList = mutableStateListOf<Pair<ContentClientProvider, MutableState<Content?>>>()
|
val contentProvidersList = mutableStateListOf<Pair<ContentClientProvider, MutableState<Content?>>>()
|
||||||
|
|
||||||
renderComposableAndLinkToContext(htmlElement) {
|
renderComposableAndLinkToContextAndRoot(htmlElement) {
|
||||||
Flex(
|
Flex(
|
||||||
UIKitFlex.Alignment.Horizontal.Center
|
UIKitFlex.Alignment.Horizontal.Center
|
||||||
) {
|
) {
|
@ -9,7 +9,7 @@ val defaultTelegramTargetModuleLoader = TelegramTargetModuleLoader
|
|||||||
|
|
||||||
object TelegramTargetModuleLoader : ModuleLoader {
|
object TelegramTargetModuleLoader : ModuleLoader {
|
||||||
init {
|
init {
|
||||||
AdditionalModules.addModule(this)
|
AdditionalModules.Default.addModule(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Module.load() {
|
override fun Module.load() {
|
||||||
|
Loading…
Reference in New Issue
Block a user