temporal potentially working variant (bot not building

This commit is contained in:
2022-03-26 14:19:20 +06:00
parent 51cdfb320b
commit 37114f0ddb
17 changed files with 216 additions and 31 deletions
features
auth
client
build.gradle
src
commonMain
kotlin
dev
inmo
postssystem
features
common
client
src
jsMain
kotlin
dev
inmo
postssystem
features
common
content
binary
client
src
jsMain
kotlin
dev
inmo
postssystem
features
client
src
jsMain
kotlin
dev
inmo
postssystem
features
text
client
src
jsMain
kotlin
dev
inmo
postssystem
features
posts
common
src
commonMain
kotlin
dev
inmo
postssystem
features
posts
common
roles
client
src
commonMain
kotlin
dev
services/posts/client/src
commonMain
jsMain
kotlin
dev
inmo
postssystem
services
posts

@ -12,7 +12,6 @@ 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.status.client")
api project(":postssystem.features.auth.common") api project(":postssystem.features.auth.common")
} }

@ -2,7 +2,6 @@ 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
import dev.inmo.postssystem.features.roles.common.Role
import dev.inmo.postssystem.features.users.common.User import dev.inmo.postssystem.features.users.common.User
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@ -11,7 +10,6 @@ import org.koin.core.module.Module
interface AuthSettings { interface AuthSettings {
val authorizedDIModule: StateFlow<Module?> val authorizedDIModule: StateFlow<Module?>
val user: StateFlow<User?> val user: StateFlow<User?>
val userRoles: StateFlow<List<Role>>
val loadingJob: Job val loadingJob: Job
suspend fun auth(serverUrl: String, creds: AuthCreds): AuthUIError? suspend fun auth(serverUrl: String, creds: AuthCreds): AuthUIError?

@ -3,14 +3,10 @@ package dev.inmo.postssystem.features.auth.client.settings
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.*
import dev.inmo.postssystem.features.roles.common.Role
import dev.inmo.postssystem.features.roles.common.RolesStorage
import dev.inmo.postssystem.features.status.client.StatusFeatureClient import dev.inmo.postssystem.features.status.client.StatusFeatureClient
import dev.inmo.postssystem.features.users.common.User import dev.inmo.postssystem.features.users.common.User
import dev.inmo.micro_utils.common.Either import dev.inmo.micro_utils.common.Either
import dev.inmo.micro_utils.common.either 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.micro_utils.repos.*
import dev.inmo.postssystem.features.auth.client.createAuthorizedFeaturesDIModule import dev.inmo.postssystem.features.auth.client.createAuthorizedFeaturesDIModule
import dev.inmo.postssystem.features.common.common.DBDropper import dev.inmo.postssystem.features.common.common.DBDropper
@ -29,8 +25,6 @@ data class DefaultAuthSettings(
override val authorizedDIModule: StateFlow<Module?> = _authorizedDIModule.asStateFlow() override val authorizedDIModule: StateFlow<Module?> = _authorizedDIModule.asStateFlow()
private val _user = MutableStateFlow<User?>(null) private val _user = MutableStateFlow<User?>(null)
override val user: StateFlow<User?> = _user.asStateFlow() override val user: StateFlow<User?> = _user.asStateFlow()
private val _userRoles = MutableStateFlow<List<Role>>(emptyList())
override val userRoles: StateFlow<List<Role>> = _userRoles.asStateFlow()
private suspend fun getCurrentServerURL() = repo.get(SERVER_URL_FIELD) as? String private suspend fun getCurrentServerURL() = repo.get(SERVER_URL_FIELD) as? String
private suspend fun getCurrentUsername() = repo.get(USERNAME_FIELD) as? String private suspend fun getCurrentUsername() = repo.get(USERNAME_FIELD) as? String
@ -42,18 +36,6 @@ data class DefaultAuthSettings(
updateModule(serverUrl, token.either()) updateModule(serverUrl, token.either())
} }
val rolesUpdatingJob = (user + authorizedDIModule).subscribeSafelyWithoutExceptions(scope) {
val user = user.value
if (user == null || authorizedDIModule.value == null) {
_userRoles.value = emptyList()
} else {
_userRoles.value = koin.get<RolesStorage<Role>>().getRoles(user.id)
}
println(user)
println(userRoles.value)
}
override suspend fun auth(serverUrl: String, creds: AuthCreds): AuthUIError? { override suspend fun auth(serverUrl: String, creds: AuthCreds): AuthUIError? {
return runCatching { return runCatching {
if (getCurrentServerURL() != serverUrl || getCurrentUsername() != creds.username.string) { if (getCurrentServerURL() != serverUrl || getCurrentUsername() != creds.username.string) {

@ -0,0 +1,18 @@
package dev.inmo.postssystem.features.common.common.ui
import androidx.compose.runtime.Composable
import com.soywiz.klock.DateTime
import com.soywiz.klock.ISO8601
import org.jetbrains.compose.web.dom.Text
object DateTimeView {
val simpleFormat = ISO8601.BaseIsoDateTimeFormat(
"DD.MM.YYYY, hh:mm"
)
@Composable
fun Simple(
dateTime: DateTime
) {
Text(dateTime.local.format(simpleFormat))
}
}

@ -1,12 +1,15 @@
package dev.inmo.postssystem.features.content.binary.client package dev.inmo.postssystem.features.content.binary.client
import androidx.compose.runtime.* import androidx.compose.runtime.*
import dev.inmo.jsuikit.elements.DefaultButton import dev.inmo.jsuikit.elements.*
import dev.inmo.jsuikit.modifiers.UIKitWidth import dev.inmo.jsuikit.modifiers.UIKitWidth
import dev.inmo.micro_utils.common.selectFile import dev.inmo.micro_utils.common.selectFile
import dev.inmo.micro_utils.mime_types.KnownMimeTypes
import dev.inmo.postssystem.features.common.common.* import dev.inmo.postssystem.features.common.common.*
import dev.inmo.postssystem.features.content.client.ContentClientProvider import dev.inmo.postssystem.features.content.client.ContentClientProvider
import dev.inmo.postssystem.features.content.common.* import dev.inmo.postssystem.features.content.common.*
import org.jetbrains.compose.web.dom.Img
import org.jetbrains.compose.web.dom.Text
import org.koin.core.module.Module import org.koin.core.module.Module
object LoadingClientModule : ModuleLoader { object LoadingClientModule : ModuleLoader {
@ -41,4 +44,23 @@ object BinaryContentClientProvider : ContentClientProvider {
} }
} }
} }
@Composable
override fun renderPreview(content: Content): Boolean {
if (content is BinaryContent) {
Tile {
Card(
header = {
CardTitle {
Text(content.filename.name)
}
}
) {}
}
return true
}
return false
}
} }

@ -10,4 +10,7 @@ interface ContentClientProvider {
@Composable @Composable
fun renderNewInstance(state: MutableState<Content?>) fun renderNewInstance(state: MutableState<Content?>)
@Composable
fun renderPreview(content: Content): Boolean
} }

@ -1,12 +1,14 @@
package dev.inmo.postssystem.features.content.text.client package dev.inmo.postssystem.features.content.text.client
import androidx.compose.runtime.* import androidx.compose.runtime.*
import dev.inmo.jsuikit.elements.Tile
import dev.inmo.jsuikit.modifiers.UIKitWidth import dev.inmo.jsuikit.modifiers.UIKitWidth
import dev.inmo.jsuikit.modifiers.include import dev.inmo.jsuikit.modifiers.include
import dev.inmo.postssystem.features.common.common.* import dev.inmo.postssystem.features.common.common.*
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.features.content.text.common.TextContent import dev.inmo.postssystem.features.content.text.common.TextContent
import org.jetbrains.compose.web.dom.Text
import org.jetbrains.compose.web.dom.TextArea import org.jetbrains.compose.web.dom.TextArea
import org.koin.core.module.Module import org.koin.core.module.Module
@ -36,4 +38,17 @@ object TextContentClientProvider : ContentClientProvider {
onInput { state.value = TextContent(it.value) } onInput { state.value = TextContent(it.value) }
} }
} }
@Composable
override fun renderPreview(content: Content): Boolean {
if (content is TextContent) {
Tile {
Text(content.text)
}
return true
}
return false
}
} }

