Skip to content
Merged
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
35 changes: 35 additions & 0 deletions Mail/Views/Thread/Message/MessageBannerHeaderView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ struct MessageBannerHeaderView: View {
@EnvironmentObject private var mailboxManager: MailboxManager

@State private var isUnsubscribeSuccessful = false
@State private var isAcknowledgeSuccessful = false

let banners: [MessageBanner]

Expand Down Expand Up @@ -79,6 +80,22 @@ struct MessageBannerHeaderView: View {
asyncAction: unsubscribeAction
)
}
case .acknowledge:
if !isAcknowledgeSuccessful && message.hasPendingAcknowledgement {
MessageHeaderAsyncActionView(
icon: MailResourcesAsset.envelope.swiftUIImage,
message: MailResourcesStrings.Localizable.acknowledgementMessage,
actionTitle: MailResourcesStrings.Localizable.sendConfirmationAction,
showBottomSeparator: showBottomSeparator,
asyncAction: acknowledgeAction
)
} else {
MessageHeaderActionView(
icon: MailResourcesAsset.check.swiftUIImage,
message: MailResourcesStrings.Localizable.acknowledgementMessageSent,
showBottomSeparator: showBottomSeparator
) {}
}
}
}
}
Expand Down Expand Up @@ -106,6 +123,24 @@ struct MessageBannerHeaderView: View {
snackbarPresenter.show(message: MailResourcesStrings.Localizable.snackbarUnsubscribeFailure)
}
}

private func acknowledgeAction() async {
@InjectService var snackbarPresenter: IKSnackBarPresentable
do {
try await mailboxManager.apiFetcher.acknowledgeMessage(messageResource: message.resource)
try mailboxManager.transactionExecutor.writeTransaction { realm in
if let live = realm.object(ofType: Message.self, forPrimaryKey: message.uid) {
live.acknowledgeStatus = .acknowledged
}
}
snackbarPresenter.show(message: MailResourcesStrings.Localizable.snackbarAcknowledgementSuccess)
withAnimation {
isAcknowledgeSuccessful = true
}
} catch {
snackbarPresenter.show(message: MailResourcesStrings.Localizable.snackbarAcknowledgementFailure)
}
}
}

#Preview {
Expand Down
4 changes: 4 additions & 0 deletions Mail/Views/Thread/Message/MessageSubHeaderView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ struct MessageSubHeaderView: View {
result.append(.encrypted)
}

if message.hasAcknowledgement {
result.append(.acknowledge)
}

return result
}

Expand Down
4 changes: 4 additions & 0 deletions MailCore/API/Endpoint/Endpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -340,4 +340,8 @@ public extension Endpoint {
static func unsubscribe(resource: String) -> Endpoint {
return .resource(resource).appending(path: "/unsubscribeFromList")
}

static func acknowledge(resource: String) -> Endpoint {
return .resource(resource).appending(path: "/acknowledge")
}
}
4 changes: 4 additions & 0 deletions MailCore/API/MailApiFetcher/MailApiFetcher+Extended.swift
Original file line number Diff line number Diff line change
Expand Up @@ -223,4 +223,8 @@ public extension MailApiFetcher {
func unsubscribe(messageResource: String) async throws {
let _: Empty = try await perform(request: authenticatedRequest(.unsubscribe(resource: messageResource), method: .post))
}

func acknowledgeMessage(messageResource: String) async throws {
let _: Empty = try await perform(request: authenticatedRequest(.acknowledge(resource: messageResource), method: .get))
}
}
2 changes: 1 addition & 1 deletion MailCore/Cache/MailboxManager/MailboxManager+Thread.swift
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,7 @@ public extension MailboxManager {
}

