Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ class Draft : RealmObject {
delay = null
field = value
}

@SerialName("reminder")
var reminder: ReminderDraftInfo? = null
//endregion

//region Local data (Transient)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
@file:UseSerializers(RealmInstantSerializer::class)

package com.infomaniak.mail.data.models.draft

import com.infomaniak.mail.data.api.RealmInstantSerializer
import io.realm.kotlin.types.EmbeddedRealmObject
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers

@Serializable
class ReminderDraftInfo : EmbeddedRealmObject {
@SerialName("delta")
var reminderDelta: Int? = null

@SerialName("display")
var shouldRemindRecipient: Boolean? = null

companion object
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import com.infomaniak.mail.data.models.calendar.CalendarEventResponse
import com.infomaniak.mail.data.models.correspondent.MergedContact
import com.infomaniak.mail.data.models.correspondent.Recipient
import com.infomaniak.mail.data.models.draft.Draft
import com.infomaniak.mail.data.models.draft.ReminderDraftInfo
import com.infomaniak.mail.data.models.mailbox.Mailbox
import com.infomaniak.mail.data.models.mailbox.MailboxPermissions
import com.infomaniak.mail.data.models.mailbox.SenderDetails
Expand Down Expand Up @@ -204,7 +205,7 @@ object RealmDatabase {
//region Configurations versions
const val USER_INFO_SCHEMA_VERSION = 5L
const val MAILBOX_INFO_SCHEMA_VERSION = 19L
const val MAILBOX_CONTENT_SCHEMA_VERSION = 39L
const val MAILBOX_CONTENT_SCHEMA_VERSION = 40L
//endregion

//region Configurations names
Expand Down Expand Up @@ -236,6 +237,7 @@ object RealmDatabase {
Folder::class,
Thread::class,
Message::class,
ReminderDraftInfo::class,
Headers::class,
Draft::class,
Recipient::class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import com.infomaniak.mail.data.models.correspondent.Recipient
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.draft.ReminderDraftInfo
import com.infomaniak.mail.data.models.extensions.action
import com.infomaniak.mail.data.models.extensions.createValidRecipientOrNull
import com.infomaniak.mail.data.models.extensions.getDefaultSignatureWithFallback
Expand Down Expand Up @@ -149,7 +150,9 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jsoup.nodes.Document
import splitties.experimental.ExperimentalSplittiesApi
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import javax.inject.Inject

@OptIn(ExperimentalCoroutinesApi::class)
Expand Down Expand Up @@ -586,6 +589,9 @@ class NewMessageViewModel @Inject constructor(
isEncrypted = isEncrypted,
encryptionPassword = encryptionKey ?: "",
attachmentsLocalUuids = attachments.mapTo(mutableSetOf()) { it.localUuid },
scheduleDate = scheduleDate,
reminderDelta = reminder?.reminderDelta,
shouldRemindRecipient = reminder?.shouldRemindRecipient ?: true,
)
}

Expand Down Expand Up @@ -621,6 +627,22 @@ class NewMessageViewModel @Inject constructor(
}

isEncryptionActivated.postValue(isEncrypted)

scheduleDate?.let { dateString ->
SimpleDateFormat(FORMAT_ISO_8601_WITH_TIMEZONE_SEPARATOR, Locale.getDefault())
.parse(dateString)
?.time
?.let { epoch ->
setScheduleConfig(ScheduleConfig.Scheduled(epoch))
}
}

reminder?.reminderDelta?.let { minutes ->
val isCustom = !ReminderPreset.entries.any { preset -> preset.delayMinutes == minutes }
setReminderConfig(ReminderConfig.Delayed(minutes, isCustom = isCustom))
}

reminder?.shouldRemindRecipient?.let { setShouldRemindRecipient(it) }
}

fun setQuotesButtonVisibility(isVisible: Boolean) {
Expand Down Expand Up @@ -932,17 +954,6 @@ class NewMessageViewModel @Inject constructor(
}.cancellable().onFailure(Sentry::captureException)
}

fun setScheduleDate(date: Date?) = viewModelScope.launch(ioDispatcher) {
val localUuid = draftLocalUuid ?: return@launch
mailboxContentRealm().write {
DraftController.getDraftBlocking(localUuid, realm = this)?.also { draft ->
draft.scheduleDate = date?.format(FORMAT_ISO_8601_WITH_TIMEZONE_SEPARATOR)
}
}
}

fun resetScheduledDate() = setScheduleDate(date = null)

fun storeBodyAndSubject(subject: String, html: String) {
globalCoroutineScope.launch(ioDispatcher) {
_subjectAndBodyChannel.send(SubjectAndBodyData(subject, html, channelExpirationIdTarget))
Expand Down Expand Up @@ -1033,10 +1044,22 @@ class NewMessageViewModel @Inject constructor(
cc = ccLiveData.valueOrEmpty().toRealmList()
bcc = bccLiveData.valueOrEmpty().toRealmList()

if (draftAction == DraftAction.SEND) delay = localSettings.cancelDelay

ackRequest = localSettings.askEmailAcknowledgement

scheduleDate = getCurrentScheduleDate()
Comment thread
Elouan1411 marked this conversation as resolved.

if (draftAction == DraftAction.SEND && scheduleDate == null) delay = localSettings.cancelDelay

val currentReminderDelta = getCurrentReminderDelta()
reminder = if (currentReminderDelta != null) {
ReminderDraftInfo().apply {
reminderDelta = currentReminderDelta
shouldRemindRecipient = this@NewMessageViewModel.shouldRemindRecipient.value ?: true
}
} else {
null
}

updateDraftAttachmentsWithLiveData(
uiAttachments = attachmentsLiveData.valueOrEmpty(),
step = "executeDraftActionWhenStopping (action = ${draftAction.name}) -> updateDraftFromLiveData",
Expand Down Expand Up @@ -1145,6 +1168,16 @@ class NewMessageViewModel @Inject constructor(
SentryDebug.addDraftBreadcrumbs(draft = this, step)
}

private fun getCurrentScheduleDate(): String? {
return (scheduleConfig.value as? ScheduleConfig.Scheduled)?.let { schedule ->
Date(schedule.epochMillis).format(FORMAT_ISO_8601_WITH_TIMEZONE_SEPARATOR)
}
}

private fun getCurrentReminderDelta(): Int? {
return (reminderConfig.value as? ReminderConfig.Delayed)?.delayMinutes
}

private fun isSnapshotTheSame(subjectValue: String?, uiBodyValue: String): Boolean {
return snapshot?.let { draftSnapshot ->
draftSnapshot.identityId == fromLiveData.value?.signature?.id?.toString() &&
Expand All @@ -1156,7 +1189,10 @@ class NewMessageViewModel @Inject constructor(
draftSnapshot.isEncrypted == isEncryptionActivated.value &&
draftSnapshot.encryptionPassword == encryptionPassword.value &&
draftSnapshot.attachmentsLocalUuids == attachmentsLiveData.valueOrEmpty()
.mapTo(mutableSetOf()) { it.localUuid }
.mapTo(mutableSetOf()) { it.localUuid } &&
draftSnapshot.scheduleDate == getCurrentScheduleDate() &&
draftSnapshot.reminderDelta == getCurrentReminderDelta() &&
draftSnapshot.shouldRemindRecipient == shouldRemindRecipient.value
} ?: false
}

Expand Down Expand Up @@ -1285,6 +1321,9 @@ class NewMessageViewModel @Inject constructor(
var isEncrypted: Boolean,
var encryptionPassword: String,
val attachmentsLocalUuids: Set<String>,
val scheduleDate: String?,
val reminderDelta: Int?,
val shouldRemindRecipient: Boolean?,
)

private data class SubjectAndBodyData(val subject: String, val body: String, val expirationId: Int)
Expand Down
Loading