@ -2,7 +2,9 @@ package dev.inmo.postssystem.features.posts.common
import com.soywiz.klock.DateTime import com.soywiz.klock.DateTime
import dev.inmo.postssystem.features.common.common.DateTimeSerializer import dev.inmo.postssystem.features.common.common.DateTimeSerializer
import dev.inmo.postssystem.features.content.common.Content
import dev.inmo.postssystem.features.content.common.ContentId import dev.inmo.postssystem.features.content.common.ContentId
import kotlinx.serialization.Polymorphic
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlin.jvm.JvmInline import kotlin.jvm.JvmInline
@ -45,3 +47,15 @@ data class RegisteredPost(
@Serializable(DateTimeSerializer::class) @Serializable(DateTimeSerializer::class)
val creationDate: DateTime val creationDate: DateTime
) : Post() ) : Post()
@Serializable
data class PostWithContent(
val post: Post,
val content: List<Content>
)
@Serializable
data class RegisteredPostWithContent(
val post: RegisteredPost,
val content: List<@Polymorphic Content>
)

@ -0,0 +1,31 @@
package dev.inmo.postssystem.features.roles.client
import dev.inmo.micro_utils.coroutines.plus
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.postssystem.features.auth.client.settings.AuthSettings
import dev.inmo.postssystem.features.roles.common.Role
import dev.inmo.postssystem.features.roles.common.RolesStorage
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
import org.koin.core.Koin
class DefaultRolesSettings(
private val koin: Koin,
private val authSettings: AuthSettings,
private val scope: CoroutineScope
) : RolesSettings {
private val _userRoles = MutableStateFlow<List<Role>>(emptyList())
override val userRoles: StateFlow<List<Role>> = _userRoles.asStateFlow()
val rolesUpdatingJob = (authSettings.user + authSettings.authorizedDIModule).subscribeSafelyWithoutExceptions(scope) {
val user = authSettings.user.value
if (user == null || authSettings.authorizedDIModule.value == null) {
_userRoles.value = emptyList()
} else {
_userRoles.value = koin.get<RolesStorage<Role>>().getRoles(user.id)
}
println(user)
println(userRoles.value)
}
}

