simpliest version of content upload
This commit is contained in:
client/src
commonMain
kotlin
dev
inmo
postssystem
client
ui
jsMain
kotlin
dev
inmo
postssystem
client
features
common
common
src
jsMain
kotlin
dev
inmo
postssystem
features
common
content
common
src
commonMain
kotlin
dev
inmo
postssystem
features
content
common
server/src/main
java
dev
inmo
postssystem
resources
services/posts/client/src
commonMain
kotlin
dev
inmo
postssystem
services
posts
client
jsMain
kotlin
dev
inmo
postssystem
services
posts
client
@ -18,7 +18,13 @@ object UIFSMStateSerializer : KSerializer<UIFSMState> by TypedSerializer(
|
||||
|
||||
@Serializable
|
||||
data class AuthUIFSMState(
|
||||
override val from: UIFSMState? = null,
|
||||
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
|
||||
|
@ -13,8 +13,8 @@ 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 kotlinx.browser.document
|
||||
import kotlinx.browser.localStorage
|
||||
import dev.inmo.postssystem.services.posts.client.ui.create.*
|
||||
import kotlinx.browser.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.serializer
|
||||
@ -77,7 +77,7 @@ fun baseKoin(): Koin {
|
||||
)
|
||||
},
|
||||
{
|
||||
JSUIFSMStatesRepo(get(), localStorage)
|
||||
JSUIFSMStatesRepo(window.history)
|
||||
}
|
||||
) {
|
||||
first.apply {
|
||||
@ -89,6 +89,10 @@ fun baseKoin(): Koin {
|
||||
factory<AuthUIModel> { DefaultAuthUIModel(get(), get()) }
|
||||
factory { AuthUIViewModel(get()) }
|
||||
factory { AuthView(get(), get(UIScopeQualifier)) }
|
||||
|
||||
factory<PostCreateUIModel> { DefaultPostCreateUIModel(get()) }
|
||||
factory { PostCreateUIViewModel(get()) }
|
||||
factory { PostCreateView(get(), get(UIScopeQualifier)) }
|
||||
}
|
||||
)
|
||||
strictlyOn<AuthUIFSMState>(get<AuthView>())
|
||||
@ -135,6 +139,7 @@ fun baseKoin(): Koin {
|
||||
)
|
||||
)
|
||||
|
||||
registerHandler(PostCreateView::class)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,62 +2,78 @@ package dev.inmo.postssystem.client
|
||||
|
||||
import dev.inmo.postssystem.client.ui.fsm.*
|
||||
import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManagerRepo
|
||||
import kotlinx.browser.window
|
||||
import kotlinx.serialization.StringFormat
|
||||
import org.w3c.dom.*
|
||||
import org.w3c.dom.url.URL
|
||||
|
||||
class JSUIFSMStatesRepo(
|
||||
private val serialFormat: StringFormat,
|
||||
private val storage: Storage,
|
||||
private val initialState: UIFSMState = DefaultAuthUIFSMState
|
||||
) : DefaultStatesManagerRepo<UIFSMState> {
|
||||
private val String.storageKey
|
||||
get() = "${FSMStateSettingsFieldPrefix}$this"
|
||||
private val String.UIFSMState
|
||||
get() = runCatching {
|
||||
serialFormat.decodeFromString(UIFSMStateSerializer, this)
|
||||
}.onFailure { it.printStackTrace() }.getOrNull()
|
||||
|
||||
init {
|
||||
if (states().isEmpty()) {
|
||||
setState(initialState)
|
||||
private fun History.refreshHistory(
|
||||
states: Iterable<UIFSMState>,
|
||||
) {
|
||||
val currentUrl = window.location.pathname
|
||||
val params = states.mapNotNull<UIFSMState, Pair<String, String>> {
|
||||
when (it) {
|
||||
is AuthUIFSMState -> null
|
||||
is CreatePostUIFSMState -> null
|
||||
}
|
||||
}
|
||||
pushState(
|
||||
null,
|
||||
"Posts System",
|
||||
"$currentUrl${params.joinToString("&", "?") { "${it.first}=${it.second}" }}"
|
||||
)
|
||||
}
|
||||
|
||||
private fun setState(state: UIFSMState) {
|
||||
storage[state.context.storageKey] = serialFormat.encodeToString(UIFSMStateSerializer, state)
|
||||
private fun takeStates(initialState: UIFSMState): List<UIFSMState> {
|
||||
val params = (URL(window.location.href)).searchParams
|
||||
val additionalStates = listOfNotNull<UIFSMState>()
|
||||
|
||||
return additionalStates + listOfNotNull(
|
||||
if (additionalStates.isEmpty()) {
|
||||
initialState
|
||||
} else {
|
||||
null
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
class JSUIFSMStatesRepo(
|
||||
private val history: History,
|
||||
private val initialState: UIFSMState = DefaultAuthUIFSMState
|
||||
) : DefaultStatesManagerRepo<UIFSMState> {
|
||||
private val statesMap = mutableMapOf<String, UIFSMState>()
|
||||
|
||||
init {
|
||||
val states = takeStates(initialState)
|
||||
states.forEach {
|
||||
statesMap[it.context] = it
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getContextState(context: Any): UIFSMState? {
|
||||
return when (context) {
|
||||
is String -> storage[context.storageKey] ?.UIFSMState ?: return DefaultAuthUIFSMState
|
||||
is String -> statesMap[context]
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun contains(context: Any): Boolean = when (context) {
|
||||
is String -> storage.get(context) ?.UIFSMState != null
|
||||
is String -> statesMap.contains(context)
|
||||
else -> super.contains(context)
|
||||
}
|
||||
|
||||
private fun states(): List<UIFSMState> = storage.iterator().asSequence().mapNotNull { (k, v) ->
|
||||
if (k.startsWith(FSMStateSettingsFieldPrefix)) {
|
||||
v.UIFSMState
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}.toList()
|
||||
|
||||
override suspend fun getStates(): List<UIFSMState> = states()
|
||||
override suspend fun getStates(): List<UIFSMState> = statesMap.values.toList()
|
||||
|
||||
override suspend fun removeState(state: UIFSMState) {
|
||||
storage.removeItem((state.context as? String) ?.storageKey ?: return)
|
||||
statesMap.remove((state.context as? String) ?: return)
|
||||
history.refreshHistory(statesMap.values)
|
||||
}
|
||||
|
||||
override suspend fun set(state: UIFSMState) {
|
||||
setState(state)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val FSMStateSettingsFieldPrefix = "UIFSMState_"
|
||||
console.log(state)
|
||||
statesMap[state.context] = state
|
||||
history.refreshHistory(statesMap.values)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,81 @@
|
||||
package dev.inmo.postssystem.client.fsm.ui
|
||||
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import dev.inmo.jsuikit.elements.DefaultButton
|
||||
import dev.inmo.jsuikit.elements.DropArea
|
||||
import dev.inmo.jsuikit.utils.InputAttrs
|
||||
import dev.inmo.micro_utils.common.MPPFile
|
||||
import dev.inmo.micro_utils.common.filename
|
||||
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.common.common.*
|
||||
import dev.inmo.postssystem.features.content.common.BinaryContent
|
||||
import dev.inmo.postssystem.features.content.text.common.TextContent
|
||||
import dev.inmo.postssystem.services.posts.client.ui.create.PostCreateUIModel
|
||||
import dev.inmo.postssystem.services.posts.client.ui.create.PostCreateUIViewModel
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.jetbrains.compose.web.dom.*
|
||||
import org.w3c.dom.HTMLElement
|
||||
import org.w3c.files.get
|
||||
|
||||
class PostCreateView(
|
||||
private val createPostCreateUIModel: PostCreateUIViewModel,
|
||||
private val uiScope: CoroutineScope
|
||||
) : JSView<CreatePostUIFSMState>() {
|
||||
override suspend fun StatesMachine<in UIFSMState>.safeHandleState(
|
||||
htmlElement: HTMLElement,
|
||||
state: CreatePostUIFSMState
|
||||
): UIFSMState? {
|
||||
val result = CompletableDeferred<UIFSMState?>()
|
||||
|
||||
val textContent = mutableStateOf("")
|
||||
val fileState = mutableStateOf<MPPFile?>(null)
|
||||
|
||||
renderComposableAndLinkToContext(htmlElement) {
|
||||
Form {
|
||||
TextArea(textContent.value) {
|
||||
onInput { textContent.value = it.value }
|
||||
}
|
||||
DropArea (
|
||||
inputAttrs = InputAttrs {
|
||||
onInput {
|
||||
it.target.files ?.get(0) ?.let {
|
||||
fileState.value = it
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
DefaultButton(
|
||||
"Upload",
|
||||
disabled = fileState.value == null || textContent.value.isBlank()
|
||||
) {
|
||||
it.preventDefault()
|
||||
fileState.value ?.let { file ->
|
||||
uiScope.launchSafelyWithoutExceptions {
|
||||
createPostCreateUIModel.create(
|
||||
listOf(
|
||||
TextContent(
|
||||
textContent.value
|
||||
),
|
||||
BinaryContent(
|
||||
file.filename,
|
||||
file.mimeType,
|
||||
file.inputProvider()
|
||||
)
|
||||
)
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
console.log("")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.await()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user