feat: Emails requiring a response#2968
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces “send options” UI and supporting plumbing for scheduling and the new “reminder / follow-up” email flow, including new bottom sheets/dialogs, new draft actions, and new thread/message UI affordances (icons + alert banners) to surface reminders that require a response.
Changes:
- Added a new Send Options screen/bottom sheet for configuring scheduled sending and “remind me if no response” (including custom delay + visibility selection).
- Introduced reminder/follow-up draft actions and UI (thread list reminder icon, thread message reminder banners, follow-up draft mode).
- Refactored schedule picker bottom sheets and extracted KSuite trailing chip handling for reuse.
Reviewed changes
Copilot reviewed 65 out of 65 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| OldKotlin/realm-models/src/main/kotlin/com/infomaniak/mail/data/models/thread/Thread.kt | Adds persisted isReminder flag on Realm Thread. |
| OldKotlin/realm-models/src/main/kotlin/com/infomaniak/mail/data/models/draft/Draft.kt | Adds FOLLOW_UP draft mode. |
| app/src/main/res/values/strings.xml | Adds reminder/send-options strings + updates encryption conflict copy. |
| app/src/main/res/values/dimens.xml | Adds emptyStatePadding for new settings-style layouts. |
| app/src/main/res/values/attrs.xml | Adds iconColor attribute to ItemSettingView. |
| app/src/main/res/values-sv/strings.xml | Swedish translations for new reminder/send-options strings. |
| app/src/main/res/values-pt/strings.xml | Portuguese translations for new reminder/send-options strings. |
| app/src/main/res/values-pl/strings.xml | Polish translations for new reminder/send-options strings. |
| app/src/main/res/values-nl/strings.xml | Dutch translations for new reminder/send-options strings. |
| app/src/main/res/values-nb/strings.xml | Norwegian Bokmål translations for new reminder/send-options strings. |
| app/src/main/res/values-it/strings.xml | Italian translations for new reminder/send-options strings. |
| app/src/main/res/values-fr/strings.xml | French translations for new reminder/send-options strings. |
| app/src/main/res/values-fi/strings.xml | Finnish translations for new reminder/send-options strings. |
| app/src/main/res/values-es/strings.xml | Spanish translations for new reminder/send-options strings. |
| app/src/main/res/values-el/strings.xml | Greek translations for new reminder/send-options strings. |
| app/src/main/res/values-de/strings.xml | German translations for new reminder/send-options strings. |
| app/src/main/res/values-da/strings.xml | Danish translations for new reminder/send-options strings. |
| app/src/main/res/navigation/new_message_navigation.xml | Replaces schedule dialog destination with Send Options fragment destination. |
| app/src/main/res/navigation/main_navigation.xml | Renames schedule bottom sheet + adds reminder bottom sheet destination. |
| app/src/main/res/layout/view_setting_radio_button.xml | Adds optional description text under radio option labels. |
| app/src/main/res/layout/view_item_setting.xml | Adds checkmark + trailing chip container + icon tinting support. |
| app/src/main/res/layout/item_message.xml | Adds reminder alert containers/banners inside message item UI. |
| app/src/main/res/layout/item_bottom_sheet_action.xml | Adjusts padding/margins for bottom-sheet action rows. |
| app/src/main/res/layout/fragment_send_options.xml | New Send Options screen layout (schedule + reminder sections). |
| app/src/main/res/layout/fragment_new_message.xml | Adds schedule/reminder alert banners + replaces schedule button with send-options button. |
| app/src/main/res/layout/dialog_select_visibility_reminder.xml | New dialog layout for reminder visibility selection. |
| app/src/main/res/layout/dialog_custom_reminder_picker.xml | New dialog layout for custom reminder delay pickers. |
| app/src/main/res/layout/cardview_thread_item.xml | Adds reminder icon to thread list item layout. |
| app/src/main/res/layout/bottom_sheet_schedule_options.xml | Updates tools context to renamed reschedule dialog. |
| app/src/main/res/layout/bottom_sheet_reminder_options.xml | New reminder bottom sheet layout. |
| app/src/main/res/drawable/bg_reminder_icon.xml | New background shape for reminder icon. |
| app/src/main/java/com/infomaniak/mail/workers/MailActionsManager.kt | Adds handling for DraftAction.REMINDER via existing draft upload path. |
| app/src/main/java/com/infomaniak/mail/workers/DraftsActionsWorker.kt | Adds reminder result key constant. |
| app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt | Adds helper to apply start padding to first child of a ViewGroup. |
| app/src/main/java/com/infomaniak/mail/utils/DraftInitManager.kt | Adds initialization path for follow-up drafts (attachments + recipients). |
| app/src/main/java/com/infomaniak/mail/utils/date/DateFormatUtils.kt | Adds helper to format reminder delay text as hours/days. |
| app/src/main/java/com/infomaniak/mail/ui/newMessage/SendOptionsConfig.kt | New config types/constants for schedule/reminder options. |
| app/src/main/java/com/infomaniak/mail/ui/newMessage/sendOptions/DraftSendOptionsFragment.kt | New fragment implementing Send Options UI + persistence into ViewModel. |
| app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt | Adds LiveData to store schedule/reminder config + follow-up draft content + reminder delay persistence. |
| app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt | Integrates Send Options, renders schedule/reminder banners, and sends drafts as schedule/reminder. |
| app/src/main/java/com/infomaniak/mail/ui/newMessage/encryption/EncryptionMessageManager.kt | Adds encryption conflict check with schedule/reminder options. |
| app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt | Adds disabledReminder() placeholder invoked from snackbar action. |
| app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt | Adds reminder result snackbar wiring. |
| app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadFragment.kt | Adds reminder bottom sheet navigation + follow-up draft navigation. |
| app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt | Adds reminder banners + callbacks for reminder/follow-up actions. |
| app/src/main/java/com/infomaniak/mail/ui/main/thread/MessageAlertView.kt | Tweaks padding based on action visibility. |
| app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt | Cleans unused import after trailing content refactor. |
| app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/KSuiteChipManager.kt | New helper to manage KSuite chip views in View-based UIs. |
| app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ActionItemView.kt | Uses KSuiteChipManager + adds convenience title/icon APIs. |
| app/src/main/java/com/infomaniak/mail/ui/main/settings/SettingRadioGroupView.kt | Adds clearCheck() API to uncheck programmatically. |
| app/src/main/java/com/infomaniak/mail/ui/main/settings/SettingRadioButtonView.kt | Moves click wiring to root + adds description support. |
| app/src/main/java/com/infomaniak/mail/ui/main/settings/ItemSettingView.kt | Adds icon tinting + trailing content support + checkmark/subtitle helpers. |
| app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt | Shows reminder icon for threads flagged as reminder and unread. |
| app/src/main/java/com/infomaniak/mail/ui/bottomSheetDialogs/SnoozeBottomSheetDialog.kt | Switches to new shared schedule picker base. |
| app/src/main/java/com/infomaniak/mail/ui/bottomSheetDialogs/SimpleSchedulePickerBottomSheet.kt | New base for schedule picker bottom sheets using BaseSchedulePickerBottomSheet. |
| app/src/main/java/com/infomaniak/mail/ui/bottomSheetDialogs/RescheduleDraftBottomSheetDialog.kt | Renames/updates schedule bottom sheet to new base implementation. |
| app/src/main/java/com/infomaniak/mail/ui/bottomSheetDialogs/ReminderBottomSheetDialog.kt | New reminder picker bottom sheet (preset + custom). |
| app/src/main/java/com/infomaniak/mail/ui/bottomSheetDialogs/BaseSchedulePickerBottomSheet.kt | Refactors schedule picker to be reusable by multiple UIs. |
| app/src/main/java/com/infomaniak/mail/ui/alertDialogs/SelectVisibilityReminderDialog.kt | New dialog for reminder visibility selection. |
| app/src/main/java/com/infomaniak/mail/ui/alertDialogs/SelectDateAndTimeForSnoozeDialog.kt | Updates constant import after moving MIN_SELECTABLE_DATE_MINUTES. |
| app/src/main/java/com/infomaniak/mail/ui/alertDialogs/SelectDateAndTimeForScheduledDraftDialog.kt | Updates constant import after moving MIN_SELECTABLE_DATE_MINUTES. |
| app/src/main/java/com/infomaniak/mail/ui/alertDialogs/SelectDateAndTimeDialog.kt | Moves MIN_SELECTABLE_DATE_MINUTES out and adds ONE_HOUR_IN_MILLIS constant. |
| app/src/main/java/com/infomaniak/mail/ui/alertDialogs/CustomReminderPickerDialog.kt | New dialog for selecting reminder delay in hours/days. |
| app/src/main/java/com/infomaniak/mail/MatomoMail.kt | Adds Matomo events for reminder + follow-up flows. |
| app/src/main/java/com/infomaniak/mail/data/models/draft/DraftAction.kt | Adds DraftAction.REMINDER. |
0298a52 to
0412b00
Compare
a02592b to
32df7fd
Compare
dfa595e to
87f147b
Compare
…and custom delay screens
…nce from DraftSendOptionFragment
2d199bb to
ce973bf
Compare
ce973bf to
92ae9fb
Compare
1bb3a8d to
f3ea5df
Compare
ac820e2 to
3f07e48
Compare
| private fun addReminder(message: Message) { | ||
| threadViewModel.modifyingReminderMessage = message | ||
| message.reminder = null | ||
| navigateToReminderBottomSheet() |
|
| mailboxUuid: String, | ||
| folderId: String, | ||
| messageId: String, | ||
| reminderUuid: String |
There was a problem hiding this comment.
Add commas to all the last arguments in this file.
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * Infomaniak Mail - Android | |||
| * Copyright (C) 2025-2026 Infomaniak Network SA | |||
There was a problem hiding this comment.
If this file existed in 2025, leave it like 2025-2026
| ThisEvening(Today, Evening, R.string.thisEvening, R.drawable.ic_evening_schedule, MatomoName.ThisEvening), | ||
| TomorrowMorning(Tomorrow, Morning, R.string.tomorrowMorning, R.drawable.ic_morning_schedule, MatomoName.TomorrowMorning), | ||
| TomorrowMorning( | ||
| RelativeDay.Tomorrow, |
There was a problem hiding this comment.
put every argument in different lines, not only one
| Afternoon(14), | ||
| Evening(18), | ||
| enum class HourOfTheDay(val hourOfTheDay: Int) { | ||
| Morning(8), Afternoon(14), Evening(18), |
|
|
||
| fun removeSubtitle() { | ||
| binding.subtitle.apply { | ||
| text = null |
There was a problem hiding this comment.
do we need to put the text as null if it's gone?
| binding.title.text = text | ||
| } | ||
|
|
||
| fun removeIcon() = with(binding) { |
There was a problem hiding this comment.
we don't need the with(binding) if it's just one value
|
|
||
| /** Keep the entries order, it's used by the attribute (or change also the attributes order in attrs.xml) */ | ||
| enum class TrailingContent { | ||
| None, Chevron, Description, KSuitePersoChip, KSuiteProChip, |
There was a problem hiding this comment.
we normally put the enums in different lines
| private fun minimalDisplayTime() = date().time - HIDE_INTERVAL.inWholeMilliseconds | ||
| } | ||
|
|
||
| private enum class RelativeDay(val getDate: () -> Date) { |
There was a problem hiding this comment.
why remove the private if it's only used here? Same for hourOfTheDay and WeekPeriod
|
|
||
| private fun ItemMessageBinding.bindEndReminderAlert(message: Message, reminderDate: Date) { | ||
| endReminderAlert.setActionsVisibility(shouldDisplayAction = true) | ||
| val recipientsNames = message.allRecipients.map { it.name.ifBlank { it.email } } |
There was a problem hiding this comment.
maybe we can do a function getNameWhenEmailisBlank or something like this if it is used as well in bindRequestResponseAlert (senderNames)
| messageId | ||
| ) | ||
|
|
||
| if (apiResponse.isSuccess()) { |
There was a problem hiding this comment.
| if (apiResponse.isSuccess()) { | |
| val snackbarStringResId = if (apiResponse.isSuccess()) successResId else failureResId | |
| snackbarManager.postValue(appContext.getString(snackbarStringResId)) |



No description provided.