private func upsertMessage(_ message: Message, oldMessage: Message, threadsToUpdate: inout Set<Thread>, using realm: Realm) {
keepCacheAttributes(for: message, keepProperties: .standard, using: realm)
keepCacheAttributes(for: message, keepProperties: [.standard, .acknowledge], using: realm)
realm.add(message, update: .modified)

threadsToUpdate.formUnion(oldMessage.threads)
Expand Down
7 changes: 6 additions & 1 deletion MailCore/Cache/MailboxManager/MailboxManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public final class MailboxManager: ObservableObject, MailboxManageable {
let realmName = "\(mailbox.userId)-\(mailbox.mailboxId).realm"
realmConfiguration = Realm.Configuration(
fileURL: MailboxManager.constants.rootDocumentsURL.appendingPathComponent(realmName),
schemaVersion: 47,
schemaVersion: 48,
migrationBlock: { migration, oldSchemaVersion in
// No migration needed from 0 to 16
if oldSchemaVersion < 17 {
Expand Down Expand Up @@ -187,6 +187,7 @@ public final class MailboxManager: ObservableObject, MailboxManageable {
static let localSafeDisplay = MessagePropertiesOptions(rawValue: 1 << 3)
static let reactions = MessagePropertiesOptions(rawValue: 1 << 4)
static let calendarEventResponse = MessagePropertiesOptions(rawValue: 1 << 5)
static let acknowledge = MessagePropertiesOptions(rawValue: 1 << 6)

static let standard: MessagePropertiesOptions = [
.fullyDownloaded,
Expand Down Expand Up @@ -228,6 +229,10 @@ public final class MailboxManager: ObservableObject, MailboxManageable {
if keepProperties.contains(.calendarEventResponse), let calendarEventResponse = savedMessage.calendarEventResponse {
message.calendarEventResponse = calendarEventResponse.detached()
}
if keepProperties.contains(.acknowledge),
message.acknowledge == nil {
message.acknowledge = savedMessage.acknowledge
}
}

func keepCacheAttributes(
Expand Down
28 changes: 27 additions & 1 deletion MailCore/Models/Message.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ public enum MessageDKIM: String, Codable, PersistableEnum {
case notSigned = "not_signed"
}

public enum AcknowledgeStatus: String {
case pending
case acknowledged
}

public struct MessageActionResult: Codable {
public var flagged: Int
}
Expand Down Expand Up @@ -163,6 +168,7 @@ public final class Message: Object, Decodable, ObjectKeyIdentifiable {
@Persisted public var encrypted: Bool
@Persisted public var encryptionPassword: String
@Persisted public var cryptPasswordValidity: Date?
@Persisted var acknowledge: String?
@Persisted private var headers: MessageHeaders?
/// Threads where the message can be found
@Persisted(originProperty: "messages") var threads: LinkingObjects<Thread>
Expand Down Expand Up @@ -289,6 +295,22 @@ public final class Message: Object, Decodable, ObjectKeyIdentifiable {
return !reactionMessages.where { $0.seen == false }.isEmpty
}

public var acknowledgeStatus: AcknowledgeStatus? {
get {
return AcknowledgeStatus(rawValue: acknowledge ?? "")
} set {
acknowledge = newValue?.rawValue
}
}

public var hasAcknowledgement: Bool {
return hasPendingAcknowledgement || acknowledgeStatus == .acknowledged
}

public var hasPendingAcknowledgement: Bool {
return acknowledgeStatus == .pending
}

public func fromMe(currentMailboxEmail: String) -> Bool {
return from.contains { $0.isMe(currentMailboxEmail: currentMailboxEmail) }
}
Expand Down Expand Up @@ -379,6 +401,7 @@ public final class Message: Object, Decodable, ObjectKeyIdentifiable {
case emojiReaction
case emojiReactionNotAllowedReason
case headers
case acknowledge
}

override init() {
Expand Down Expand Up @@ -459,6 +482,7 @@ public final class Message: Object, Decodable, ObjectKeyIdentifiable {
)

headers = try? values.decodeIfPresent(MessageHeaders.self, forKey: .headers)
acknowledge = try values.decodeIfPresent(String.self, forKey: .acknowledge)
}

public convenience init(
Expand Down Expand Up @@ -498,7 +522,8 @@ public final class Message: Object, Decodable, ObjectKeyIdentifiable {
snoozeUuid: String? = nil,
snoozeEndDate: Date? = nil,
emojiReaction: String? = nil,
emojiReactionNotAllowedReason: EmojiReactionNotAllowedReason? = nil
emojiReactionNotAllowedReason: EmojiReactionNotAllowedReason? = nil,
acknowledge: String? = nil
) {
self.init()

Expand Down Expand Up @@ -540,6 +565,7 @@ public final class Message: Object, Decodable, ObjectKeyIdentifiable {
self.snoozeEndDate = snoozeEndDate
self.emojiReaction = emojiReaction
self.emojiReactionNotAllowedReason = emojiReactionNotAllowedReason
self.acknowledge = acknowledge
}

public func toThread() -> Thread {
Expand Down
5 changes: 5 additions & 0 deletions MailCore/Models/MessageBanner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public enum MessageBanner: Equatable, Identifiable, Hashable {
case displayContent
case encrypted
case unsubscribeLink
case acknowledge

public static func == (lhs: MessageBanner, rhs: MessageBanner) -> Bool {
switch (lhs, rhs) {
Expand All @@ -41,6 +42,8 @@ public enum MessageBanner: Equatable, Identifiable, Hashable {
return true
case (.unsubscribeLink, .unsubscribeLink):
return true
case (.acknowledge, .acknowledge):
return true
default:
return false
}
Expand All @@ -58,6 +61,8 @@ public extension [MessageBanner] {
return !contains(.encrypted)
case .unsubscribeLink:
return true
case .acknowledge:
return true
default:
return false
}
Expand Down
3 changes: 2 additions & 1 deletion MailCoreUI/Helpers/PreviewHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ public enum PreviewHelper {
scheduled: false,
forwarded: false,
flagged: false,
hasUnsubscribeLink: true)
hasUnsubscribeLink: true,
acknowledge: "pending")

public static let sampleMessages = Array(
repeating: PreviewHelper.sampleMessage,
Expand Down
15 changes: 15 additions & 0 deletions MailResources/Localizable/de.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
/* loco:642179d9733cd4523d03dcf2 */
"accentColorSystemTitle" = "Farbe des Systems";

/* loco:6936f775de93800154038b75 */
"acknowledgementMessage" = "Der Absender hat eine Lesebestätigung angefordert.";

/* loco:6936f7e6835ef86e27063eb6 */
"acknowledgementMessageSent" = "Es wurde eine Lesebestätigung an den Absender gesendet.";

/* loco:629f0e2244ac887a51141312 */
"actionArchive" = "Archiv";

Expand Down Expand Up @@ -1360,6 +1366,9 @@
/* loco:62ac871d2502053d7b0cbb32 */
"send" = "Senden Sie";

/* loco:6936f6d2c3bd045a2001d752 */
"sendConfirmationAction" = "Senden Sie die Bestätigung";

/* loco:627e401ec1c7c02de9181292 */
"sentFolder" = "Gesendete Nachrichten";

Expand Down Expand Up @@ -1627,6 +1636,12 @@
/* loco:652fd443ec75b05b000d43f2 */
"snackBarAccountDeleted" = "Gelöschtes Konto";

/* loco:6937d843b9fb15c2510859d2 */
"snackbarAcknowledgementFailure" = "Senden fehlgeschlagen";

/* loco:6937d715da67a52ff201eed2 */
"snackbarAcknowledgementSuccess" = "Lesebestätigung gesendet";

/* loco:63f3961e7c16fb607809d632 */
"snackbarBlockUserConfirmation" = "Absender ist jetzt blockiert";

Expand Down
15 changes: 15 additions & 0 deletions MailResources/Localizable/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
/* loco:642179d9733cd4523d03dcf2 */
"accentColorSystemTitle" = "System color";

/* loco:6936f775de93800154038b75 */
"acknowledgementMessage" = "The sender requested a read receipt.";

/* loco:6936f7e6835ef86e27063eb6 */
"acknowledgementMessageSent" = "A read receipt has been sent to the sender.";

/* loco:629f0e2244ac887a51141312 */
"actionArchive" = "Archive";

Expand Down Expand Up @@ -1360,6 +1366,9 @@
/* loco:62ac871d2502053d7b0cbb32 */
"send" = "Send";

/* loco:6936f6d2c3bd045a2001d752 */
"sendConfirmationAction" = "Send the confirmation";

/* loco:627e401ec1c7c02de9181292 */
"sentFolder" = "Sent messages";

Expand Down Expand Up @@ -1627,6 +1636,12 @@
/* loco:652fd443ec75b05b000d43f2 */
"snackBarAccountDeleted" = "Account deleted";

/* loco:6937d843b9fb15c2510859d2 */
"snackbarAcknowledgementFailure" = "Failed to send";

/* loco:6937d715da67a52ff201eed2 */
"snackbarAcknowledgementSuccess" = "Read receipt sent";

/* loco:63f3961e7c16fb607809d632 */
"snackbarBlockUserConfirmation" = "Sender is now blocked";

Expand Down
15 changes: 15 additions & 0 deletions MailResources/Localizable/es.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
/* loco:642179d9733cd4523d03dcf2 */
"accentColorSystemTitle" = "Color del sistema";

/* loco:6936f775de93800154038b75 */
"acknowledgementMessage" = "El remitente ha solicitado una confirmación de lectura.";

/* loco:6936f7e6835ef86e27063eb6 */
"acknowledgementMessageSent" = "Se ha enviado una confirmación de lectura al remitente.";

/* loco:629f0e2244ac887a51141312 */
"actionArchive" = "Archivo";

Expand Down Expand Up @@ -1357,6 +1363,9 @@
/* loco:62ac871d2502053d7b0cbb32 */
"send" = "Enviar";

/* loco:6936f6d2c3bd045a2001d752 */
"sendConfirmationAction" = "Enviar la confirmación";

/* loco:627e401ec1c7c02de9181292 */
"sentFolder" = "Mensajes enviados";

Expand Down Expand Up @@ -1624,6 +1633,12 @@
/* loco:652fd443ec75b05b000d43f2 */
"snackBarAccountDeleted" = "Cuenta eliminada";

/* loco:6937d843b9fb15c2510859d2 */
"snackbarAcknowledgementFailure" = "Error al enviar";

/* loco:6937d715da67a52ff201eed2 */
"snackbarAcknowledgementSuccess" = "Recibo de lectura enviado";

/* loco:63f3961e7c16fb607809d632 */
"snackbarBlockUserConfirmation" = "El remitente está bloqueado";

Expand Down
15 changes: 15 additions & 0 deletions MailResources/Localizable/fr.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
/* loco:642179d9733cd4523d03dcf2 */
"accentColorSystemTitle" = "Couleur du système";

/* loco:6936f775de93800154038b75 */
"acknowledgementMessage" = "L’expéditeur a demandé une confirmation de lecture.";

/* loco:6936f7e6835ef86e27063eb6 */
"acknowledgementMessageSent" = "Une confirmation de lecture a été envoyée à l’expéditeur.";

/* loco:629f0e2244ac887a51141312 */
"actionArchive" = "Archiver";

Expand Down Expand Up @@ -1360,6 +1366,9 @@
/* loco:62ac871d2502053d7b0cbb32 */
"send" = "Envoyer";

/* loco:6936f6d2c3bd045a2001d752 */
"sendConfirmationAction" = "Envoyer la confirmation";

/* loco:627e401ec1c7c02de9181292 */
"sentFolder" = "Messages envoyés";

Expand Down Expand Up @@ -1627,6 +1636,12 @@
/* loco:652fd443ec75b05b000d43f2 */
"snackBarAccountDeleted" = "Compte supprimé";

/* loco:6937d843b9fb15c2510859d2 */
"snackbarAcknowledgementFailure" = "Échec de l’envoi";

/* loco:6937d715da67a52ff201eed2 */
"snackbarAcknowledgementSuccess" = "Accusé de lecture envoyé";

/* loco:63f3961e7c16fb607809d632 */
"snackbarBlockUserConfirmation" = "L’expéditeur est maintenant bloqué";

Expand Down
Loading
Loading