@ -6,5 +6,6 @@ import dev.inmo.postssystem.features.roles.common.Role
import dev.inmo.postssystem.features.roles.common.RolesStorage import dev.inmo.postssystem.features.roles.common.RolesStorage
val loader = AuthorizedModuleLoader { val loader = AuthorizedModuleLoader {
single<RolesSettings> { DefaultRolesSettings(getKoin(), get(), get()) }
single<RolesStorage<Role>> { ClientRolesStorage(get(AuthorizedQualifiers.ServerUrlQualifier), get(), Role.serializer()) } single<RolesStorage<Role>> { ClientRolesStorage(get(AuthorizedQualifiers.ServerUrlQualifier), get(), Role.serializer()) }
} }

@ -0,0 +1,8 @@
package dev.inmo.postssystem.features.roles.client
import dev.inmo.postssystem.features.roles.common.Role
import kotlinx.coroutines.flow.StateFlow
interface RolesSettings {
val userRoles: StateFlow<List<Role>>
}

@ -14,9 +14,6 @@ val loader = AuthorizedModuleLoader {
WritePostsService::class WritePostsService::class
) )
UIFSMStateSerializer.include("posts_create", PostsCreateUIFSMState.serializer())
UIFSMStateSerializer.include("posts_list", PostsListUIFSMState.serializer())
factory<PostCreateUIModel> { DefaultPostCreateUIModel(get(), get()) } factory<PostCreateUIModel> { DefaultPostCreateUIModel(get(), get()) }
factory { PostCreateUIViewModel(get()) } factory { PostCreateUIViewModel(get()) }
} }

