Skip to content

Commit 32cbd48

Browse files
Use externalMailInfo API call instead of mailboxes one to get ExternalMail info
1 parent 69fb9a0 commit 32cbd48

File tree

12 files changed

+100
-20
lines changed

12 files changed

+100
-20
lines changed

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
}

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

+4
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
}

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

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,9 @@ 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(emailDictionary: MergedContactDictionary, aliases: List<String>, trustedDomains: List<String>): Boolean {
6868
val isUnknownContact = email !in emailDictionary
6969
val isMailerDaemon = """mailer-daemon@(?:.+\.)?infomaniak\.ch""".toRegex(RegexOption.IGNORE_CASE).matches(email)
70-
val trustedDomains = listOf("@infomaniak.com", "@infomaniak.event", "@swisstransfer.com")
7170
val isUntrustedDomain = email.isEmail() && trustedDomains.none { email.endsWith(it) }
7271
val isAlias = email in aliases
7372

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+
)

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

+12
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ import com.infomaniak.mail.utils.extensions.*
6464
import com.infomaniak.mail.views.itemViews.AvatarMergedContactData
6565
import dagger.hilt.android.lifecycle.HiltViewModel
6666
import io.realm.kotlin.ext.copyFromRealm
67+
import io.realm.kotlin.ext.toRealmList
6768
import io.realm.kotlin.notifications.ResultsChange
6869
import kotlinx.coroutines.*
6970
import kotlinx.coroutines.flow.*
@@ -281,6 +282,7 @@ class MainViewModel @Inject constructor(
281282
updatePermissions(mailbox)
282283
updateSignatures(mailbox)
283284
updateFeatureFlag(mailbox)
285+
updateExternalMailInfo(mailbox)
284286

285287
// This update is blocking because we need it for the rest of the flow : `selectFolder()` needs the Folders.
286288
updateFolders(mailbox)
@@ -343,6 +345,16 @@ class MainViewModel @Inject constructor(
343345
sharedUtils.updateAiFeatureFlag(mailbox.objectId, mailbox.uuid)
344346
}
345347

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

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ class SubjectFormatter @Inject constructor(private val appContext: Context) {
5656
): CharSequence {
5757
if (!externalMailFlagEnabled) return previousContent
5858

59-
val (externalRecipientEmail, externalRecipientQuantity) = thread.findExternalRecipients(emailDictionary, aliases)
59+
val (externalRecipientEmail, externalRecipientQuantity) = thread.findExternalRecipients(
60+
emailDictionary, aliases, trustedDomains,
61+
)
6062
if (externalRecipientQuantity == 0) return previousContent
6163

6264
return postFixWithExternal(previousContent, externalRecipientQuantity, externalRecipientEmail, onExternalClicked)
@@ -135,6 +137,7 @@ class SubjectFormatter @Inject constructor(private val appContext: Context) {
135137
val emailDictionary: MergedContactDictionary,
136138
val aliases: List<String>,
137139
val externalMailFlagEnabled: Boolean,
140+
val trustedDomains: List<String>,
138141
)
139142

140143
data class EllipsizeConfiguration(

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.ThreadViewModel.OpenThreadResult
@@ -415,11 +416,12 @@ class ThreadFragment : Fragment() {
415416
threadViewModel.assembleSubjectData(mainViewModel.mergedContactsLive).observe(viewLifecycleOwner) { result ->
416417

417418
val (subjectWithoutTags, subjectWithTags) = subjectFormatter.generateSubjectContent(
418-
subjectData = SubjectFormatter.SubjectData(
419+
subjectData = SubjectData(
419420
thread = result.thread ?: return@observe,
420421
emailDictionary = result.mergedContacts ?: emptyMap(),
421422
aliases = result.mailbox?.aliases ?: emptyList(),
422423
externalMailFlagEnabled = result.mailbox?.externalMailFlagEnabled ?: false,
424+
trustedDomains = result.mailbox?.trustedDomains ?: emptyList(),
423425
),
424426
) { description ->
425427
informationDialog.show(

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

+21-8
Original file line numberDiff line numberDiff line change
@@ -50,27 +50,40 @@ class NewMessageExternalsManager @Inject constructor() : NewMessageManager() {
5050
fun observeExternals(arrivedFromExistingDraft: Boolean) = with(newMessageViewModel) {
5151
Utils.waitInitMediator(initResult, mergedContacts).observe(viewLifecycleOwner) { (_, mergedContacts) ->
5252
val externalMailFlagEnabled = currentMailbox.externalMailFlagEnabled
53+
val trustedDomains = currentMailbox.trustedDomains
5354
val shouldWarnForExternal = externalMailFlagEnabled && !arrivedFromExistingDraft
5455
val emailDictionary = mergedContacts.second
5556
val aliases = currentMailbox.aliases
5657

57-
updateFields(shouldWarnForExternal, emailDictionary, aliases)
58-
updateBanner(shouldWarnForExternal, emailDictionary, aliases)
58+
updateFields(shouldWarnForExternal, emailDictionary, aliases, trustedDomains)
59+
updateBanner(shouldWarnForExternal, emailDictionary, aliases, trustedDomains)
5960
}
6061
}
6162

62-
private fun updateFields(shouldWarnForExternal: Boolean, emailDictionary: MergedContactDictionary, aliases: List<String>) {
63+
private fun updateFields(
64+
shouldWarnForExternal: Boolean,
65+
emailDictionary: MergedContactDictionary,
66+
aliases: List<String>,
67+
trustedDomains: List<String>,
68+
) {
6369
with(binding) {
64-
toField.updateExternals(shouldWarnForExternal, emailDictionary, aliases)
65-
ccField.updateExternals(shouldWarnForExternal, emailDictionary, aliases)
66-
bccField.updateExternals(shouldWarnForExternal, emailDictionary, aliases)
70+
toField.updateExternals(shouldWarnForExternal, emailDictionary, aliases, trustedDomains)
71+
ccField.updateExternals(shouldWarnForExternal, emailDictionary, aliases, trustedDomains)
72+
bccField.updateExternals(shouldWarnForExternal, emailDictionary, aliases, trustedDomains)
6773
}
6874
}
6975

70-
private fun updateBanner(shouldWarnForExternal: Boolean, emailDictionary: MergedContactDictionary, aliases: List<String>) {
76+
private fun updateBanner(
77+
shouldWarnForExternal: Boolean,
78+
emailDictionary: MergedContactDictionary,
79+
aliases: List<String>,
80+
trustedDomains: List<String>,
81+
) {
7182
with(newMessageViewModel) {
7283
if (shouldWarnForExternal && !isExternalBannerManuallyClosed) {
73-
val (externalEmail, externalQuantity) = draft.findExternalRecipientForNewMessage(aliases, emailDictionary)
84+
val (externalEmail, externalQuantity) = draft.findExternalRecipientForNewMessage(
85+
aliases, emailDictionary, trustedDomains,
86+
)
7487
externalRecipientCount.value = externalEmail to externalQuantity
7588
}
7689
}

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

+7-2
Original file line numberDiff line numberDiff line change
@@ -417,11 +417,16 @@ 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(
421+
shouldWarnForExternal: Boolean,
422+
emailDictionary: MergedContactDictionary,
423+
aliases: List<String>,
424+
trustedDomains: List<String>,
425+
) {
421426
for (recipient in contactChipAdapter.getRecipients()) {
422427
if (recipient.isManuallyEntered) continue
423428

424-
val shouldDisplayAsExternal = shouldWarnForExternal && recipient.isExternal(emailDictionary, aliases)
429+
val shouldDisplayAsExternal = shouldWarnForExternal && recipient.isExternal(emailDictionary, aliases, trustedDomains)
425430
recipient.initDisplayAsExternal(shouldDisplayAsExternal)
426431

427432
updateCollapsedChipValues(isSelfCollapsed)

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

+9-3
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,22 @@ 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
*/
2930
fun Thread.findExternalRecipients(
3031
emailDictionary: MergedContactDictionary,
3132
aliases: List<String>,
33+
trustedDomains: List<String>,
3234
): Pair<String?, Int> {
3335
var externalRecipientEmail: String? = null
3436
var externalRecipientQuantity = 0
3537

3638
messages.forEach { message ->
37-
val (singleEmail, quantityForThisMessage) = findExternalRecipientInIterables(emailDictionary, aliases, message.from)
39+
val (singleEmail, quantityForThisMessage) = findExternalRecipientInIterables(
40+
emailDictionary, aliases, trustedDomains, message.from,
41+
)
3842

3943
externalRecipientQuantity += quantityForThisMessage
4044
if (externalRecipientQuantity > 1) return null to 2
@@ -48,11 +52,12 @@ object ExternalUtils {
4852
fun Draft.findExternalRecipientForNewMessage(
4953
aliases: List<String>,
5054
emailDictionary: MergedContactDictionary,
55+
trustedDomains: List<String>,
5156
): Pair<String?, Int> {
5257
val to = to.onlyAutomaticallyAddedOnes()
5358
val cc = cc.onlyAutomaticallyAddedOnes()
5459
val bcc = bcc.onlyAutomaticallyAddedOnes()
55-
return findExternalRecipientInIterables(emailDictionary, aliases, to, cc, bcc)
60+
return findExternalRecipientInIterables(emailDictionary, aliases, trustedDomains, to, cc, bcc)
5661
}
5762

5863
private fun List<Recipient>.onlyAutomaticallyAddedOnes(): List<Recipient> = filter { !it.isManuallyEntered }
@@ -63,14 +68,15 @@ object ExternalUtils {
6368
private fun findExternalRecipientInIterables(
6469
emailDictionary: MergedContactDictionary,
6570
aliases: List<String>,
71+
trustedDomains: List<String>,
6672
vararg recipientLists: Iterable<Recipient>,
6773
): Pair<String?, Int> {
6874
var externalRecipientEmail: String? = null
6975
var externalRecipientQuantity = 0
7076

7177
recipientLists.forEach { recipientList ->
7278
recipientList.forEach { recipient ->
73-
if (recipient.isExternal(emailDictionary, aliases)) {
79+
if (recipient.isExternal(emailDictionary, aliases, trustedDomains)) {
7480
if (externalRecipientQuantity++ == 0) {
7581
externalRecipientEmail = recipient.email
7682
} else {

0 commit comments

Comments
 (0)