Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Scheduled sending #1619

Open
wants to merge 83 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
df1ffc8
style: Init floating panel list and enum
lebojo Oct 16, 2024
3edb40f
style: All views done
lebojo Oct 17, 2024
0c08a97
fix: Testing integration with preprod
lebojo Oct 18, 2024
10762c7
feat: Can now schedule message
lebojo Oct 22, 2024
67203c1
fix: Enum date
lebojo Oct 22, 2024
6b13fd2
fix: Can recieve scheduled messages
lebojo Oct 23, 2024
9acdea4
feat: Show scheduled thread count
lebojo Oct 23, 2024
1415c7f
refactor: Add custom schedule to enum
lebojo Oct 23, 2024
38b4b89
refactor: Same date view for thread + feat: Icon for scheduled messages
lebojo Oct 23, 2024
1cd38c6
fix: Can delete scheduled draft
lebojo Oct 23, 2024
a00c08f
feat: Change draft schedule from message View
lebojo Oct 28, 2024
e19443f
feat: Delete schedule
lebojo Oct 28, 2024
3a6be0e
fix: Better use for calendar
lebojo Oct 29, 2024
c8dc893
feat: Collapse double func
lebojo Oct 29, 2024
f28f68a
fix: Remove preprod
lebojo Oct 29, 2024
70708d7
refactor: Send or schedule
lebojo Oct 29, 2024
efafd0b
refactor: Delete doublons
lebojo Oct 29, 2024
fe5571a
feat: Custom schedule
lebojo Oct 29, 2024
2686958
fix: Edit draft
lebojo Oct 30, 2024
88a9bd7
fix: See scheduled draft folder
lebojo Oct 30, 2024
67c2bb8
refactor: Delete multiple draft
lebojo Oct 31, 2024
b8703cb
fix: Localized strings
lebojo Oct 31, 2024
1a6181b
fix: Format
lebojo Oct 31, 2024
eaaa208
refactor: Working system for new schedule
lebojo Nov 1, 2024
b04a33d
fix: Localizable for snackbar info
lebojo Nov 1, 2024
7ace1c9
feat: Reload schedule folder
lebojo Nov 1, 2024
50836f5
fix: Scheduled folder is showing only when needed
lebojo Nov 4, 2024
2098bda
feat: Added option for modal dismiss optional
lebojo Nov 6, 2024
45368a5
feat: Edit draft from draft
lebojo Nov 6, 2024
8668e63
feat: Snakbar to cancel schedule
lebojo Nov 6, 2024
3b3ac29
feat: Verify is schedule is possible
lebojo Nov 6, 2024
9f13792
style: Localized string for cancelled schedule
lebojo Nov 6, 2024
5f0d898
style: Refactor to match Figma's style
lebojo Nov 6, 2024
9a37475
style: Match color of figma
lebojo Nov 6, 2024
a89005e
fix: Replace date iso8601 String to Date object
lebojo Nov 6, 2024
529f54d
style: Match the figma's clock for HeaderDateView
lebojo Nov 7, 2024
943b8d9
feat: Schedule header match color of label
lebojo Nov 7, 2024
a23a5f1
fix: Modifying schedule date
lebojo Nov 8, 2024
75be70f
fix: Color for schedule date matching figma's
lebojo Nov 8, 2024
dd97555
fix: Remove useless date
lebojo Nov 8, 2024
a962974
style: Remove debug things
lebojo Nov 8, 2024
66fcaae
feat: Add feature flag support for custom schedule
lebojo Nov 8, 2024
ffec3a4
feat: Add schedule discoveryView and set optional later button
lebojo Nov 11, 2024
a34cc99
fix: Floating panel can't be hidden
lebojo Nov 11, 2024
e290d6a
fix: Check if conversion worked
lebojo Nov 11, 2024
4f69d20
fix: Transition between panel and modal
lebojo Nov 11, 2024
0ddad01
style: Extract custom Schedule button into his own file
lebojo Nov 11, 2024
55d0f2e
refactor: Clarify CustomScheduleModalView
lebojo Nov 13, 2024
f546d12
fix: Norm code
lebojo Nov 14, 2024
3e0a18d
style: Change color of schedule header to match figma
lebojo Nov 15, 2024
129d75e
fix: Correct feature flag check
lebojo Nov 19, 2024
ef9700b
refactor: New icons and icon names.
lebojo Nov 19, 2024
6e89601
fix: Update old tests
lebojo Nov 19, 2024
edf67e6
fix: Floating panel showing für reschedule too
lebojo Nov 20, 2024
11f9b30
feat: Floating panel self dismiss
lebojo Nov 20, 2024
2643c1f
feat: Inverse threads list order in scheduled draft folder
lebojo Nov 20, 2024
77a4831
fix: Remove useless ternary
lebojo Nov 20, 2024
10bfa5d
feat: Snackbar take boolean to display
lebojo Nov 20, 2024
77d4a60
feat: New icons and localized strings
lebojo Nov 22, 2024
de176ad
fix: Remove unused
lebojo Nov 25, 2024
150e90d
fix: Wrong statement
lebojo Dec 2, 2024
779eb33
fix: Added snooze folder for match API
lebojo Dec 2, 2024
78f95d7
style: Added iconsize
lebojo Dec 2, 2024
8eb4074
refactor: Simplified showed scheduled option logic
lebojo Dec 2, 2024
28b852f
fix: Wording
lebojo Dec 10, 2024
183c409
refactor: Rethrows schedules action
lebojo Dec 10, 2024
89ff993
fix: Format
lebojo Dec 10, 2024
2d8c8c7
fix: Wording
lebojo Dec 10, 2024
f6ae3ca
refactor: Wording and uses of ikLabel
lebojo Dec 10, 2024
45eaf9b
fix: Used already own discovery picture
lebojo Dec 11, 2024
5a9c197
fix: Remove swipe action from scheduled messages/thread
lebojo Dec 11, 2024
3d54606
refactor: Correct Draft Action to schedule action
lebojo Dec 30, 2024
3dbefe4
refactor: Simplified schedule date checker
lebojo Jan 3, 2025
c0e6701
chore: Updated strings
lebojo Jan 3, 2025
eb85bda
refactor: Wording scheduleAction draftAction
lebojo Jan 3, 2025
a69caca
feat: Change quick actions for scheduled
lebojo Jan 3, 2025
3de6c13
fix: No more thread in scheduled folder
lebojo Jan 6, 2025
b497bd6
fix: Bottombar for Scheduled
lebojo Jan 6, 2025
275f60a
refactor: New strategy for Later button (DiscoveryView)
lebojo Jan 7, 2025
4c08fd4
refactor: Remove "row" from name
lebojo Jan 7, 2025
81070cc
fix: Remove comments
lebojo Jan 9, 2025
a60928e
feat: Matomo
lebojo Jan 9, 2025
f0396c7
fix: Update locco
lebojo Jan 14, 2025
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
5 changes: 4 additions & 1 deletion Mail/Components/Custom Buttons/ModalButtonsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ struct ModalButtonsView: View {
let primaryButtonTitle: String
var secondaryButtonTitle: String? = MailResourcesStrings.Localizable.buttonCancel
var primaryButtonEnabled = true
var primaryButtonDismiss = true
let primaryButtonAction: () async -> Void
var secondaryButtonAction: (() -> Void)?

Expand All @@ -47,7 +48,9 @@ struct ModalButtonsView: View {
isButtonLoading = true
await primaryButtonAction()
isButtonLoading = false
dismiss()
if primaryButtonDismiss {
dismiss()
}
}
}
.buttonStyle(.ikBorderedProminent)
Expand Down
7 changes: 1 addition & 6 deletions Mail/Components/ThreadCell/ThreadCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,6 @@ struct ThreadCellDataHolder {
/// Sender of the last message that is not in the Sent folder, otherwise the last message of the thread
let recipientToDisplay: Recipient?

/// Date of the last message of the folder, otherwise the last message of the thread
let date: String

/// Subject of the first message
let subject: String

Expand All @@ -58,8 +55,6 @@ struct ThreadCellDataHolder {
// swiftlint:disable:next last_where
let lastMessageNotFromSent = thread.messages.filter(Self.lastMessageNotFromSentPredicate).last ?? thread.messages.last

date = thread.date.formatted(.thread(.list))

subject = thread.formattedSubject

isInWrittenByMeFolder = FolderRole.writtenByMeFolders.contains { $0 == thread.folder?.role }
Expand Down Expand Up @@ -181,7 +176,7 @@ struct ThreadCell: View {
),
messageCount: thread.messages.count,
prominentMessageCount: thread.hasUnseenMessages,
formattedDate: dataHolder.date,
date: thread.date,
showDraftPrefix: thread.hasDrafts
)

Expand Down
8 changes: 2 additions & 6 deletions Mail/Components/ThreadCell/ThreadCellHeaderView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ struct ThreadCellHeaderView: View, Equatable {
let recipientsTitle: String
let messageCount: Int
let prominentMessageCount: Bool
let formattedDate: String
let date: Date
let showDraftPrefix: Bool

var body: some View {
Expand All @@ -49,11 +49,7 @@ struct ThreadCellHeaderView: View, Equatable {
}
.frame(maxWidth: .infinity, alignment: .leading)

Text(formattedDate)
.textStyle(.bodySmallSecondary)
.lineLimit(1)
.layoutPriority(1)
.accessibilityHidden(true)
HeaderDateView(date: date, format: .list)
}
}
}
4 changes: 4 additions & 0 deletions Mail/Utils/Date+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,8 @@ extension Date {
components.second = -1
return Calendar.current.date(byAdding: components, to: startOfMonth)!
}