@ -1,9 +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.ui.fsm.UIFSMState import dev.inmo.postssystem.features.common.common.ui.fsm.UIFSMState
import kotlinx.serialization.Serializable
@Serializable
data class PostsCreateUIFSMState( data class PostsCreateUIFSMState(
override val from: UIFSMState? = null, override val from: UIFSMState? = null,
override val context: String = "main" override val context: String = "main"

@ -1,9 +1,7 @@
package dev.inmo.postssystem.services.posts.client.ui.list package dev.inmo.postssystem.services.posts.client.ui.list
import dev.inmo.postssystem.features.common.common.ui.fsm.UIFSMState import dev.inmo.postssystem.features.common.common.ui.fsm.UIFSMState
import kotlinx.serialization.Serializable
@Serializable
data class PostsListUIFSMState( data class PostsListUIFSMState(
override val from: UIFSMState? = null, override val from: UIFSMState? = null,
override val context: String = "main" override val context: String = "main"

@ -1,6 +1,6 @@
package dev.inmo.postssystem.services.posts.client.ui.list package dev.inmo.postssystem.services.posts.client.ui.list
import dev.inmo.postssystem.features.posts.common.Post import dev.inmo.postssystem.features.posts.common.RegisteredPostWithContent
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
@ -10,7 +10,7 @@ sealed class PostsListUIState {
@Serializable @Serializable
data class Show( data class Show(
val posts: List<Post> val posts: List<RegisteredPostWithContent>
) : PostsListUIState() ) : PostsListUIState()
} }

@ -0,0 +1,11 @@
package dev.inmo.postssystem.services.posts.client.ui.list
import dev.inmo.postssystem.features.common.common.ui.UIViewModel
import kotlinx.coroutines.flow.StateFlow
class PostsListUIViewModel(
private val model: PostsListUIModel
) : UIViewModel<PostsListUIState> {
override val currentState: StateFlow<PostsListUIState>
get() = model.currentState
}

@ -0,0 +1,90 @@
package dev.inmo.postssystem.services.posts.client
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import dev.inmo.jsuikit.elements.*
import dev.inmo.micro_utils.common.applyDiff
import dev.inmo.micro_utils.coroutines.compose.renderComposableAndLinkToContextAndRoot
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.features.auth.client.registerAfterAuthHandler
import dev.inmo.postssystem.features.common.common.*
import dev.inmo.postssystem.features.common.common.ui.DateTimeView
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.posts.common.RegisteredPostWithContent
import dev.inmo.postssystem.services.posts.client.ui.create.PostsCreateUIFSMState
import dev.inmo.postssystem.services.posts.client.ui.list.*
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import org.w3c.dom.HTMLElement
val postsListViewLoader = DefaultModuleLoader {
factory { PostsListView(get(), getAllDistinct(), get(), getAllDistinct()) }
singleWithRandomQualifier <UIFSMHandler.Registrator> {
UIFSMHandler.Registrator {
registerAfterAuthHandler(getKoin(), PostsListView::class)
}
}
}
class PostsListView(
private val model: PostsListUIViewModel,
private val contentClientProviders: List<ContentClientProvider>,
private val uiScope: CoroutineScope,
defaultExceptionsHandlers: Iterable<UIFSMExceptionHandler>
) : JSView<PostsCreateUIFSMState>(defaultExceptionsHandlers) {
override suspend fun StatesMachine<in UIFSMState>.safeHandleState(
htmlElement: HTMLElement,
state: PostsCreateUIFSMState
): UIFSMState? {
val result = CompletableDeferred<UIFSMState?>()
val loadingState = mutableStateOf(true)
val postsState = mutableStateListOf<RegisteredPostWithContent>()
renderComposableAndLinkToContextAndRoot(htmlElement) {
if (loadingState.value) {
Spinner()
} else {
postsState.forEach {
Card(
header = {
CardTitle {
DateTimeView.Simple(it.post.creationDate)
}
Icon.App.Plus.drawAsButton {
uiScope.launchSafelyWithoutExceptions {
startChain(PostsCreateUIFSMState(state))
}
}
}
) {
it.content.forEach {
for (provider in contentClientProviders) {
if (provider.renderPreview(it)) {
break
}
}
}
}
}
}
}
model.currentState.subscribeSafelyWithoutExceptions(uiScope) {
loadingState.value = it == PostsListUIState.Loading
when (it) {
PostsListUIState.Loading -> {}
is PostsListUIState.Show -> {
postsState.applyDiff(it.posts)
}
}
}
return result.await()
}
}