From e43c74caea31e9ab7d38f16dfab0eb365d181671 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Tue, 4 Aug 2026 11:21:11 +0200 Subject: [PATCH 01/12] feat: Add new menu for attachment --- .../ui/newMessage/NewMessageEditorManager.kt | 17 ++++++++++- .../mail/ui/newMessage/NewMessageFragment.kt | 1 + app/src/main/res/menu/attachment_menu.xml | 29 +++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 app/src/main/res/menu/attachment_menu.xml diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageEditorManager.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageEditorManager.kt index f145143e972..064e9c59f1e 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageEditorManager.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageEditorManager.kt @@ -28,6 +28,7 @@ import com.infomaniak.mail.MatomoMail.trackEditorActionEvent import com.infomaniak.mail.R import com.infomaniak.mail.databinding.FragmentNewMessageBinding import com.infomaniak.mail.ui.newMessage.encryption.EncryptionMessageManager +import com.infomaniak.mail.utils.SimpleIconPopupMenu import com.infomaniak.mail.utils.extensions.getAttributeColor import dagger.hilt.android.scopes.FragmentScoped import kotlinx.coroutines.launch @@ -44,6 +45,7 @@ class NewMessageEditorManager @Inject constructor(private val insertLinkDialog: private inline val encryptionManager: EncryptionMessageManager get() = _encryptionManager!! private var _openFilePicker: (() -> Unit)? = null + private var _openPhotoPicker: (() -> Unit)? = null fun initValues( newMessageViewModel: NewMessageViewModel, @@ -52,6 +54,7 @@ class NewMessageEditorManager @Inject constructor(private val insertLinkDialog: aiManager: NewMessageAiManager, encryptionManager: EncryptionMessageManager, openFilePicker: () -> Unit, + openPhotoPicker: () -> Unit, ) { super.initValues( newMessageViewModel = newMessageViewModel, @@ -63,6 +66,14 @@ class NewMessageEditorManager @Inject constructor(private val insertLinkDialog: _aiManager = aiManager _encryptionManager = encryptionManager _openFilePicker = openFilePicker + _openPhotoPicker = openPhotoPicker + } + + private fun onMenuItemClicked(itemId: Int) { + when (itemId) { + R.id.attachmentPhotoLibrary -> _openPhotoPicker?.invoke() + R.id.attachmentFile -> _openFilePicker?.invoke() + } } private fun extractUrl(text: String): String { @@ -74,7 +85,7 @@ class NewMessageEditorManager @Inject constructor(private val insertLinkDialog: fun observeEditorFormatActions() = with(binding) { newMessageViewModel.editorAction.observe(viewLifecycleOwner) { (editorAction, _) -> when (editorAction) { - EditorAction.ATTACHMENT -> _openFilePicker?.invoke() + EditorAction.ATTACHMENT -> showAttachmentTypeSelector() EditorAction.LINK -> handleLink() EditorAction.AI -> aiManager.openAiPrompt() EditorAction.BOLD -> editorWebView.toggleBold() @@ -87,6 +98,10 @@ class NewMessageEditorManager @Inject constructor(private val insertLinkDialog: } } + private fun showAttachmentTypeSelector() { + SimpleIconPopupMenu(context, R.menu.attachment_menu, binding.editorAttachment, ::onMenuItemClicked).show() + } + private fun handleLink() = with(binding) { if (buttonLink.isActivated) { editorWebView.unlink() diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt index d323e0e530c..972987d300d 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt @@ -385,6 +385,7 @@ class NewMessageFragment : Fragment() { aiManager = aiManager, encryptionManager = encryptionMessageManager, openFilePicker = filePicker::open, + openPhotoPicker = { filePicker.open("image/*") }, ) encryptionMessageManager.init( diff --git a/app/src/main/res/menu/attachment_menu.xml b/app/src/main/res/menu/attachment_menu.xml new file mode 100644 index 00000000000..24317082b95 --- /dev/null +++ b/app/src/main/res/menu/attachment_menu.xml @@ -0,0 +1,29 @@ + + + + + + + From 3cd3129775468b14daa127012f6a3e424b77c60e Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Tue, 4 Aug 2026 13:39:02 +0200 Subject: [PATCH 02/12] feat: Add script to insert image in body --- app/src/main/res/raw/insert_inline_image.js | 49 +++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 app/src/main/res/raw/insert_inline_image.js diff --git a/app/src/main/res/raw/insert_inline_image.js b/app/src/main/res/raw/insert_inline_image.js new file mode 100644 index 00000000000..7a4366b3004 --- /dev/null +++ b/app/src/main/res/raw/insert_inline_image.js @@ -0,0 +1,49 @@ +/* + * Infomaniak Mail - Android + * Copyright (C) 2026 Infomaniak Network SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +(function() { + function focusEditorIfNeeded() { + if (document.activeElement !== document.body) document.body.focus(); + } + + globalThis.insertInlineImage = function(contentId) { + if (!contentId) return; + + focusEditorIfNeeded(); + + var img = document.createElement("img"); + img.src = "cid:" + contentId; + img.alt = ""; + img.style.maxWidth = "100%"; + img.style.height = "auto"; + + var selection = window.getSelection(); + if (selection && selection.rangeCount > 0) { + var range = selection.getRangeAt(0); + range.deleteContents(); + range.insertNode(img); + + range.setStartAfter(img); + range.collapse(true); + selection.removeAllRanges(); + selection.addRange(range); + return; + } + + document.body.appendChild(img); + }; +})(); From 14486909931ae1a8d9a3f07e81ed2a679b7ce18b Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Tue, 4 Aug 2026 13:39:42 +0200 Subject: [PATCH 03/12] feat: Insert script and add observer --- .../infomaniak/mail/data/models/Attachment.kt | 5 ++ .../infomaniak/mail/data/api/ApiRepository.kt | 10 ++-- .../mail/ui/newMessage/NewMessageFragment.kt | 46 ++++++++++++++++++- .../mail/ui/newMessage/NewMessageViewModel.kt | 10 ++++ .../infomaniak/mail/utils/HtmlFormatter.kt | 4 ++ 5 files changed, 71 insertions(+), 4 deletions(-) diff --git a/OldKotlin/realm-models/src/main/kotlin/com/infomaniak/mail/data/models/Attachment.kt b/OldKotlin/realm-models/src/main/kotlin/com/infomaniak/mail/data/models/Attachment.kt index 09889b4a171..119db9c5b60 100644 --- a/OldKotlin/realm-models/src/main/kotlin/com/infomaniak/mail/data/models/Attachment.kt +++ b/OldKotlin/realm-models/src/main/kotlin/com/infomaniak/mail/data/models/Attachment.kt @@ -67,6 +67,11 @@ class Attachment : EmbeddedRealmObject, Attachable { val disposition: AttachmentDisposition? get() = enumValueOfOrNull(_disposition) + fun markAsInline(contentId: String) { + _disposition = AttachmentDisposition.INLINE.name + this.contentId = contentId + } + fun initLocalValues(name: String, size: Long, mimeType: String, uri: String): Attachment { this.name = name this.size = size diff --git a/app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt b/app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt index 3513d856996..51d7290bb8c 100644 --- a/app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt +++ b/app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt @@ -610,13 +610,17 @@ object ApiRepository : ApiRepositoryCore() { mailbox: Mailbox, userApiToken: String, ): ApiResponse? { + val attachmentDisposition = attachment.disposition?.name?.lowercase() ?: "attachment" + @OptIn(ManualAuthorizationRequired::class) - val headers = HttpUtils.getHeaders(contentType = null).newBuilder() + val headersBuilder = HttpUtils.getHeaders(contentType = null).newBuilder() .set("Authorization", "Bearer $userApiToken") .addUnsafeNonAscii("x-ws-attachment-filename", attachment.name) .add("x-ws-attachment-mime-type", attachment.mimeType) - .add("x-ws-attachment-disposition", "attachment") - .build() + .add("x-ws-attachment-disposition", attachmentDisposition) + attachment.contentId?.let { headersBuilder.add("x-ws-attachment-content-id", it) } + val headers = headersBuilder.build() + val request = Request.Builder().url(ApiRoutes.createAttachment(mailbox.uuid)) .headers(headers) .post(attachmentFile.asRequestBody(attachment.mimeType.toMediaType())) diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt index 972987d300d..6879ee62e2b 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt @@ -106,6 +106,7 @@ import com.infomaniak.mail.utils.HtmlFormatter.Companion.getEditorMentionClickHa import com.infomaniak.mail.utils.HtmlFormatter.Companion.getEditorMentionsDetectorScript import com.infomaniak.mail.utils.HtmlFormatter.Companion.getFixStyleScript import com.infomaniak.mail.utils.HtmlFormatter.Companion.getIncludeQuotesScript +import com.infomaniak.mail.utils.HtmlFormatter.Companion.getInsertInlineImageScript import com.infomaniak.mail.utils.HtmlFormatter.Companion.getInsertMentionScript import com.infomaniak.mail.utils.HtmlFormatter.Companion.getMentionDeletionObserverScript import com.infomaniak.mail.utils.HtmlFormatter.Companion.getMentionsStyle @@ -116,10 +117,13 @@ import com.infomaniak.mail.utils.MessageBodyUtils.EDITOR_LOCAL_SIGNATURE_ID import com.infomaniak.mail.utils.MessageBodyUtils.INFOMANIAK_FORWARD_QUOTE_HTML_CLASS_NAME import com.infomaniak.mail.utils.MessageBodyUtils.INFOMANIAK_REPLY_QUOTE_HTML_CLASS_NAME import com.infomaniak.mail.utils.MessageBodyUtils.INFOMANIAK_SIGNATURE_HTML_CLASS_NAME +import com.infomaniak.mail.utils.LocalStorageUtils import com.infomaniak.mail.utils.SentryDebug import com.infomaniak.mail.utils.SignatureUtils import com.infomaniak.mail.utils.WebViewUtils.Companion.evaluateJs import com.infomaniak.mail.utils.WebViewUtils.Companion.setupNewMessageWebViewSettings +import com.infomaniak.mail.data.models.extensions.getCacheFile +import com.infomaniak.mail.data.models.extensions.getUploadLocalFile import com.infomaniak.mail.utils.extensions.AttachmentExt import com.infomaniak.mail.utils.extensions.AttachmentExt.openAttachment import com.infomaniak.mail.utils.extensions.applySideAndBottomSystemInsets @@ -169,6 +173,7 @@ class NewMessageFragment : Fragment() { private val fixStyle by lazy { requireContext().getFixStyleScript() } private val setAiContentScript by lazy { requireContext().getSetAiContentScript() } private val getEditorBodyScript by lazy { requireContext().getEditorBodyScript() } + private val insertInlineImageScript by lazy { requireContext().getInsertInlineImageScript() } private val insertMentionScript by lazy { requireContext().getInsertMentionScript() } private val mentionClickHandlerScript by lazy { requireContext().getEditorMentionClickHandlerScript() } private val removeElementsByIdScript by lazy { requireContext().getRemoveElementsByIdScript() } @@ -187,6 +192,9 @@ class NewMessageFragment : Fragment() { private val filePicker = FilePicker(fragment = this).apply { initCallback { uris -> newMessageViewModel.importAttachmentsLiveData.value = uris } } + private val photoPicker = FilePicker(fragment = this).apply { + initCallback { uris -> newMessageViewModel.importInlineAttachmentsLiveData.value = uris } + } private var addressListPopupWindow: ListPopupWindow? = null @@ -385,7 +393,7 @@ class NewMessageFragment : Fragment() { aiManager = aiManager, encryptionManager = encryptionMessageManager, openFilePicker = filePicker::open, - openPhotoPicker = { filePicker.open("image/*") }, + openPhotoPicker = { photoPicker.open("image/*") }, ) encryptionMessageManager.init( @@ -526,6 +534,7 @@ class NewMessageFragment : Fragment() { addScript(fixStyle) addScript(editorJsBridgeScript) addScript(deletedInlineImagesObserverScript) + addScript(insertInlineImageScript) addScript(mentionClickHandlerScript) addScript(removeElementsByIdScript) @@ -800,6 +809,7 @@ class NewMessageFragment : Fragment() { if (isFirstTime) { isFirstTime = false observeImportAttachments() + observeImportInlineAttachments() } else if (attachments.count() > attachmentAdapter.itemCount) { // If we are adding Attachments, directly upload them to save time when sending/saving the Draft. newMessageViewModel.uploadAttachmentsToServer(attachments) @@ -829,6 +839,40 @@ class NewMessageFragment : Fragment() { } } + private fun observeImportInlineAttachments() = with(newMessageViewModel) { + importInlineAttachmentsLiveData.observe(viewLifecycleOwner) { uris -> + val currentAttachments = attachmentsLiveData.valueOrEmpty() + importNewAttachments(currentAttachments, uris) { newAttachments -> + val inlineAttachments = prepareInlineAttachments(newAttachments) + val updatedAttachments = currentAttachments + inlineAttachments + attachmentsLiveData.postValue(updatedAttachments) + + viewLifecycleOwner.lifecycleScope.launch { + inlineAttachments.forEach { attachment -> + attachment.getUploadLocalFile()?.inputStream()?.use { inputStream -> + LocalStorageUtils.saveAttachmentToCacheDir(inputStream, attachment.getCacheFile(requireContext())) + } + } + refreshEditorWebViewClient(updatedAttachments) + inlineAttachments.mapNotNull(Attachment::contentId).forEach { contentId -> + binding.editorWebView.executeJsMethodWhenEditorIsSetup( + JsExecutableMethod("insertInlineImage", contentId), + ) + } + } + } + } + } + + private fun refreshEditorWebViewClient(attachments: List) { + val alwaysShowExternalContent = localSettings.externalContent == LocalSettings.ExternalContent.ALWAYS + binding.editorWebView.initEditorWebviewClient( + attachments = attachments, + shouldLoadDistantResources = alwaysShowExternalContent || newMessageViewModel.shouldLoadDistantResources(), + onPageFinished = {}, + ) + } + private fun observeImportAttachmentsResult() = with(newMessageViewModel) { importAttachmentsResult.observe(viewLifecycleOwner) { result -> if (result == ImportationResult.ATTACHMENTS_TOO_BIG) showSnackbar(R.string.attachmentFileLimitReached) diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt index feffa90df38..d900321fc9c 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt @@ -150,6 +150,7 @@ import kotlinx.coroutines.withContext import org.jsoup.nodes.Document import splitties.experimental.ExperimentalSplittiesApi import java.util.Date +import java.util.UUID import javax.inject.Inject @OptIn(ExperimentalCoroutinesApi::class) @@ -224,6 +225,7 @@ class NewMessageViewModel @Inject constructor( //region Attachments val importAttachmentsLiveData = SingleLiveEvent>() + val importInlineAttachmentsLiveData = SingleLiveEvent>() val importAttachmentsResult = SingleLiveEvent() //endRegion @@ -800,6 +802,14 @@ class NewMessageViewModel @Inject constructor( return newAttachments } + fun prepareInlineAttachments(attachments: List): List { + attachments.forEach { attachment -> + val contentId = attachment.contentId?.takeIf(String::isNotBlank) ?: "${UUID.randomUUID()}@infomaniak.com" + attachment.markAsInline(contentId) + } + return attachments + } + private suspend fun importAttachment(uri: Uri, availableSpace: Long): Pair { val (fileName, fileSize) = getFileNameAndSize(uri) ?: return null to false diff --git a/app/src/main/java/com/infomaniak/mail/utils/HtmlFormatter.kt b/app/src/main/java/com/infomaniak/mail/utils/HtmlFormatter.kt index 55ff9f5493f..922c75dfd52 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/HtmlFormatter.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/HtmlFormatter.kt @@ -300,6 +300,10 @@ class HtmlFormatter(private val html: String) { return loadScript(R.raw.insert_mention) } + fun Context.getInsertInlineImageScript(): String { + return loadScript(R.raw.insert_inline_image) + } + fun Context.getEditorMentionClickHandlerScript(): String { return loadScript(R.raw.editor_mention_click_handler) } From 6aa16ec58ce90f208717d54c8565d40c31079c26 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Tue, 4 Aug 2026 15:04:03 +0200 Subject: [PATCH 04/12] fix: Correct image style in the editor --- app/src/main/res/raw/editor_style.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/res/raw/editor_style.css b/app/src/main/res/raw/editor_style.css index e8d9d4707b3..3b3f7805bea 100644 --- a/app/src/main/res/raw/editor_style.css +++ b/app/src/main/res/raw/editor_style.css @@ -20,3 +20,8 @@ a[data-ik-mention-ref] { background-color: var(--mail-content-mention-background-color, #f1f1f1); font-weight: var(--mail-content-mention-font-weight, inherit); } + +img { + max-width: 100%; + height: auto; +} From ec01a05a21d37c71c23d8840e01e32c4d115b324 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Tue, 4 Aug 2026 16:13:21 +0200 Subject: [PATCH 05/12] feat: Add br --- app/src/main/res/raw/insert_inline_image.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/src/main/res/raw/insert_inline_image.js b/app/src/main/res/raw/insert_inline_image.js index 7a4366b3004..c28ded02296 100644 --- a/app/src/main/res/raw/insert_inline_image.js +++ b/app/src/main/res/raw/insert_inline_image.js @@ -35,9 +35,18 @@ if (selection && selection.rangeCount > 0) { var range = selection.getRangeAt(0); range.deleteContents(); + + var brBefore = document.createElement("br"); + range.insertNode(brBefore); + + range.setStartAfter(brBefore); range.insertNode(img); + var brAfter = document.createElement("br"); range.setStartAfter(img); + range.insertNode(brAfter); + + range.setStartAfter(brAfter); range.collapse(true); selection.removeAllRanges(); selection.addRange(range); From 375e520e8e0c2e46b151d79ac2903842d6fe5d8c Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Tue, 4 Aug 2026 16:21:06 +0200 Subject: [PATCH 06/12] feat: Add strings --- app/src/main/res/menu/attachment_menu.xml | 4 ++-- app/src/main/res/values-da/strings.xml | 2 ++ app/src/main/res/values-de/strings.xml | 2 ++ app/src/main/res/values-el/strings.xml | 2 ++ app/src/main/res/values-es/strings.xml | 2 ++ app/src/main/res/values-fi/strings.xml | 2 ++ app/src/main/res/values-fr/strings.xml | 2 ++ app/src/main/res/values-it/strings.xml | 2 ++ app/src/main/res/values-nb/strings.xml | 2 ++ app/src/main/res/values-nl/strings.xml | 2 ++ app/src/main/res/values-pl/strings.xml | 2 ++ app/src/main/res/values-pt/strings.xml | 2 ++ app/src/main/res/values-sv/strings.xml | 2 ++ app/src/main/res/values/strings.xml | 2 ++ 14 files changed, 28 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/menu/attachment_menu.xml b/app/src/main/res/menu/attachment_menu.xml index 24317082b95..df5a01debd8 100644 --- a/app/src/main/res/menu/attachment_menu.xml +++ b/app/src/main/res/menu/attachment_menu.xml @@ -20,10 +20,10 @@ + android:title="@string/importPhotosButton" /> + android:title="@string/importFilesButton" /> diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index e41c0dfa084..70095383226 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -368,6 +368,8 @@ Besked oversat Google Play Services er påkrævet Gruppe: %s + Filer + Billeder Indbakke Din mail-lagerplads er næsten fuld. Opgrader til ubegrænset lagerplads og lås nye funktioner op. Din mail-lagerplads er fuld. Opgrader til ubegrænset lagerplads og lås nye funktioner op. diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 828edb87785..e84efd13413 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -368,6 +368,8 @@ Nachricht übersetzt Google Play Services sind erforderlich Gruppe: %s + Dateien + Fotos Posteingang Der Speicherplatz in Ihrer Mail ist bald voll. Ändern Sie Ihr Angebot auf unbegrenzten Speicherplatz und schalten Sie neue Funktionen frei. Der Speicherplatz in Ihrer Mail ist voll. Ändern Sie Ihr Angebot auf unbegrenzten Speicherplatz und schalten Sie neue Funktionen frei. diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 939b0f33efc..646c3cc77cb 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -368,6 +368,8 @@ Μήνυμα μεταφρασμένο Απαιτούνται οι Υπηρεσίες Google Play Ομάδα: %s + Αρχεία + Φωτογραφίες Εισερχόμενα Ο χώρος αποθήκευσης του Mail σας είναι σχεδόν πλήρης. Αναβαθμίστε σε απεριόριστο χώρο και ξεκλειδώστε νέες δυνατότητες. Ο χώρος αποθήκευσης του Mail σας είναι πλήρης. Αναβαθμίστε σε απεριόριστο χώρο και ξεκλειδώστε νέες δυνατότητες. diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 819d28e9b37..2e48f007092 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -368,6 +368,8 @@ Mensaje traducido Se requieren los servicios de Google Play Grupo: %s + Archivos + Fotos Bandeja de entrada Tu espacio de almacenamiento de Mail está casi lleno. Modifica tu oferta para conseguir almacenamiento ilimitado y desbloquear nuevas funciones. Tu espacio de almacenamiento de Mail está lleno. Modifica tu oferta para conseguir almacenamiento ilimitado y desbloquear nuevas funciones. diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 5ee1d68ad9f..b34888639ff 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -368,6 +368,8 @@ Viesti käännetty Google Play Services vaaditaan Ryhmä: %s + Tiedostot + Kuvat Saapuneet Mail-tallennustilasi on melkein täynnä. Päivitä rajattomaan tallennustilaan ja avaa uusia ominaisuuksia. Mail-tallennustilasi on täynnä. Päivitä rajattomaan tallennustilaan ja avaa uusia ominaisuuksia. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 2fc302578bc..bf02bb47644 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -368,6 +368,8 @@ Message traduit Les Google Play Services sont requis Groupe : %s + Fichiers + Photos Boîte de réception L’espace de stockage de votre Mail est bientôt plein. Modifiez votre offre pour avoir un stockage illimité et débloquez de nouvelles fonctionnalités. L’espace de stockage de votre Mail est plein. Modifiez votre offre pour avoir un stockage illimité et débloquez de nouvelles fonctionnalités. diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index da2cccff1a0..554e01624c8 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -368,6 +368,8 @@ Messaggio tradotto I servizi Google Play sono necessari Gruppo: %s + File + Foto Posta in arrivo Lo spazio di archiviazione della posta è quasi esaurito. Modificate la vostra offerta per ottenere uno spazio di archiviazione illimitato e sbloccare nuove funzionalità. Lo spazio di archiviazione della posta è esaurito. Modificate la vostra offerta per ottenere uno spazio di archiviazione illimitato e sbloccare nuove funzionalità. diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index d0dc46c4dd6..1077b1855a8 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -368,6 +368,8 @@ Melding oversatt Google Play Services kreves Gruppe: %s + Filer + Bilder Innboks Din e-postlagringsplass er nesten full. Oppgrader til ubegrenset lagring og lås opp nye funksjoner. Din e-postlagringsplass er full. Oppgrader til ubegrenset lagring og lås opp nye funksjoner. diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 686171535ce..c2e417583b6 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -368,6 +368,8 @@ Bericht vertaald Google Play Services zijn vereist Groep: %s + Bestanden + Foto’s Inbox Uw Mail-opslagruimte is bijna vol. Upgrade naar onbeperkte opslag en ontgrendel nieuwe functies. Uw Mail-opslagruimte is vol. Upgrade naar onbeperkte opslag en ontgrendel nieuwe functies. diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 6ed805948c2..50840268d81 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -382,6 +382,8 @@ Wiadomość przetłumaczona Wymagane są Usługi Google Play Grupa: %s + Pliki + Zdjęcia Skrzynka odbiorcza Miejsce do przechowywania Twojej poczty jest prawie pełne. Przejdź na nieograniczoną przestrzeń i odblokuj nowe funkcje. Miejsce do przechowywania Twojej poczty jest pełne. Przejdź na nieograniczoną przestrzeń i odblokuj nowe funkcje. diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 94a66ef647e..1847734b58d 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -368,6 +368,8 @@ Mensagem traduzida Os Google Play Services são necessários Grupo: %s + Ficheiros + Fotos Caixa de entrada O espaço de armazenamento do seu Mail está quase cheio. Atualize para armazenamento ilimitado e desbloqueie novas funcionalidades. O espaço de armazenamento do seu Mail está cheio. Atualize para armazenamento ilimitado e desbloqueie novas funcionalidades. diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index c15eb0952e2..b37434870dd 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -368,6 +368,8 @@ Meddelande översatt Google Play Services krävs Grupp: %s + Filer + Bilder Inkorg Din e-postlagring är nästan full. Uppgradera till obegränsad lagring och lås upp nya funktioner. Din e-postlagring är full. Uppgradera till obegränsad lagring och lås upp nya funktioner. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ad4b8709415..cab846862f7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -372,6 +372,8 @@ Message translated Google Play Services are required Group: %s + Files + Photos Inbox Your Mail storage space is almost full. Upgrade to unlimited storage and unlock new features. Your Mail storage space is full. Upgrade to unlimited storage and unlock new features. From 95aba8bee10bd9f5839e1bafd1cafba3594b827d Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Wed, 5 Aug 2026 14:17:29 +0200 Subject: [PATCH 07/12] fix: Resolve concurrent import issue --- .../mail/ui/newMessage/NewMessageFragment.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt index 6879ee62e2b..e1697c34c35 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt @@ -77,6 +77,8 @@ import com.infomaniak.mail.data.models.correspondent.MergedContact import com.infomaniak.mail.data.models.draft.Draft import com.infomaniak.mail.data.models.draft.Draft.DraftMode import com.infomaniak.mail.data.models.draft.DraftAction +import com.infomaniak.mail.data.models.extensions.getCacheFile +import com.infomaniak.mail.data.models.extensions.getUploadLocalFile import com.infomaniak.mail.data.models.extensions.kSuite import com.infomaniak.mail.data.models.mailbox.Mailbox import com.infomaniak.mail.data.models.signature.Signature @@ -113,17 +115,15 @@ import com.infomaniak.mail.utils.HtmlFormatter.Companion.getMentionsStyle import com.infomaniak.mail.utils.HtmlFormatter.Companion.getRemoveElementsByIdScript import com.infomaniak.mail.utils.HtmlFormatter.Companion.getReplaceSignatureScript import com.infomaniak.mail.utils.HtmlFormatter.Companion.getSetAiContentScript +import com.infomaniak.mail.utils.LocalStorageUtils import com.infomaniak.mail.utils.MessageBodyUtils.EDITOR_LOCAL_SIGNATURE_ID import com.infomaniak.mail.utils.MessageBodyUtils.INFOMANIAK_FORWARD_QUOTE_HTML_CLASS_NAME import com.infomaniak.mail.utils.MessageBodyUtils.INFOMANIAK_REPLY_QUOTE_HTML_CLASS_NAME import com.infomaniak.mail.utils.MessageBodyUtils.INFOMANIAK_SIGNATURE_HTML_CLASS_NAME -import com.infomaniak.mail.utils.LocalStorageUtils import com.infomaniak.mail.utils.SentryDebug import com.infomaniak.mail.utils.SignatureUtils import com.infomaniak.mail.utils.WebViewUtils.Companion.evaluateJs import com.infomaniak.mail.utils.WebViewUtils.Companion.setupNewMessageWebViewSettings -import com.infomaniak.mail.data.models.extensions.getCacheFile -import com.infomaniak.mail.data.models.extensions.getUploadLocalFile import com.infomaniak.mail.utils.extensions.AttachmentExt import com.infomaniak.mail.utils.extensions.AttachmentExt.openAttachment import com.infomaniak.mail.utils.extensions.applySideAndBottomSystemInsets @@ -834,7 +834,7 @@ class NewMessageFragment : Fragment() { importAttachmentsLiveData.observe(viewLifecycleOwner) { uris -> val currentAttachments = attachmentsLiveData.valueOrEmpty() importNewAttachments(currentAttachments, uris) { newAttachments -> - attachmentsLiveData.postValue(currentAttachments + newAttachments) + attachmentsLiveData.postValue(attachmentsLiveData.valueOrEmpty() + newAttachments) } } } @@ -844,7 +844,7 @@ class NewMessageFragment : Fragment() { val currentAttachments = attachmentsLiveData.valueOrEmpty() importNewAttachments(currentAttachments, uris) { newAttachments -> val inlineAttachments = prepareInlineAttachments(newAttachments) - val updatedAttachments = currentAttachments + inlineAttachments + val updatedAttachments = attachmentsLiveData.valueOrEmpty() + inlineAttachments attachmentsLiveData.postValue(updatedAttachments) viewLifecycleOwner.lifecycleScope.launch { From 02cd796b2b79a74185ecc92fbc98dd74eaeea524 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Wed, 5 Aug 2026 14:26:09 +0200 Subject: [PATCH 08/12] fix: Clear error when attachment is removed --- .../mail/ui/main/thread/AttachmentAdapter.kt | 4 ++-- .../mail/ui/newMessage/NewMessageFragment.kt | 5 +++-- .../mail/ui/newMessage/NewMessageViewModel.kt | 22 ++++++++++--------- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/AttachmentAdapter.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/AttachmentAdapter.kt index fb8055bbace..dcf14941c20 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/AttachmentAdapter.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/AttachmentAdapter.kt @@ -30,7 +30,7 @@ import com.infomaniak.mail.utils.Utils.runCatchingRealm class AttachmentAdapter( private val shouldDisplayCloseButton: Boolean = false, - private val onDelete: ((position: Int) -> Unit)? = null, + private val onDelete: ((attachable: Attachable) -> Unit)? = null, private val onAttachmentClicked: ((Attachable) -> Unit)? = null, private val onAttachmentOptionsClicked: ((Attachable) -> Unit)? = null, ) : Adapter() { @@ -67,7 +67,7 @@ class AttachmentAdapter( attachments.removeAt(index) notifyItemRemoved(index) - onDelete?.invoke(index) + onDelete?.invoke(attachment) } } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt index e1697c34c35..09112e56285 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt @@ -69,6 +69,7 @@ import com.infomaniak.mail.MatomoMail.trackNewMessageEvent import com.infomaniak.mail.MatomoMail.trackScheduleSendEvent import com.infomaniak.mail.R import com.infomaniak.mail.data.LocalSettings +import com.infomaniak.mail.data.models.Attachable import com.infomaniak.mail.data.models.Attachment import com.infomaniak.mail.data.models.AttachmentDisposition import com.infomaniak.mail.data.models.FeatureFlag @@ -970,9 +971,9 @@ class NewMessageFragment : Fragment() { super.onStop() } - private fun onDeleteAttachment(position: Int) { + private fun onDeleteAttachment(attachable: Attachable) { trackAttachmentActionsEvent(MatomoName.Delete) - newMessageViewModel.deleteAttachment(position) + if (attachable is Attachment) newMessageViewModel.deleteAttachment(attachable) } private fun setupSendButtons(mailbox: Mailbox) = with(binding) { diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt index d900321fc9c..63c2f3fadb8 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt @@ -863,21 +863,23 @@ class NewMessageViewModel @Inject constructor( if (recipient.isDisplayedAsExternal) trackExternalEvent(MatomoName.DeleteRecipient) } - fun deleteAttachment(position: Int) = viewModelScope.launch(ioCoroutineContext) { + fun deleteAttachment(attachmentToDelete: Attachment) = viewModelScope.launch(ioCoroutineContext) { runCatching { val attachments = attachmentsLiveData.valueOrEmpty().toMutableList() - val attachment = attachments[position] - attachment.getUploadLocalFile()?.delete() - LocalStorageUtils.deleteAttachmentUploadDir(appContext, draftLocalUuid!!, attachment.localUuid) + val attachment = attachments.findSpecificAttachment(attachmentToDelete) + attachment?.let { + it.getUploadLocalFile()?.delete() + LocalStorageUtils.deleteAttachmentUploadDir(appContext, draftLocalUuid!!, it.localUuid) - mailboxContentRealm().write { - DraftController.updateDraftBlocking(draftLocalUuid!!, realm = this) { - it.attachments.findSpecificAttachment(attachment)?.let(::delete) + mailboxContentRealm().write { + DraftController.updateDraftBlocking(draftLocalUuid!!, realm = this) { draft -> + draft.attachments.findSpecificAttachment(it)?.let(::delete) + } } - } - attachments.removeAt(position) - attachmentsLiveData.postValue(attachments) + attachments.remove(it) + attachmentsLiveData.postValue(attachments) + } } } From 416364fe2252c54c0c41eaee6f6b6b07116fa4d1 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Wed, 5 Aug 2026 14:45:44 +0200 Subject: [PATCH 09/12] fix: Add upload cache on Dispatcher.IO --- .../mail/ui/newMessage/NewMessageFragment.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt index 09112e56285..5acaa049400 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt @@ -146,10 +146,12 @@ import dagger.hilt.android.AndroidEntryPoint import io.sentry.Sentry import io.sentry.SentryLevel import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import splitties.experimental.ExperimentalSplittiesApi import java.util.Date import javax.inject.Inject @@ -849,9 +851,11 @@ class NewMessageFragment : Fragment() { attachmentsLiveData.postValue(updatedAttachments) viewLifecycleOwner.lifecycleScope.launch { - inlineAttachments.forEach { attachment -> - attachment.getUploadLocalFile()?.inputStream()?.use { inputStream -> - LocalStorageUtils.saveAttachmentToCacheDir(inputStream, attachment.getCacheFile(requireContext())) + withContext(Dispatchers.IO) { + inlineAttachments.forEach { attachment -> + attachment.getUploadLocalFile()?.inputStream()?.use { inputStream -> + LocalStorageUtils.saveAttachmentToCacheDir(inputStream, attachment.getCacheFile(requireContext())) + } } } refreshEditorWebViewClient(updatedAttachments) From f14326636b5fc143defc7c168d58d964a401c867 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Wed, 5 Aug 2026 15:02:32 +0200 Subject: [PATCH 10/12] fix: Fix cache file collision for inline attachments --- .../data/models/extensions/AttachableExtensions.kt | 11 +++++++++++ .../mail/ui/newMessage/NewMessageFragment.kt | 4 +++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/infomaniak/mail/data/models/extensions/AttachableExtensions.kt b/app/src/main/java/com/infomaniak/mail/data/models/extensions/AttachableExtensions.kt index 600cf370a0b..510610e3784 100644 --- a/app/src/main/java/com/infomaniak/mail/data/models/extensions/AttachableExtensions.kt +++ b/app/src/main/java/com/infomaniak/mail/data/models/extensions/AttachableExtensions.kt @@ -66,3 +66,14 @@ fun Attachable.getCacheFile( } is SwissTransferFile -> File("") } + +fun Attachment.getInlineCacheFile( + context: Context, + userId: Int = AccountUtils.currentUserId, + mailboxId: Int = AccountUtils.currentMailboxId, +): File { + val cacheFolder = LocalStorageUtils.getAttachmentsCacheDir(context, extractPathFromResource(), userId, mailboxId) + val extension = name.substringAfterLast('.', "") + val uniqueName = if (extension.isNotEmpty()) "$localUuid.$extension" else localUuid + return File(cacheFolder, uniqueName) +} diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt index 5acaa049400..4af0bad94e7 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt @@ -79,6 +79,7 @@ import com.infomaniak.mail.data.models.draft.Draft import com.infomaniak.mail.data.models.draft.Draft.DraftMode import com.infomaniak.mail.data.models.draft.DraftAction import com.infomaniak.mail.data.models.extensions.getCacheFile +import com.infomaniak.mail.data.models.extensions.getInlineCacheFile import com.infomaniak.mail.data.models.extensions.getUploadLocalFile import com.infomaniak.mail.data.models.extensions.kSuite import com.infomaniak.mail.data.models.mailbox.Mailbox @@ -854,7 +855,8 @@ class NewMessageFragment : Fragment() { withContext(Dispatchers.IO) { inlineAttachments.forEach { attachment -> attachment.getUploadLocalFile()?.inputStream()?.use { inputStream -> - LocalStorageUtils.saveAttachmentToCacheDir(inputStream, attachment.getCacheFile(requireContext())) + val cacheFile = attachment.getInlineCacheFile(requireContext()) + LocalStorageUtils.saveAttachmentToCacheDir(inputStream, cacheFile) } } } From bd3bf74d3d95337422e1ee932a2f737f1bffb516 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Fri, 7 Aug 2026 09:08:27 +0200 Subject: [PATCH 11/12] fix: Allow inserting multiple inline items --- .../models/extensions/AttachmentExtensions.kt | 5 +++ .../webViewClient/MessageWebViewClient.kt | 8 +++- app/src/main/res/raw/insert_inline_image.js | 37 ++++++++++++------- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/data/models/extensions/AttachmentExtensions.kt b/app/src/main/java/com/infomaniak/mail/data/models/extensions/AttachmentExtensions.kt index 73d8b090cbd..f7fa6d6d725 100644 --- a/app/src/main/java/com/infomaniak/mail/data/models/extensions/AttachmentExtensions.kt +++ b/app/src/main/java/com/infomaniak/mail/data/models/extensions/AttachmentExtensions.kt @@ -22,6 +22,7 @@ package com.infomaniak.mail.data.models.extensions import androidx.core.net.toFile import androidx.core.net.toUri import com.infomaniak.mail.data.models.Attachment +import com.infomaniak.mail.data.models.AttachmentDisposition import com.infomaniak.mail.data.models.AttachmentUploadStatus import com.infomaniak.mail.data.models.InternalModelProperties import com.infomaniak.mail.data.models.draft.Draft @@ -42,6 +43,10 @@ fun Attachment.setUploadStatus(attachmentUploadStatus: AttachmentUploadStatus, d fun Attachment.backupLocalData(oldAttachment: Attachment, draft: Draft) { localUuid = oldAttachment.localUuid uploadLocalUri = oldAttachment.uploadLocalUri + contentId = oldAttachment.contentId + if (oldAttachment.disposition == AttachmentDisposition.INLINE) { + oldAttachment.contentId?.let { markAsInline(it) } + } setUploadStatus(AttachmentUploadStatus.UPLOADED, draft, "backupLocalData -> setUploadStatus") } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/webViewClient/MessageWebViewClient.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/webViewClient/MessageWebViewClient.kt index 01e530b4254..ba6f06cf45b 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/webViewClient/MessageWebViewClient.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/webViewClient/MessageWebViewClient.kt @@ -25,7 +25,9 @@ import android.webkit.WebView import android.webkit.WebViewClient import com.infomaniak.mail.data.api.ApiRepository import com.infomaniak.mail.data.models.Attachment +import com.infomaniak.mail.data.models.AttachmentDisposition import com.infomaniak.mail.data.models.extensions.getCacheFile +import com.infomaniak.mail.data.models.extensions.getInlineCacheFile import com.infomaniak.mail.data.models.extensions.hasUsableCache import com.infomaniak.mail.utils.LocalStorageUtils import com.infomaniak.mail.utils.Utils @@ -49,7 +51,11 @@ abstract class MessageWebViewClient( if (url?.scheme.equals(CID_SCHEME, ignoreCase = true)) { val cid = url.schemeSpecificPart return cidDictionary[cid]?.let { attachment -> - val cacheFile = attachment.getCacheFile(context) + val cacheFile = if (attachment.disposition == AttachmentDisposition.INLINE) { + attachment.getInlineCacheFile(context).takeIf { it.exists() } ?: attachment.getCacheFile(context) + } else { + attachment.getCacheFile(context) + } val data = if (attachment.hasUsableCache(context, cacheFile)) { cacheFile.inputStream() diff --git a/app/src/main/res/raw/insert_inline_image.js b/app/src/main/res/raw/insert_inline_image.js index c28ded02296..aa694f0670c 100644 --- a/app/src/main/res/raw/insert_inline_image.js +++ b/app/src/main/res/raw/insert_inline_image.js @@ -20,6 +20,10 @@ if (document.activeElement !== document.body) document.body.focus(); } + function getEditor() { + return document.getElementById("editor"); + } + globalThis.insertInlineImage = function(contentId) { if (!contentId) return; @@ -31,28 +35,33 @@ img.style.maxWidth = "100%"; img.style.height = "auto"; + var editor = getEditor(); var selection = window.getSelection(); if (selection && selection.rangeCount > 0) { var range = selection.getRangeAt(0); - range.deleteContents(); + var isInsideEditor = editor.contains(range.commonAncestorContainer); + + if (isInsideEditor) { + range.deleteContents(); - var brBefore = document.createElement("br"); - range.insertNode(brBefore); + var brBefore = document.createElement("br"); + range.insertNode(brBefore); - range.setStartAfter(brBefore); - range.insertNode(img); + range.setStartAfter(brBefore); + range.insertNode(img); - var brAfter = document.createElement("br"); - range.setStartAfter(img); - range.insertNode(brAfter); + var brAfter = document.createElement("br"); + range.setStartAfter(img); + range.insertNode(brAfter); - range.setStartAfter(brAfter); - range.collapse(true); - selection.removeAllRanges(); - selection.addRange(range); - return; + range.setStartAfter(brAfter); + range.collapse(true); + selection.removeAllRanges(); + selection.addRange(range); + return; + } } - document.body.appendChild(img); + editor.appendChild(img); }; })(); From f02128ef6e8a39dd31f7a76c6e2d6064619dfa37 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Fri, 7 Aug 2026 09:33:33 +0200 Subject: [PATCH 12/12] feat: Insert line breaks before and after input field to enable writing --- app/src/main/res/raw/insert_inline_image.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/raw/insert_inline_image.js b/app/src/main/res/raw/insert_inline_image.js index aa694f0670c..1d7151208f8 100644 --- a/app/src/main/res/raw/insert_inline_image.js +++ b/app/src/main/res/raw/insert_inline_image.js @@ -44,10 +44,14 @@ if (isInsideEditor) { range.deleteContents(); - var brBefore = document.createElement("br"); - range.insertNode(brBefore); + var brBefore1 = document.createElement("br"); + var brBefore2 = document.createElement("br"); - range.setStartAfter(brBefore); + range.insertNode(brBefore1); + range.setStartAfter(brBefore1); + range.insertNode(brBefore2); + + range.setStartAfter(brBefore2); range.insertNode(img); var brAfter = document.createElement("br");