static var minimumScheduleDelay: Date {
Date.now.addingTimeInterval(5 * 60)
}
}
22 changes: 15 additions & 7 deletions Mail/Utils/DraftUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,8 @@ enum DraftUtils {
composeMessageIntent: Binding<ComposeMessageIntent?>
) {
guard let message = thread.messages.first else { return }
// If we already have the draft locally, present it directly
if let draft = mailboxManager.draft(messageUid: message.uid)?.detached() {
matomoOpenDraft(isLoadedRemotely: false)
composeMessageIntent.wrappedValue = ComposeMessageIntent.existing(draft: draft, originMailboxManager: mailboxManager)
} else {
DraftUtils.editDraft(from: message, mailboxManager: mailboxManager, composeMessageIntent: composeMessageIntent)
}

DraftUtils.editDraft(from: message, mailboxManager: mailboxManager, composeMessageIntent: composeMessageIntent)
Comment on lines +33 to +34
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert

Copy link
Contributor Author

@lebojo lebojo Dec 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Context: We had seen together (IRL) that there was a bug: When we modify a draft, it appeared twice (the modified version and the old one) (duplicate bug)
And when we reverted that part of the code it seemed to work again.

Update: I tried to reproduce the bug but I can't do it, it would be necessary to investigate a little more

}

