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
|
@Serializable
|
||||||
data class AuthUIFSMState(
|
data class AuthUIFSMState(
|
||||||
override val from: UIFSMState? = null,
|
override val from: UIFSMState? = CreatePostUIFSMState(),
|
||||||
override val context: String = "main"
|
override val context: String = "main"
|
||||||
) : UIFSMState
|
) : UIFSMState
|
||||||
val DefaultAuthUIFSMState = AuthUIFSMState()
|
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.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.client.settings.auth.AuthSettings
|
||||||
import kotlinx.browser.document
|
import dev.inmo.postssystem.services.posts.client.ui.create.*
|
||||||
import kotlinx.browser.localStorage
|
import kotlinx.browser.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.serialization.builtins.serializer
|
import kotlinx.serialization.builtins.serializer
|
||||||
import kotlinx.serialization.serializer
|
import kotlinx.serialization.serializer
|
||||||
@ -77,7 +77,7 @@ fun baseKoin(): Koin {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
JSUIFSMStatesRepo(get(), localStorage)
|
JSUIFSMStatesRepo(window.history)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
first.apply {
|
first.apply {
|
||||||
@ -89,6 +89,10 @@ fun baseKoin(): Koin {
|
|||||||
factory<AuthUIModel> { DefaultAuthUIModel(get(), get()) }
|
factory<AuthUIModel> { DefaultAuthUIModel(get(), get()) }
|
||||||
factory { AuthUIViewModel(get()) }
|
factory { AuthUIViewModel(get()) }
|
||||||
factory { AuthView(get(), get(UIScopeQualifier)) }
|
factory { AuthView(get(), get(UIScopeQualifier)) }
|
||||||
|
|
||||||
|
factory<PostCreateUIModel> { DefaultPostCreateUIModel(get()) }
|
||||||
|
factory { PostCreateUIViewModel(get()) }
|
||||||
|
factory { PostCreateView(get(), get(UIScopeQualifier)) }
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
strictlyOn<AuthUIFSMState>(get<AuthView>())
|
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.postssystem.client.ui.fsm.*
|
||||||
import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManagerRepo
|
import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManagerRepo
|
||||||
|
import kotlinx.browser.window
|
||||||
import kotlinx.serialization.StringFormat
|
import kotlinx.serialization.StringFormat
|
||||||
import org.w3c.dom.*
|
import org.w3c.dom.*
|
||||||
|
import org.w3c.dom.url.URL
|
||||||
|
|
||||||
class JSUIFSMStatesRepo(
|
private fun History.refreshHistory(
|
||||||
private val serialFormat: StringFormat,
|
states: Iterable<UIFSMState>,
|
||||||
private val storage: Storage,
|
) {
|
||||||
private val initialState: UIFSMState = DefaultAuthUIFSMState
|
val currentUrl = window.location.pathname
|
||||||
) : DefaultStatesManagerRepo<UIFSMState> {
|
val params = states.mapNotNull<UIFSMState, Pair<String, String>> {
|
||||||
private val String.storageKey
|
when (it) {
|
||||||
get() = "${FSMStateSettingsFieldPrefix}$this"
|
is AuthUIFSMState -> null
|
||||||
private val String.UIFSMState
|
is CreatePostUIFSMState -> null
|
||||||
get() = runCatching {
|
|
||||||
serialFormat.decodeFromString(UIFSMStateSerializer, this)
|
|
||||||
}.onFailure { it.printStackTrace() }.getOrNull()
|
|
||||||
|
|
||||||
init {
|
|
||||||
if (states().isEmpty()) {
|
|
||||||
setState(initialState)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pushState(
|
||||||
|
null,
|
||||||
|
"Posts System",
|
||||||
|
"$currentUrl${params.joinToString("&", "?") { "${it.first}=${it.second}" }}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun setState(state: UIFSMState) {
|
private fun takeStates(initialState: UIFSMState): List<UIFSMState> {
|
||||||
storage[state.context.storageKey] = serialFormat.encodeToString(UIFSMStateSerializer, state)
|
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? {
|
override suspend fun getContextState(context: Any): UIFSMState? {
|
||||||
return when (context) {
|
return when (context) {
|
||||||
is String -> storage[context.storageKey] ?.UIFSMState ?: return DefaultAuthUIFSMState
|
is String -> statesMap[context]
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun contains(context: Any): Boolean = when (context) {
|
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)
|
else -> super.contains(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun states(): List<UIFSMState> = storage.iterator().asSequence().mapNotNull { (k, v) ->
|
override suspend fun getStates(): List<UIFSMState> = statesMap.values.toList()
|
||||||
if (k.startsWith(FSMStateSettingsFieldPrefix)) {
|
|
||||||
v.UIFSMState
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}.toList()
|
|
||||||
|
|
||||||
override suspend fun getStates(): List<UIFSMState> = states()
|
|
||||||
|
|
||||||
override suspend fun removeState(state: UIFSMState) {
|
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) {
|
override suspend fun set(state: UIFSMState) {
|
||||||
setState(state)
|
console.log(state)
|
||||||
}
|
statesMap[state.context] = state
|
||||||
|
history.refreshHistory(statesMap.values)
|
||||||
companion object {
|
|
||||||
private const val FSMStateSettingsFieldPrefix = "UIFSMState_"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,16 @@
|
|||||||
package dev.inmo.postssystem.features.common.common
|
package dev.inmo.postssystem.features.common.common
|
||||||
|
|
||||||
import dev.inmo.micro_utils.common.MPPFile
|
import dev.inmo.micro_utils.common.MPPFile
|
||||||
|
import dev.inmo.micro_utils.common.bytes
|
||||||
import io.ktor.utils.io.core.ByteReadPacket
|
import io.ktor.utils.io.core.ByteReadPacket
|
||||||
import io.ktor.utils.io.core.Input
|
import io.ktor.utils.io.core.Input
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable(SimpleInputProviderSerializer::class)
|
@Serializable(SimpleInputProviderSerializer::class)
|
||||||
actual class FileBasedInputProvider internal constructor(
|
actual class FileBasedInputProvider internal constructor(
|
||||||
actual val file: MPPFile,
|
actual val file: MPPFile
|
||||||
private val bytes: ByteArray
|
|
||||||
) : SimpleInputProvider {
|
) : SimpleInputProvider {
|
||||||
override fun invoke(): Input = ByteReadPacket(bytes)
|
override fun invoke(): Input = error("Files inputs must not be used directly")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun MPPFile.inputProvider() = FileBasedInputProvider(this)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package dev.inmo.postssystem.features.content.common
|
package dev.inmo.postssystem.features.content.common
|
||||||
|
|
||||||
import dev.inmo.micro_utils.common.FileName
|
import dev.inmo.micro_utils.common.FileName
|
||||||
|
import dev.inmo.micro_utils.common.MPPFile
|
||||||
import dev.inmo.micro_utils.mime_types.MimeType
|
import dev.inmo.micro_utils.mime_types.MimeType
|
||||||
import dev.inmo.postssystem.features.common.common.SimpleInputProvider
|
import dev.inmo.postssystem.features.common.common.SimpleInputProvider
|
||||||
import kotlinx.serialization.PolymorphicSerializer
|
import kotlinx.serialization.PolymorphicSerializer
|
||||||
|
@ -12,16 +12,7 @@ class ClientStaticRoutingConfiguration(
|
|||||||
private val clientStatic: String?
|
private val clientStatic: String?
|
||||||
) : ApplicationRoutingConfigurator.Element {
|
) : ApplicationRoutingConfigurator.Element {
|
||||||
override fun Route.invoke() {
|
override fun Route.invoke() {
|
||||||
clientStatic ?.let {
|
resources("web")
|
||||||
static("client") {
|
defaultResource("/web/index.html")
|
||||||
resources(it)
|
|
||||||
get {
|
|
||||||
call.respondRedirect("client/index.html")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
get {
|
|
||||||
call.respondRedirect("client")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,8 @@ import dev.inmo.postssystem.features.content.binary.server.BinaryServerContentSt
|
|||||||
import dev.inmo.postssystem.features.content.common.*
|
import dev.inmo.postssystem.features.content.common.*
|
||||||
import dev.inmo.postssystem.features.content.server.storage.ServerContentStorage
|
import dev.inmo.postssystem.features.content.server.storage.ServerContentStorage
|
||||||
import dev.inmo.postssystem.features.content.server.ServerContentStorageAggregator
|
import dev.inmo.postssystem.features.content.server.ServerContentStorageAggregator
|
||||||
|
import dev.inmo.postssystem.features.content.server.ServerContentStorageWrapper
|
||||||
|
import dev.inmo.postssystem.features.content.text.common.TextContent
|
||||||
import dev.inmo.postssystem.features.content.text.common.TextContentSerializerModuleConfigurator
|
import dev.inmo.postssystem.features.content.text.common.TextContentSerializerModuleConfigurator
|
||||||
import dev.inmo.postssystem.features.content.text.server.TextServerContentStorage
|
import dev.inmo.postssystem.features.content.text.server.TextServerContentStorage
|
||||||
import dev.inmo.postssystem.features.posts.server.ExposedServerPostsStorage
|
import dev.inmo.postssystem.features.posts.server.ExposedServerPostsStorage
|
||||||
@ -55,6 +57,7 @@ import org.koin.core.module.Module
|
|||||||
import org.koin.core.parameter.ParametersHolder
|
import org.koin.core.parameter.ParametersHolder
|
||||||
import org.koin.core.qualifier.StringQualifier
|
import org.koin.core.qualifier.StringQualifier
|
||||||
import org.koin.dsl.*
|
import org.koin.dsl.*
|
||||||
|
import org.w3c.dom.Text
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
private fun Route.print() {
|
private fun Route.print() {
|
||||||
@ -169,8 +172,18 @@ fun getDIModule(
|
|||||||
WriteDistFilesStorage(get(binaryFilesFolderQualifier), get(binaryFilesMetasKeyValueRepoQualifier))
|
WriteDistFilesStorage(get(binaryFilesFolderQualifier), get(binaryFilesMetasKeyValueRepoQualifier))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
singleWithRandomQualifier { BinaryServerContentStorage(get(binaryStorageFilesQualifier)) }
|
singleWithRandomQualifier {
|
||||||
singleWithRandomQualifier { TextServerContentStorage(get()) }
|
ServerContentStorageWrapper(
|
||||||
|
BinaryServerContentStorage(get(binaryStorageFilesQualifier)),
|
||||||
|
BinaryContent::class
|
||||||
|
)
|
||||||
|
}
|
||||||
|
singleWithRandomQualifier {
|
||||||
|
ServerContentStorageWrapper(
|
||||||
|
TextServerContentStorage(get()),
|
||||||
|
TextContent::class
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
single<ServerContentStorage<Content>> { ServerContentStorageAggregator(getAll(), get()) }
|
single<ServerContentStorage<Content>> { ServerContentStorageAggregator(getAll(), get()) }
|
||||||
|
|
||||||
@ -213,7 +226,7 @@ fun getDIModule(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
singleWithBinds { AuthenticationRoutingConfigurator(get(), get(), get()) }
|
singleWithBinds { AuthenticationRoutingConfigurator(get(), get(), get()) }
|
||||||
singleWithBinds { NotFoundStatusPageRedirectToIndex("/client") }
|
singleWithBinds { NotFoundStatusPageRedirectToIndex("/") }
|
||||||
|
|
||||||
if (config.debugMode) {
|
if (config.debugMode) {
|
||||||
single<ApplicationRoutingConfigurator.Element>(StringQualifier("Tracer")) { ApplicationRoutingConfigurator.Element {(this as Routing).trace { application.log.trace(it.buildText()) } } }
|
single<ApplicationRoutingConfigurator.Element>(StringQualifier("Tracer")) { ApplicationRoutingConfigurator.Element {(this as Routing).trace { application.log.trace(it.buildText()) } } }
|
||||||
|
File diff suppressed because one or more lines are too long
@ -48,7 +48,7 @@ class ClientWritePostsService(
|
|||||||
removeRoute
|
removeRoute
|
||||||
)
|
)
|
||||||
private val tempUploadFullPath = buildStandardUrl(
|
private val tempUploadFullPath = buildStandardUrl(
|
||||||
baseUrl,
|
root,
|
||||||
postsCreateTempPathPart
|
postsCreateTempPathPart
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -60,6 +60,7 @@ class ClientWritePostsService(
|
|||||||
tempUploadFullPath,
|
tempUploadFullPath,
|
||||||
provider.file
|
provider.file
|
||||||
)
|
)
|
||||||
|
println(fileId)
|
||||||
it.copy(inputProvider = TempFileIdentifierInputProvider(fileId))
|
it.copy(inputProvider = TempFileIdentifierInputProvider(fileId))
|
||||||
}
|
}
|
||||||
is TempFileIdentifierInputProvider -> it
|
is TempFileIdentifierInputProvider -> it
|
||||||
|
15
services/posts/client/src/commonMain/kotlin/dev/inmo/postssystem/services/posts/client/ui/create/PostCreateUIViewModel.kt
Normal file
15
services/posts/client/src/commonMain/kotlin/dev/inmo/postssystem/services/posts/client/ui/create/PostCreateUIViewModel.kt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package dev.inmo.postssystem.services.posts.client.ui.create
|
||||||
|
|
||||||
|
import dev.inmo.postssystem.features.common.common.*
|
||||||
|
import dev.inmo.postssystem.features.content.common.Content
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
|
class PostCreateUIViewModel(
|
||||||
|
private val model: PostCreateUIModel
|
||||||
|
) : UIViewModel<PostCreateUIState> {
|
||||||
|
override val currentState: StateFlow<PostCreateUIState>
|
||||||
|
get() = model.currentState
|
||||||
|
suspend fun create(content: List<Content>) {
|
||||||
|
model.create(content)
|
||||||
|
}
|
||||||
|
}
|
@ -3,9 +3,8 @@ package dev.inmo.postssystem.services.posts.client
|
|||||||
import dev.inmo.micro_utils.common.MPPFile
|
import dev.inmo.micro_utils.common.MPPFile
|
||||||
import dev.inmo.micro_utils.ktor.client.UnifiedRequester
|
import dev.inmo.micro_utils.ktor.client.UnifiedRequester
|
||||||
import dev.inmo.postssystem.features.files.common.FileId
|
import dev.inmo.postssystem.features.files.common.FileId
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.GlobalScope.coroutineContext
|
import kotlinx.coroutines.GlobalScope.coroutineContext
|
||||||
import kotlinx.coroutines.job
|
|
||||||
import org.w3c.xhr.*
|
import org.w3c.xhr.*
|
||||||
|
|
||||||
internal actual suspend fun UnifiedRequester.tempUpload(
|
internal actual suspend fun UnifiedRequester.tempUpload(
|
||||||
@ -39,7 +38,7 @@ internal actual suspend fun UnifiedRequester.tempUpload(
|
|||||||
request.open("POST", fullTempUploadDraftPath, true)
|
request.open("POST", fullTempUploadDraftPath, true)
|
||||||
request.send(formData)
|
request.send(formData)
|
||||||
|
|
||||||
coroutineContext.job.invokeOnCompletion {
|
currentCoroutineContext().job.invokeOnCompletion {
|
||||||
runCatching {
|
runCatching {
|
||||||
request.abort()
|
request.abort()
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user