Skip to content

Commit 85c5559

Browse files
Merge pull request #1656 from Infomaniak/externals-api-call
Use `externalMailInfo` API call instead of `mailboxes` one to get ExternalMail info
2 parents d28bf0c + c5abb57 commit 85c5559

File tree

12 files changed

+104
-41
lines changed

12 files changed

+104
-41
lines changed

Diff for: app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt

+5
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import com.infomaniak.mail.data.models.getMessages.ActivitiesResult
4949
import com.infomaniak.mail.data.models.getMessages.GetMessagesByUidsResult
5050
import com.infomaniak.mail.data.models.getMessages.NewMessagesResult
5151
import com.infomaniak.mail.data.models.mailbox.Mailbox
52+
import com.infomaniak.mail.data.models.mailbox.MailboxExternalMailInfo
5253
import com.infomaniak.mail.data.models.mailbox.MailboxLinkedResult
5354
import com.infomaniak.mail.data.models.mailbox.MailboxPermissions
5455
import com.infomaniak.mail.data.models.message.Message
@@ -148,6 +149,10 @@ object ApiRepository : ApiRepositoryCore() {
148149
return callApi(ApiRoutes.permissions(mailboxLinkId, mailboxHostingId), GET)
149150
}
150151

152+
fun getExternalMailInfo(mailboxHostingId: Int, mailboxName: String): ApiResponse<MailboxExternalMailInfo> {
153+
return callApi(ApiRoutes.externalMailInfo(mailboxHostingId, mailboxName), GET)
154+
}
155+
151156
fun markMessagesAsSeen(mailboxUuid: String, messagesUids: List<String>): ApiResponse<Unit> {
152157
return callApi(ApiRoutes.messagesSeen(mailboxUuid), POST, mapOf("uids" to messagesUids))
153158
}

Diff for: app/src/main/java/com/infomaniak/mail/data/api/ApiRoutes.kt

+5-1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ object ApiRoutes {
8787
return "$MAIL_API/api/securedProxy"
8888
}
8989

90+
fun externalMailInfo(mailboxHostingId: Int, mailboxName: String): String {
91+
return "${securedProxy()}/1/mail_hostings/$mailboxHostingId/mailboxes/$mailboxName/external_mail_flag"
92+
}
93+
9094
fun updateMailboxPassword(mailboxId: Int): String {
9195
return "${securedProxy()}/cache/invalidation/profile/workspace/mailbox/$mailboxId/update_password"
9296
}
@@ -106,7 +110,7 @@ object ApiRoutes {
106110
}
107111

108112
fun mailboxes(): String {
109-
return "${mailbox()}?with=aliases,external_mail_flag_enabled,unseen"
113+
return "${mailbox()}?with=aliases,unseen"
110114
}
111115