public static func editDraft(
Expand All @@ -58,6 +53,19 @@ enum DraftUtils {
}
}

public static func editDraft(
from draft: Draft,
mailboxManager: MailboxManageable,
composeMessageIntent: Binding<ComposeMessageIntent?>
) {
let draftDetached = draft.detached()
matomoOpenDraft(isLoadedRemotely: false)
composeMessageIntent.wrappedValue = ComposeMessageIntent.existing(
draft: draftDetached,
originMailboxManager: mailboxManager
)
}

private static func matomoOpenDraft(isLoadedRemotely: Bool) {
@InjectService var matomo: MatomoUtils
matomo.track(eventWithCategory: .newMessage, name: "openFromDraft")
Expand Down
86 changes: 86 additions & 0 deletions Mail/Views/Alerts/CustomScheduleAlertView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
Infomaniak Mail - iOS App
Copyright (C) 2024 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/>.
*/

import InfomaniakCoreCommonUI
import InfomaniakCoreSwiftUI
import InfomaniakDI
import MailCore
import MailCoreUI
import MailResources
import SwiftUI

struct CustomScheduleAlertView: View {
@LazyInjectService private var matomo: MatomoUtils

@State private var isShowingError = false
@State private var selectedDate: Date

let startingDate: Date
let confirmAction: (Date) -> Void
let cancelAction: (() -> Void)?

private var isDelayTooShort: Bool {
selectedDate < Date.minimumScheduleDelay
}

init(startingDate: Date, confirmAction: @escaping (Date) -> Void, cancelAction: (() -> Void)? = nil) {
_selectedDate = .init(initialValue: startingDate)
self.startingDate = startingDate
self.confirmAction = confirmAction
self.cancelAction = cancelAction
}

var body: some View {
VStack(alignment: .leading, spacing: 0) {
Text(MailResourcesStrings.Localizable.datePickerTitle)
.textStyle(.bodyMedium)
.padding(.bottom, IKPadding.alertTitleBottom)
DatePicker(
MailResourcesStrings.Localizable.datePickerTitle,
selection: $selectedDate,
in: Date.minimumScheduleDelay...
)
.labelsHidden()
.onChange(of: selectedDate) { newDate in
isShowingError = newDate < Date.minimumScheduleDelay
}

Text(MailResourcesStrings.Localizable.errorScheduleDelayTooShort)
.textStyle(.labelError)
.padding(.top, value: .extraSmall)
.opacity(isShowingError ? 1 : 0)
.padding(.bottom, IKPadding.alertDescriptionBottom)

ModalButtonsView(primaryButtonTitle: MailResourcesStrings.Localizable.buttonScheduleTitle,
secondaryButtonTitle: MailResourcesStrings.Localizable.buttonCancel,
primaryButtonEnabled: !isShowingError,
primaryButtonDismiss: !isDelayTooShort,
primaryButtonAction: executeActionIfPossible,
secondaryButtonAction: cancelAction)
}
}

private func executeActionIfPossible() {
guard !isDelayTooShort else {
isShowingError = true
return
}
confirmAction(selectedDate)
matomo.track(eventWithCategory: .scheduleSend, name: "customSchedule")
}
}
60 changes: 60 additions & 0 deletions Mail/Views/Alerts/ModifyMessageScheduleAlertView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
Infomaniak Mail - iOS App
Copyright (C) 2024 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/>.
*/

import InfomaniakCoreSwiftUI
import MailCore
import MailResources
import SwiftUI

struct ModifyMessageScheduleAlertView: View {
@EnvironmentObject private var mainViewState: MainViewState
@EnvironmentObject private var mailboxManager: MailboxManager

let draftResource: String

var body: some View {
VStack(alignment: .leading, spacing: IKPadding.medium) {
Text(MailResourcesStrings.Localizable.editSendTitle)
.textStyle(.bodyMedium)
Text(MailResourcesStrings.Localizable.editSendDescription)
.textStyle(.body)
ModalButtonsView(primaryButtonTitle: MailResourcesStrings.Localizable.buttonModify,
secondaryButtonTitle: MailResourcesStrings.Localizable.buttonCancel,
primaryButtonAction: modifySchedule)
}
}

private func modifySchedule() {
Task {
await tryOrDisplayError {
try await mailboxManager.moveScheduleToDraft(draftResource: draftResource)
guard let draft = try await mailboxManager.loadRemotely(from: draftResource) else { return }

DraftUtils.editDraft(
from: draft,
mailboxManager: mailboxManager,
composeMessageIntent: $mainViewState.composeMessageIntent
)
}
}
}
}

#Preview {
ModifyMessageScheduleAlertView(draftResource: "")
}
58 changes: 46 additions & 12 deletions Mail/Views/Bottom sheets/Discovery/DiscoveryView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@ import MailCore
import MailResources
import SwiftUI

