diff --git a/client/src/jsMain/kotlin/dev/inmo/postssystem/client/utils/DownloadFile.kt b/client/src/jsMain/kotlin/dev/inmo/postssystem/client/utils/DownloadFile.kt index f1a852d5..43dfbb1c 100644 --- a/client/src/jsMain/kotlin/dev/inmo/postssystem/client/utils/DownloadFile.kt +++ b/client/src/jsMain/kotlin/dev/inmo/postssystem/client/utils/DownloadFile.kt @@ -10,7 +10,7 @@ import org.w3c.files.Blob fun triggerDownloadFile(fullFileInfo: FullFileInfo) { val hiddenElement = document.createElement("a") as HTMLAnchorElement - val url = URL.createObjectURL(Blob(arrayOf(fullFileInfo.byteArrayAllocator().toArrayBuffer()))) + val url = URL.createObjectURL(Blob(arrayOf(fullFileInfo.inputProvider().toArrayBuffer()))) hiddenElement.href = url hiddenElement.target = "_blank" hiddenElement.download = fullFileInfo.name.name diff --git a/client/src/jsMain/kotlin/dev/inmo/postssystem/client/utils/UploadFile.kt b/client/src/jsMain/kotlin/dev/inmo/postssystem/client/utils/UploadFile.kt index 50a8b599..a0231c94 100644 --- a/client/src/jsMain/kotlin/dev/inmo/postssystem/client/utils/UploadFile.kt +++ b/client/src/jsMain/kotlin/dev/inmo/postssystem/client/utils/UploadFile.kt @@ -4,9 +4,8 @@ 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 kotlinx.coroutines.CoroutineScope +import io.ktor.utils.io.core.ByteReadPacket import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.launch import org.khronos.webgl.ArrayBuffer import org.w3c.dom.HTMLInputElement import org.w3c.dom.events.Event @@ -14,28 +13,64 @@ import org.w3c.files.FileReader import org.w3c.files.get fun uploadFileCallbackForHTMLInputChange( - output: MutableStateFlow, - scope: CoroutineScope + onSet: (FullFileInfo) -> Unit ): (Event) -> Unit = { (it.target as? HTMLInputElement) ?.apply { files ?.also { files -> files[0] ?.also { file -> - scope.launch { - val reader: FileReader = FileReader() + val reader: FileReader = FileReader() - reader.onload = { - val bytes = ((it.target.asDynamic()).result as ArrayBuffer).toByteArray() - output.value = FullFileInfo( + reader.onload = { + val bytes = ((it.target.asDynamic()).result as ArrayBuffer).toByteArray() + onSet( + FullFileInfo( FileName(file.name), findBuiltinMimeType(file.type) ?: KnownMimeTypes.Any, - bytes.asAllocator - ) - Unit - } - - reader.readAsArrayBuffer(file) + ) { + ByteReadPacket(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 +): (Event) -> Unit = uploadFileCallbackForHTMLInputChange { + output.value = it +} + +fun uploadFileCallbackForHTMLInputChange( + output: MutableStateFlow +): (Event) -> Unit = uploadFileCallbackForHTMLInputChange { + output.value = it +} + +fun fileCallbackForHTMLInputChange( + output: MutableState +): (Event) -> Unit = fileCallbackForHTMLInputChange { + output.value = it +} + +fun fileCallbackForHTMLInputChange( + output: MutableStateFlow +): (Event) -> Unit = fileCallbackForHTMLInputChange { + output.value = it +} + diff --git a/features/common/common/build.gradle b/features/common/common/build.gradle index ab951534..7f69df12 100644 --- a/features/common/common/build.gradle +++ b/features/common/common/build.gradle @@ -15,6 +15,7 @@ kotlin { api "io.insert-koin:koin-core:$koin_version" api "com.benasher44:uuid:$uuid_version" api "com.soywiz.korlibs.klock:klock:$klock_version" + api "io.ktor:ktor-http:$ktor_version" } } jvmMain { diff --git a/features/common/common/src/commonMain/kotlin/dev/inmo/postssystem/features/common/common/SimpleInputProvider.kt b/features/common/common/src/commonMain/kotlin/dev/inmo/postssystem/features/common/common/SimpleInputProvider.kt new file mode 100644 index 00000000..0fea6ec2 --- /dev/null +++ b/features/common/common/src/commonMain/kotlin/dev/inmo/postssystem/features/common/common/SimpleInputProvider.kt @@ -0,0 +1,30 @@ +package dev.inmo.postssystem.features.common.common + +import dev.inmo.micro_utils.common.ByteArrayAllocatorSerializer +import io.ktor.utils.io.core.* +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +typealias SimpleInputProvider = () -> Input + +object SimpleInputProviderSerializer : KSerializer { + override val descriptor: SerialDescriptor + get() = ByteArrayAllocatorSerializer.descriptor + override fun deserialize(decoder: Decoder): SimpleInputProvider { + val allocator = ByteArrayAllocatorSerializer.deserialize(decoder) + return { + ByteReadPacket(allocator.invoke()) + } + } + + override fun serialize(encoder: Encoder, value: SimpleInputProvider) { + ByteArrayAllocatorSerializer.serialize( + encoder + ) { + value().readBytes() + } + } + +} diff --git a/features/content/binary/common/src/commonMain/kotlin/dev/inmo/postssystem/features/content/binary/common/BinaryContent.kt b/features/content/binary/common/src/commonMain/kotlin/dev/inmo/postssystem/features/content/binary/common/BinaryContent.kt deleted file mode 100644 index d35f11e4..00000000 --- a/features/content/binary/common/src/commonMain/kotlin/dev/inmo/postssystem/features/content/binary/common/BinaryContent.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.inmo.postssystem.features.content.binary.common - -import dev.inmo.micro_utils.common.ByteArrayAllocator -import dev.inmo.micro_utils.common.FileName -import dev.inmo.micro_utils.mime_types.MimeType -import dev.inmo.postssystem.features.content.common.Content -import kotlinx.serialization.Serializable - -@Serializable -data class BinaryContent( - val filename: FileName, - val mimeType: MimeType, - val bytesAllocator: ByteArrayAllocator -) : Content diff --git a/features/content/binary/common/src/commonMain/kotlin/dev/inmo/postssystem/features/content/binary/common/BinaryContentSerializerModuleConfigurator.kt b/features/content/binary/common/src/commonMain/kotlin/dev/inmo/postssystem/features/content/binary/common/BinaryContentSerializerModuleConfigurator.kt index ad2d8bf0..e72de93d 100644 --- a/features/content/binary/common/src/commonMain/kotlin/dev/inmo/postssystem/features/content/binary/common/BinaryContentSerializerModuleConfigurator.kt +++ b/features/content/binary/common/src/commonMain/kotlin/dev/inmo/postssystem/features/content/binary/common/BinaryContentSerializerModuleConfigurator.kt @@ -6,6 +6,6 @@ import kotlinx.serialization.modules.PolymorphicModuleBuilder object BinaryContentSerializerModuleConfigurator : ContentSerializersModuleConfigurator.Element { override fun PolymorphicModuleBuilder.invoke() { - subclass(BinaryContent::class, BinaryContent.serializer()) + subclass(DefaultBinaryContent::class, DefaultBinaryContent.serializer()) } -} \ No newline at end of file +} diff --git a/features/content/binary/common/src/commonMain/kotlin/dev/inmo/postssystem/features/content/binary/common/DefaultBinaryContent.kt b/features/content/binary/common/src/commonMain/kotlin/dev/inmo/postssystem/features/content/binary/common/DefaultBinaryContent.kt new file mode 100644 index 00000000..80c808dd --- /dev/null +++ b/features/content/binary/common/src/commonMain/kotlin/dev/inmo/postssystem/features/content/binary/common/DefaultBinaryContent.kt @@ -0,0 +1,14 @@ +package dev.inmo.postssystem.features.content.binary.common + +import dev.inmo.micro_utils.common.FileName +import dev.inmo.micro_utils.mime_types.MimeType +import dev.inmo.postssystem.features.common.common.SimpleInputProvider +import dev.inmo.postssystem.features.content.common.BinaryContent +import kotlinx.serialization.Serializable + +@Serializable +data class DefaultBinaryContent( + override val filename: FileName, + override val mimeType: MimeType, + override val inputProvider: SimpleInputProvider +) : BinaryContent diff --git a/features/content/binary/server/src/jvmMain/kotlin/dev/inmo/postssystem/features/content/binary/server/BinaryServerContentStorage.kt b/features/content/binary/server/src/jvmMain/kotlin/dev/inmo/postssystem/features/content/binary/server/BinaryServerContentStorage.kt index e5bbe4cd..9fda299e 100644 --- a/features/content/binary/server/src/jvmMain/kotlin/dev/inmo/postssystem/features/content/binary/server/BinaryServerContentStorage.kt +++ b/features/content/binary/server/src/jvmMain/kotlin/dev/inmo/postssystem/features/content/binary/server/BinaryServerContentStorage.kt @@ -2,7 +2,7 @@ package dev.inmo.postssystem.features.content.binary.server import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.repos.UpdatedValuePair -import dev.inmo.postssystem.features.content.binary.common.BinaryContent +import dev.inmo.postssystem.features.content.binary.common.DefaultBinaryContent import dev.inmo.postssystem.features.content.common.* import dev.inmo.postssystem.features.content.server.ServerContentStorage import dev.inmo.postssystem.features.files.common.* @@ -12,7 +12,7 @@ import kotlinx.coroutines.flow.map class BinaryServerContentStorage( private val filesStorage: FilesStorage -) : ServerContentStorage { +) : ServerContentStorage { private val FileId.asContentId get() = ContentId(string) private val ContentId.asFileId @@ -20,23 +20,23 @@ class BinaryServerContentStorage( private val FullFileInfoStorageWrapper.asRegisteredContent get() = RegisteredContent( id.asContentId, - BinaryContent( + DefaultBinaryContent( fileInfo.name, fileInfo.mimeType, - fileInfo.byteArrayAllocator + fileInfo.inputProvider ) ) - private val BinaryContent.asFullFileInfo + private val DefaultBinaryContent.asFullFileInfo get() = FullFileInfo( filename, mimeType, - bytesAllocator + inputProvider ) override val deletedObjectsIdsFlow: Flow = filesStorage.deletedObjectsIdsFlow.map { it.asContentId } override val newObjectsFlow: Flow = filesStorage.newObjectsFlow.map { it.asRegisteredContent } override val updatedObjectsFlow: Flow = filesStorage.updatedObjectsFlow.map { it.asRegisteredContent } - override suspend fun create(values: List): List { + override suspend fun create(values: List): List { return filesStorage.create( values.map { it.asFullFileInfo } ).map { it.asRegisteredContent } @@ -46,14 +46,14 @@ class BinaryServerContentStorage( filesStorage.deleteById(ids.map { it.asFileId }) } - override suspend fun update(id: ContentId, value: BinaryContent): RegisteredContent? { + override suspend fun update(id: ContentId, value: DefaultBinaryContent): RegisteredContent? { return filesStorage.update( id.asFileId, value.asFullFileInfo ) ?.asRegisteredContent } - override suspend fun update(values: List>): List { + override suspend fun update(values: List>): List { return filesStorage.update( values.map { (id, content) -> id.asFileId to content.asFullFileInfo diff --git a/features/content/common/build.gradle b/features/content/common/build.gradle index d4a562e5..00ae37d6 100644 --- a/features/content/common/build.gradle +++ b/features/content/common/build.gradle @@ -11,6 +11,7 @@ kotlin { commonMain { dependencies { api project(":postssystem.features.common.common") + api "dev.inmo:micro_utils.mime_types:$microutils_version" } } } diff --git a/features/content/common/src/commonMain/kotlin/dev/inmo/postssystem/features/content/common/Content.kt b/features/content/common/src/commonMain/kotlin/dev/inmo/postssystem/features/content/common/Content.kt index 04c2d0dc..a5704ff6 100644 --- a/features/content/common/src/commonMain/kotlin/dev/inmo/postssystem/features/content/common/Content.kt +++ b/features/content/common/src/commonMain/kotlin/dev/inmo/postssystem/features/content/common/Content.kt @@ -1,5 +1,8 @@ package dev.inmo.postssystem.features.content.common +import dev.inmo.micro_utils.common.FileName +import dev.inmo.micro_utils.mime_types.MimeType +import dev.inmo.postssystem.features.common.common.SimpleInputProvider import kotlinx.serialization.Serializable import kotlin.jvm.JvmInline @@ -13,7 +16,21 @@ value class ContentId(val string: String) * @see ContentSerializersModuleConfigurator.Element * @see ContentSerializersModuleConfigurator */ -interface Content +sealed interface Content + +/** + * This type of content represents simple content which is easy to serialize/deserialize and to use + */ +interface SimpleContent : Content + +/** + * This type represents some binary data which can be sent with multipart and deserialized from it + */ +interface BinaryContent : Content { + val filename: FileName + val mimeType: MimeType + val inputProvider: SimpleInputProvider +} /** * Content which is already registered in database. Using its [id] you can retrieve all known diff --git a/features/files/common/src/commonMain/kotlin/dev/inmo/postssystem/features/files/common/FileInfo.kt b/features/files/common/src/commonMain/kotlin/dev/inmo/postssystem/features/files/common/FileInfo.kt index 9834820d..ab6c0c16 100644 --- a/features/files/common/src/commonMain/kotlin/dev/inmo/postssystem/features/files/common/FileInfo.kt +++ b/features/files/common/src/commonMain/kotlin/dev/inmo/postssystem/features/files/common/FileInfo.kt @@ -3,6 +3,8 @@ package dev.inmo.postssystem.features.files.common import dev.inmo.micro_utils.common.* import dev.inmo.micro_utils.mime_types.MimeType import dev.inmo.micro_utils.serialization.typed_serializer.TypedSerializer +import dev.inmo.postssystem.features.common.common.SimpleInputProvider +import dev.inmo.postssystem.features.common.common.SimpleInputProviderSerializer import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable @@ -28,8 +30,8 @@ data class MetaFileInfo(override val name: FileName, override val mimeType: Mime data class FullFileInfo( override val name: FileName, override val mimeType: MimeType, - @Serializable(ByteArrayAllocatorSerializer::class) - val byteArrayAllocator: ByteArrayAllocator + @Serializable(SimpleInputProviderSerializer::class) + val inputProvider: SimpleInputProvider ) : FileInfo fun FullFileInfo.toMetaFileInfo() = MetaFileInfo(name, mimeType) diff --git a/features/files/common/src/jvmMain/kotlin/dev/inmo/postssystem/features/files/common/DiskReadFilesStorage.kt b/features/files/common/src/jvmMain/kotlin/dev/inmo/postssystem/features/files/common/DiskReadFilesStorage.kt index d18608af..f87ca62f 100644 --- a/features/files/common/src/jvmMain/kotlin/dev/inmo/postssystem/features/files/common/DiskReadFilesStorage.kt +++ b/features/files/common/src/jvmMain/kotlin/dev/inmo/postssystem/features/files/common/DiskReadFilesStorage.kt @@ -3,6 +3,7 @@ package dev.inmo.postssystem.features.files.common import dev.inmo.postssystem.features.files.common.storage.ReadFilesStorage import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.repos.ReadKeyValueRepo +import io.ktor.utils.io.streams.asInput import java.io.File class DiskReadFilesStorage( @@ -38,7 +39,7 @@ class DiskReadFilesStorage( it.fileInfo.name, it.fileInfo.mimeType ) { - id.file.readBytes() + id.file.inputStream().asInput() } ) } diff --git a/features/files/common/src/jvmMain/kotlin/dev/inmo/postssystem/features/files/common/WriteDistFilesStorage.kt b/features/files/common/src/jvmMain/kotlin/dev/inmo/postssystem/features/files/common/WriteDistFilesStorage.kt index d70645b4..a52aa52b 100644 --- a/features/files/common/src/jvmMain/kotlin/dev/inmo/postssystem/features/files/common/WriteDistFilesStorage.kt +++ b/features/files/common/src/jvmMain/kotlin/dev/inmo/postssystem/features/files/common/WriteDistFilesStorage.kt @@ -36,7 +36,7 @@ class WriteDistFilesStorage( file = newId.file } while (file.exists()) metasKeyValueRepo.set(newId, it.toMetaFileInfo()) - file.writeBytes(it.byteArrayAllocator()) + file.writeBytes(it.inputProvider()) FullFileInfoStorageWrapper(newId, it) } @@ -52,7 +52,7 @@ class WriteDistFilesStorage( override suspend fun update( id: FileId, value: FullFileInfo - ): FullFileInfoStorageWrapper? = id.file.takeIf { it.exists() } ?.writeBytes(value.byteArrayAllocator()) ?.let { + ): FullFileInfoStorageWrapper? = id.file.takeIf { it.exists() } ?.writeBytes(value.inputProvider()) ?.let { val result = FullFileInfoStorageWrapper(id, value.copy()) metasKeyValueRepo.set(id, value.toMetaFileInfo()) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 62c0cb2d..9b50a1ea 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip zipStoreBase=GRADLE_USER_HOME diff --git a/targets/telegram/publication/server/src/jvmMain/kotlin/dev/inmo/postssystem/targets/telegram/publication/server/PublicationTargetTelegram.kt b/targets/telegram/publication/server/src/jvmMain/kotlin/dev/inmo/postssystem/targets/telegram/publication/server/PublicationTargetTelegram.kt index 5163390f..91285c23 100644 --- a/targets/telegram/publication/server/src/jvmMain/kotlin/dev/inmo/postssystem/targets/telegram/publication/server/PublicationTargetTelegram.kt +++ b/targets/telegram/publication/server/src/jvmMain/kotlin/dev/inmo/postssystem/targets/telegram/publication/server/PublicationTargetTelegram.kt @@ -1,13 +1,12 @@ package dev.inmo.postssystem.targets.telegram.publication.server import dev.inmo.micro_utils.mime_types.KnownMimeTypes -import dev.inmo.postssystem.features.content.binary.common.BinaryContent +import dev.inmo.postssystem.features.content.binary.common.DefaultBinaryContent import dev.inmo.postssystem.features.content.text.common.TextContent import dev.inmo.postssystem.features.publication.server.PublicationPost import dev.inmo.postssystem.features.publication.server.PublicationTarget import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.extensions.utils.shortcuts.executeUnsafe -import dev.inmo.tgbotapi.requests.abstracts.MultipartFile import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile import dev.inmo.tgbotapi.requests.send.SendTextMessage import dev.inmo.tgbotapi.requests.send.media.* @@ -23,7 +22,7 @@ class PublicationTargetTelegram( post.content.mapNotNull { val content = it.content when (content) { - is BinaryContent -> { + is DefaultBinaryContent -> { val storageFile by lazy { StorageFile(content.filename.name, content.bytesAllocator()).asMultipartFile() }