112116
fun permissions(linkId: Int, mailboxHostingId: Int): String {

Diff for: app/src/main/java/com/infomaniak/mail/data/cache/RealmDatabase.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ object RealmDatabase {
161161

162162
//region Configurations versions
163163
const val USER_INFO_SCHEMA_VERSION = 1L
164-
const val MAILBOX_INFO_SCHEMA_VERSION = 4L
164+
const val MAILBOX_INFO_SCHEMA_VERSION = 5L
165165
const val MAILBOX_CONTENT_SCHEMA_VERSION = 10L
166166
//endregion
167167

Diff for: app/src/main/java/com/infomaniak/mail/data/models/correspondent/Recipient.kt

+5-6
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
package com.infomaniak.mail.data.models.correspondent
1919

2020
import android.os.Parcel
21-
import com.infomaniak.mail.utils.extensions.MergedContactDictionary
21+
import com.infomaniak.mail.utils.ExternalUtils.ExternalData
2222
import com.infomaniak.mail.utils.extensions.isEmail
2323
import io.realm.kotlin.types.EmbeddedRealmObject
2424
import io.realm.kotlin.types.annotations.Ignore
@@ -64,14 +64,13 @@ open class Recipient : EmbeddedRealmObject, Correspondent {
6464

6565
// Computes if the Recipient is external, according to the required conditions.
6666
// Does not tell anything about how to display the Recipient chip when composing a new Message.
67-
fun isExternal(emailDictionary: MergedContactDictionary, aliases: List<String>): Boolean {
67+
fun isExternal(externalData: ExternalData): Boolean = with(externalData) {
6868
val isUnknownContact = email !in emailDictionary
69-
val isMailerDaemon = """mailer-daemon@(?:.+\.)?infomaniak\.ch""".toRegex(RegexOption.IGNORE_CASE).matches(email)
70-
val trustedDomains = listOf("@infomaniak.com", "@infomaniak.event", "@swisstransfer.com")
71-
val isUntrustedDomain = email.isEmail() && trustedDomains.none { email.endsWith(it) }
7269
val isAlias = email in aliases
70+
val isUntrustedDomain = email.isEmail() && trustedDomains.none(email::endsWith)
71+
val isMailerDaemon = """mailer-daemon@(?:.+\.)?infomaniak\.ch""".toRegex(RegexOption.IGNORE_CASE).matches(email)
7372

74-
return isUnknownContact && !isMailerDaemon && isUntrustedDomain && !isAlias
73+
return@with isUnknownContact && !isAlias && isUntrustedDomain && !isMailerDaemon
7574
}
7675

7776
fun quotedDisplayName(): String = "${("$name ").ifBlank { "" }}<$email>"

Diff for: app/src/main/java/com/infomaniak/mail/data/models/mailbox/Mailbox.kt

+4-2
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,6 @@ class Mailbox : RealmObject {
6262
@SerialName("unseen_messages")
6363
var unreadCountRemote: Int = 0
6464
var aliases: RealmList<String> = realmListOf()
65-
@SerialName("external_mail_flag_enabled")
66-
var externalMailFlagEnabled: Boolean = false
6765
//endregion
6866

6967
//region Local data (Transient)
@@ -80,6 +78,10 @@ class Mailbox : RealmObject {
8078
var permissions: MailboxPermissions? = null
8179
@Transient
8280
private var _featureFlags: RealmSet<String> = realmSetOf()
81+
@Transient
82+
var externalMailFlagEnabled: Boolean = false
83+
@Transient
84+
var trustedDomains: RealmList<String> = realmListOf()
8385
//endregion
8486

8587
//region UI data (Transient & Ignore)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Infomaniak Mail - Android
3+
* Copyright (C) 2024 Infomaniak Network SA
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
package com.infomaniak.mail.data.models.mailbox
19+
20+
import kotlinx.serialization.SerialName
21+
import kotlinx.serialization.Serializable
22+
23+
@Serializable
24+
data class MailboxExternalMailInfo(
25+
@SerialName("external_mail_flag_enabled")
26+
val externalMailFlagEnabled: Boolean = false,
27+
@SerialName("domains")
28+
val trustedDomains: List<String> = emptyList(),
29+
)

Diff for: app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt

+15
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import com.infomaniak.mail.utils.extensions.*
6363
import com.infomaniak.mail.views.itemViews.AvatarMergedContactData
6464
import dagger.hilt.android.lifecycle.HiltViewModel
6565
import io.realm.kotlin.ext.copyFromRealm
66+
import io.realm.kotlin.ext.toRealmList
6667
import io.realm.kotlin.notifications.ResultsChange
6768
import kotlinx.coroutines.*
6869
import kotlinx.coroutines.flow.*
@@ -279,6 +280,7 @@ class MainViewModel @Inject constructor(
279280
updatePermissions(mailbox)
280281
updateSignatures(mailbox)
281282
updateFeatureFlag(mailbox)
283+
updateExternalMailInfo(mailbox)
282284

283285
// This update is blocking because we need it for the rest of the flow : `selectFolder()` needs the Folders.
284286
updateFolders(mailbox)
@@ -341,6 +343,19 @@ class MainViewModel @Inject constructor(
341343
sharedUtils.updateAiFeatureFlag(mailbox.objectId, mailbox.uuid)
342344
}
343345

346+
private fun updateExternalMailInfo(mailbox: Mailbox) = viewModelScope.launch(ioCoroutineContext) {
347+
SentryLog.d(TAG, "Force refresh External Mail info")
348+
with(ApiRepository.getExternalMailInfo(mailbox.hostingId, mailbox.mailboxName)) {
349+
if (!isSuccess()) return@launch
350+
data?.let { externalMailInfo ->
351+
mailboxController.updateMailbox(mailbox.objectId) {
352+
it.externalMailFlagEnabled = externalMailInfo.externalMailFlagEnabled
353+
it.trustedDomains = externalMailInfo.trustedDomains.toRealmList()
354+
}
355+
}
356+
}
357+
}
358+
344359
private fun updateFolders(mailbox: Mailbox) {
345360
SentryLog.d(TAG, "Force refresh Folders")
346361
ApiRepository.getFolders(mailbox.uuid).data?.let { folders ->

Diff for: app/src/main/java/com/infomaniak/mail/ui/main/thread/SubjectFormatter.kt

+5-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import androidx.core.content.res.ResourcesCompat
2727
import com.infomaniak.mail.MatomoMail.trackExternalEvent
2828
import com.infomaniak.mail.R
2929
import com.infomaniak.mail.data.models.thread.Thread
30+
import com.infomaniak.mail.utils.ExternalUtils.ExternalData
3031
import com.infomaniak.mail.utils.ExternalUtils.findExternalRecipients
3132
import com.infomaniak.mail.utils.Utils
3233
import com.infomaniak.mail.utils.extensions.MergedContactDictionary
@@ -56,7 +57,9 @@ class SubjectFormatter @Inject constructor(private val appContext: Context) {
5657
): CharSequence {
5758
if (!externalMailFlagEnabled) return previousContent
5859

59-
val (externalRecipientEmail, externalRecipientQuantity) = thread.findExternalRecipients(emailDictionary, aliases)
60+
val (externalRecipientEmail, externalRecipientQuantity) = thread.findExternalRecipients(
61+
externalData = ExternalData(emailDictionary, aliases, trustedDomains),
62+
)
6063
if (externalRecipientQuantity == 0) return previousContent
6164

6265
return postFixWithExternal(previousContent, externalRecipientQuantity, externalRecipientEmail, onExternalClicked)
@@ -135,6 +138,7 @@ class SubjectFormatter @Inject constructor(private val appContext: Context) {
135138
val emailDictionary: MergedContactDictionary,
136139
val aliases: List<String>,
137140
val externalMailFlagEnabled: Boolean,
141+
val trustedDomains: List<String>,
138142
)
139143

140144
data class EllipsizeConfiguration(

Diff for: app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadFragment.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ import com.infomaniak.mail.ui.main.SnackbarManager
6262
import com.infomaniak.mail.ui.main.folder.TwoPaneFragment
6363
import com.infomaniak.mail.ui.main.folder.TwoPaneViewModel
6464
import com.infomaniak.mail.ui.main.folder.TwoPaneViewModel.NavData
65+
import com.infomaniak.mail.ui.main.thread.SubjectFormatter.SubjectData
6566
import com.infomaniak.mail.ui.main.thread.ThreadAdapter.ContextMenuType
6667
import com.infomaniak.mail.ui.main.thread.ThreadAdapter.ThreadAdapterCallbacks
6768
import com.infomaniak.mail.ui.main.thread.actions.AttachmentActionsBottomSheetDialogArgs
@@ -424,11 +425,12 @@ class ThreadFragment : Fragment() {
424425
threadViewModel.assembleSubjectData(mainViewModel.mergedContactsLive).observe(viewLifecycleOwner) { result ->
425426

426427
val (subjectWithoutTags, subjectWithTags) = subjectFormatter.generateSubjectContent(
427-
subjectData = SubjectFormatter.SubjectData(
428+
subjectData = SubjectData(
428429
thread = result.thread ?: return@observe,
429430
emailDictionary = result.mergedContacts ?: emptyMap(),
430431
aliases = result.mailbox?.aliases ?: emptyList(),
431432
externalMailFlagEnabled = result.mailbox?.externalMailFlagEnabled ?: false,
433+
trustedDomains = result.mailbox?.trustedDomains ?: emptyList(),
432434
),
433435
) { description ->
434436
informationDialog.show(

Diff for: app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageExternalsManager.kt

+15-13
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ import com.infomaniak.mail.MatomoMail.trackExternalEvent
2222
import com.infomaniak.mail.R
2323
import com.infomaniak.mail.databinding.FragmentNewMessageBinding
2424
import com.infomaniak.mail.ui.alertDialogs.InformationAlertDialog
25+
import com.infomaniak.mail.utils.ExternalUtils.ExternalData
2526
import com.infomaniak.mail.utils.ExternalUtils.findExternalRecipientForNewMessage
2627
import com.infomaniak.mail.utils.Utils
27-
import com.infomaniak.mail.utils.extensions.MergedContactDictionary
2828
import dagger.hilt.android.scopes.FragmentScoped
2929
import javax.inject.Inject
3030

@@ -49,28 +49,30 @@ class NewMessageExternalsManager @Inject constructor() : NewMessageManager() {
4949

5050
fun observeExternals(arrivedFromExistingDraft: Boolean) = with(newMessageViewModel) {
5151
Utils.waitInitMediator(initResult, mergedContacts).observe(viewLifecycleOwner) { (_, mergedContacts) ->
52-
val externalMailFlagEnabled = currentMailbox.externalMailFlagEnabled
53-
val shouldWarnForExternal = externalMailFlagEnabled && !arrivedFromExistingDraft
54-
val emailDictionary = mergedContacts.second
55-
val aliases = currentMailbox.aliases
52+
val shouldWarnForExternal = currentMailbox.externalMailFlagEnabled && !arrivedFromExistingDraft
53+
val externalData = ExternalData(
54+
emailDictionary = mergedContacts.second,
55+
aliases = currentMailbox.aliases,
56+
trustedDomains = currentMailbox.trustedDomains,
57+
)
5658

57-
updateFields(shouldWarnForExternal, emailDictionary, aliases)
58-
updateBanner(shouldWarnForExternal, emailDictionary, aliases)
59+
updateFields(shouldWarnForExternal, externalData)
60+
updateBanner(shouldWarnForExternal, externalData)
5961
}
6062
}
6163

62-
private fun updateFields(shouldWarnForExternal: Boolean, emailDictionary: MergedContactDictionary, aliases: List<String>) {
64+
private fun updateFields(shouldWarnForExternal: Boolean, externalData: ExternalData) {
6365
with(binding) {
64-
toField.updateExternals(shouldWarnForExternal, emailDictionary, aliases)
65-
ccField.updateExternals(shouldWarnForExternal, emailDictionary, aliases)
66-
bccField.updateExternals(shouldWarnForExternal, emailDictionary, aliases)
66+
toField.updateExternals(shouldWarnForExternal, externalData)
67+
ccField.updateExternals(shouldWarnForExternal, externalData)
68+
bccField.updateExternals(shouldWarnForExternal, externalData)
6769
}
6870
}
6971

70-
private fun updateBanner(shouldWarnForExternal: Boolean, emailDictionary: MergedContactDictionary, aliases: List<String>) {
72+
private fun updateBanner(shouldWarnForExternal: Boolean, externalData: ExternalData) {
7173
with(newMessageViewModel) {
7274
if (shouldWarnForExternal && !isExternalBannerManuallyClosed) {
73-
val (externalEmail, externalQuantity) = draft.findExternalRecipientForNewMessage(aliases, emailDictionary)
75+
val (externalEmail, externalQuantity) = draft.findExternalRecipientForNewMessage(externalData)
7476
externalRecipientCount.value = externalEmail to externalQuantity
7577
}
7678
}

Diff for: app/src/main/java/com/infomaniak/mail/ui/newMessage/RecipientFieldView.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ import com.infomaniak.mail.data.models.correspondent.Recipient
4343
import com.infomaniak.mail.databinding.ViewContactChipContextMenuBinding
4444
import com.infomaniak.mail.databinding.ViewRecipientFieldBinding
4545
import com.infomaniak.mail.ui.main.SnackbarManager
46+
import com.infomaniak.mail.utils.ExternalUtils.ExternalData
4647
import com.infomaniak.mail.utils.UiUtils
47-
import com.infomaniak.mail.utils.extensions.MergedContactDictionary
4848
import com.infomaniak.mail.utils.extensions.isEmail
4949
import com.infomaniak.mail.utils.extensions.toggleChevron
5050
import dagger.hilt.android.AndroidEntryPoint
@@ -417,11 +417,11 @@ class RecipientFieldView @JvmOverloads constructor(
417417
return (if (recipientCount == 1) recipients.single().email else null) to recipientCount
418418
}
419419

420-
fun updateExternals(shouldWarnForExternal: Boolean, emailDictionary: MergedContactDictionary, aliases: List<String>) {
420+
fun updateExternals(shouldWarnForExternal: Boolean, externalData: ExternalData) {
421421
for (recipient in contactChipAdapter.getRecipients()) {
422422
if (recipient.isManuallyEntered) continue
423423

424-
val shouldDisplayAsExternal = shouldWarnForExternal && recipient.isExternal(emailDictionary, aliases)
424+
val shouldDisplayAsExternal = shouldWarnForExternal && recipient.isExternal(externalData)
425425
recipient.initDisplayAsExternal(shouldDisplayAsExternal)
426426

427427
updateCollapsedChipValues(isSelfCollapsed)

Diff for: app/src/main/java/com/infomaniak/mail/utils/ExternalUtils.kt

+14-13
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,16 @@ import com.infomaniak.mail.data.models.thread.Thread
2323
import com.infomaniak.mail.utils.extensions.MergedContactDictionary
2424

2525
object ExternalUtils {
26+
2627
/**
2728
* Only returns a quantity of at most 2, used to differentiate between the singular or plural form of the dialog messages
2829
*/
29-
fun Thread.findExternalRecipients(
30-
emailDictionary: MergedContactDictionary,
31-
aliases: List<String>,
32-
): Pair<String?, Int> {
30+
fun Thread.findExternalRecipients(externalData: ExternalData): Pair<String?, Int> {
3331
var externalRecipientEmail: String? = null
3432
var externalRecipientQuantity = 0
3533

3634
messages.forEach { message ->
37-
val (singleEmail, quantityForThisMessage) = findExternalRecipientInIterables(emailDictionary, aliases, message.from)
35+
val (singleEmail, quantityForThisMessage) = findExternalRecipientInIterables(externalData, message.from)
3836

3937
externalRecipientQuantity += quantityForThisMessage
4038
if (externalRecipientQuantity > 1) return null to 2
@@ -45,14 +43,12 @@ object ExternalUtils {
4543
return externalRecipientEmail to externalRecipientQuantity
4644
}
4745

48-
fun Draft.findExternalRecipientForNewMessage(
49-
aliases: List<String>,
50-
emailDictionary: MergedContactDictionary,
51-
): Pair<String?, Int> {
46+
fun Draft.findExternalRecipientForNewMessage(externalData: ExternalData): Pair<String?, Int> {
5247
val to = to.onlyAutomaticallyAddedOnes()
5348
val cc = cc.onlyAutomaticallyAddedOnes()
5449
val bcc = bcc.onlyAutomaticallyAddedOnes()
55-
return findExternalRecipientInIterables(emailDictionary, aliases, to, cc, bcc)
50+
51+
return findExternalRecipientInIterables(externalData, to, cc, bcc)
5652
}
5753

5854
private fun List<Recipient>.onlyAutomaticallyAddedOnes(): List<Recipient> = filter { !it.isManuallyEntered }
@@ -61,16 +57,15 @@ object ExternalUtils {
6157
* Only returns a quantity of at most 2, used to differentiate between the singular or plural form of the dialog messages
6258
*/
6359
private fun findExternalRecipientInIterables(
64-
emailDictionary: MergedContactDictionary,
65-
aliases: List<String>,
60+
externalData: ExternalData,
6661
vararg recipientLists: Iterable<Recipient>,
6762
): Pair<String?, Int> {
6863
var externalRecipientEmail: String? = null
6964
var externalRecipientQuantity = 0
7065

7166
recipientLists.forEach { recipientList ->
7267
recipientList.forEach { recipient ->
73-
if (recipient.isExternal(emailDictionary, aliases)) {
68+
if (recipient.isExternal(externalData)) {
7469
if (externalRecipientQuantity++ == 0) {
7570
externalRecipientEmail = recipient.email
7671
} else {
@@ -82,4 +77,10 @@ object ExternalUtils {
8277

8378
return externalRecipientEmail to externalRecipientQuantity
8479
}
80+
81+
data class ExternalData(
82+
val emailDictionary: MergedContactDictionary,
83+
val aliases: List<String>,
84+
val trustedDomains: List<String>,
85+
)
8586
}

0 commit comments

Comments
 (0)