public struct DiscoveryItem {
public struct DiscoveryItem: Equatable {
public let image: MailResourcesImages
public let title: String
public let description: String
public let primaryButtonLabel: String
public let matomoCategory: MatomoUtils.EventCategory

public static func == (lhs: DiscoveryItem, rhs: DiscoveryItem) -> Bool {
lhs.title == rhs.title && lhs.description == rhs.description
}
}

public extension DiscoveryItem {
Expand Down Expand Up @@ -63,6 +67,14 @@ public extension DiscoveryItem {
primaryButtonLabel: MailResourcesStrings.Localizable.buttonSetNow,
matomoCategory: .setAsDefaultApp
)

static let scheduleDiscovery = DiscoveryItem(
image: MailResourcesAsset.updateRequired,
title: MailResourcesStrings.Localizable.disabledFeatureFlagTitle,
description: MailResourcesStrings.Localizable.disabledFeatureFlagDescription,
primaryButtonLabel: MailResourcesStrings.Localizable.buttonClose,
matomoCategory: .scheduleSend
)
}

struct DiscoveryView: View {
Expand All @@ -81,9 +93,17 @@ struct DiscoveryView: View {
var body: some View {
Group {
if isCompactWindow {
DiscoveryBottomSheetView(item: item, nowButton: didTouchNowButton, laterButton: didTouchLaterButton)
DiscoveryBottomSheetView(
item: item,
nowButton: didTouchNowButton,
laterButton: didTouchLaterButton
)
} else {
DiscoveryAlertView(item: item, nowButton: didTouchNowButton, laterButton: didTouchLaterButton)
DiscoveryAlertView(
item: item,
nowButton: didTouchNowButton,
laterButton: didTouchLaterButton
)
}
}
.onAppear {
Expand Down Expand Up @@ -114,6 +134,11 @@ struct DiscoveryBottomSheetView: View {
let nowButton: () -> Void
let laterButton: () -> Void

var shouldDisplayLaterButton: Bool {
guard item != .scheduleDiscovery else { return false }
return true
}

var body: some View {
VStack(spacing: 32) {
item.image.swiftUIImage
Expand All @@ -130,8 +155,10 @@ struct DiscoveryBottomSheetView: View {
Button(item.primaryButtonLabel, action: nowButton)
.buttonStyle(.ikBorderedProminent)

Button(MailResourcesStrings.Localizable.buttonLater, action: laterButton)
.buttonStyle(.ikBorderless)
if shouldDisplayLaterButton {
Button(MailResourcesStrings.Localizable.buttonLater, action: laterButton)
.buttonStyle(.ikBorderless)
}
}
.ikButtonFullWidth(true)
.controlSize(.large)
Expand All @@ -145,7 +172,7 @@ struct DiscoveryAlertView: View {
let item: DiscoveryItem

let nowButton: () -> Void
let laterButton: () -> Void
let laterButton: (() -> Void)?

var body: some View {
VStack(spacing: IKPadding.large) {
Expand All @@ -159,12 +186,19 @@ struct DiscoveryAlertView: View {
.multilineTextAlignment(.center)
.textStyle(.bodySecondary)

ModalButtonsView(
primaryButtonTitle: item.primaryButtonLabel,
secondaryButtonTitle: MailResourcesStrings.Localizable.buttonLater,
primaryButtonAction: nowButton,
secondaryButtonAction: laterButton
)
if let laterButton {
ModalButtonsView(
primaryButtonTitle: item.primaryButtonLabel,
secondaryButtonTitle: MailResourcesStrings.Localizable.buttonLater,
primaryButtonAction: nowButton,
secondaryButtonAction: laterButton
)
} else {
ModalButtonsView(
primaryButtonTitle: item.primaryButtonLabel,
primaryButtonAction: nowButton
)
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Mail/Views/Menu Drawer/FolderListViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ struct FolderListViewModelWorker {
private func filterFolders(_ folders: Results<Folder>, searchQuery: String) -> [Folder] {
guard !searchQuery.isEmpty else {
// swiftlint:disable:next empty_count
return Array(folders.where { $0.parents.count == 0 })
return Array(folders.filter { ($0.parents.count == 0 || $0.role != nil) && $0.shouldBeDisplayed })
}

return folders.filter {
Expand Down
Loading
Loading