Skip to content

Commit

Permalink
Merge pull request #766 from Infomaniak/delta
Browse files Browse the repository at this point in the history
fix: Fetch all new messages after delta
  • Loading branch information
valentinperignon committed Jun 2, 2023
2 parents a1499c5 + a80d33b commit 22d3205
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 70 deletions.
6 changes: 4 additions & 2 deletions Mail/Views/Thread List/ThreadListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,10 @@ struct ThreadListView: View {
}
Task {
await tryOrDisplayError {
_ = try await viewModel.mailboxManager
.moreMessages(folder: viewModel.folder.freeze())
_ = try await viewModel.mailboxManager.fetchOnePage(
folder: viewModel.folder.freeze(),
direction: .previous
)
isLoadingMore = false
}
}
Expand Down
12 changes: 8 additions & 4 deletions MailCore/API/Endpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,17 @@ public extension Endpoint {
// MARK: - New Routes

static func messages(mailboxUuid: String, folderId: String) -> Endpoint {
return Endpoint(hostKeypath: \.mailHost, path: "/api/mail/\(mailboxUuid)/folder/\(folderId)/mobile")
return Endpoint(
hostKeypath: \.mailHost,
path: "/api/mail/\(mailboxUuid)/folder/\(folderId)/mobile"
)
}

static func messagesUids(mailboxUuid: String, folderId: String, offset: String?) -> Endpoint {
static func messagesUids(mailboxUuid: String, folderId: String, paginationInfo: PaginationInfo?) -> Endpoint {
var queryItems = [URLQueryItem(name: "messages", value: Constants.pageSize.toString())]
if let offset {
queryItems.append(URLQueryItem(name: "uid_offset", value: offset))
if let paginationInfo {
queryItems.append(URLQueryItem(name: "uid_offset", value: paginationInfo.offsetUid))
queryItems.append(URLQueryItem(name: "direction", value: paginationInfo.direction.rawValue))
}
return .messages(mailboxUuid: mailboxUuid, folderId: folderId).appending(path: "/messages-uids", queryItems: queryItems)
}
Expand Down
8 changes: 6 additions & 2 deletions MailCore/API/MailApiFetcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,15 @@ public class MailApiFetcher: ApiFetcher {
try await perform(request: authenticatedRequest(.resource(resource, queryItems: searchFilter))).data
}

func messagesUids(mailboxUuid: String, folderId: String, offset: String? = nil) async throws -> MessageUidsResult {
func messagesUids(
mailboxUuid: String,
folderId: String,
paginationInfo: PaginationInfo? = nil
) async throws -> MessageUidsResult {
try await perform(request: authenticatedRequest(.messagesUids(
mailboxUuid: mailboxUuid,
folderId: folderId,
offset: offset
paginationInfo: paginationInfo
))).data
}

Expand Down
133 changes: 71 additions & 62 deletions MailCore/Cache/MailboxManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ public class MailboxManager: ObservableObject {

// MARK: - Message

private func getUniqueUidsInReverse(folder: Folder, remoteUids: [String]) -> [String] {
private func getUniqueUids(folder: Folder, remoteUids: [String]) -> [String] {
let localUids = Set(folder.threads.map { Constants.shortUid(from: $0.uid) })
let remoteUidsSet = Set(remoteUids)
var uniqueUids: Set<String> = Set()
Expand All @@ -619,7 +619,7 @@ public class MailboxManager: ObservableObject {
} else {
uniqueUids = remoteUidsSet.subtracting(localUids)
}
return uniqueUids.reversed()
return uniqueUids.toArray()
}

public func messages(folder: Folder) async throws {
Expand All @@ -644,7 +644,7 @@ public class MailboxManager: ObservableObject {
signature: previousCursor!
)
messagesUids = MessagesUids(
addedShortUids: messageDeltaResult.addedShortUids,
addedShortUids: [],
deletedUids: messageDeltaResult.deletedShortUids
.map { Constants.longUid(from: $0, folderId: folder.id) },
updated: messageDeltaResult.updated,
Expand Down Expand Up @@ -677,6 +677,10 @@ public class MailboxManager: ObservableObject {
)
}

while try await fetchOnePage(folder: folder, direction: .following) {
guard !Task.isCancelled else { return }
}

if folder.role == .inbox {
MailboxInfosManager.instance.updateUnseen(unseenMessages: folder.unreadCount, for: mailbox)
}
Expand All @@ -685,7 +689,7 @@ public class MailboxManager: ObservableObject {
while remainingOldMessagesToFetch > 0 {
guard !Task.isCancelled else { return }

if try !(await moreMessages(folder: folder)) {
if try !(await fetchOnePage(folder: folder, direction: .previous)) {
break
}

Expand All @@ -699,16 +703,25 @@ public class MailboxManager: ObservableObject {
}
}

public func moreMessages(folder: Folder) async throws -> Bool {
public func fetchOnePage(folder: Folder, direction: NewMessagesDirection? = nil) async throws -> Bool {
let realm = getRealm()
guard let offset = realm.objects(Message.self).where({ $0.folderId == folder.id })
.sorted(by: { $0.shortUid! < $1.shortUid! }).first?
.shortUid?.toString() else { return false }
var paginationInfo: PaginationInfo?

if let offset = realm.objects(Message.self).where({ $0.folderId == folder.id })
.sorted(by: {
if direction == .following {
return $0.shortUid! > $1.shortUid!
}
return $0.shortUid! < $1.shortUid!
}).first?.shortUid?.toString(),
let direction {
paginationInfo = PaginationInfo(offsetUid: offset, direction: direction)
}

let messageUidsResult = try await apiFetcher.messagesUids(
mailboxUuid: mailbox.uuid,
folderId: folder.id,
offset: offset
paginationInfo: paginationInfo
)
let messagesUids = MessagesUids(
addedShortUids: messageUidsResult.messageShortUids,
Expand All @@ -717,12 +730,18 @@ public class MailboxManager: ObservableObject {

try await handleMessagesUids(messageUids: messagesUids, folder: folder)

guard paginationInfo != nil else {
return messagesUids.addedShortUids.count > Constants.pageSize
}

if messagesUids.addedShortUids.count < Constants.pageSize || messagesUids.addedShortUids.contains("1") {
await backgroundRealm.execute { realm in
let freshFolder = folder.fresh(using: realm)
try? realm.safeWrite {
freshFolder?.isHistoryComplete = true
freshFolder?.remainingOldMessagesToFetch = 0
if direction == .previous {
await backgroundRealm.execute { realm in
let freshFolder = folder.fresh(using: realm)
try? realm.safeWrite {
freshFolder?.isHistoryComplete = true
freshFolder?.remainingOldMessagesToFetch = 0
}
}
}
return false
Expand All @@ -739,68 +758,58 @@ public class MailboxManager: ObservableObject {
private func addMessages(shortUids: [String], folder: Folder, newCursor: String?) async throws {
guard !shortUids.isEmpty && !Task.isCancelled else { return }

let reversedUids: [String] = getUniqueUidsInReverse(folder: folder, remoteUids: shortUids)
let pageSize = 50
var offset = 0
while offset < reversedUids.count && !Task.isCancelled {
let end = min(offset + pageSize, reversedUids.count)
let newList = Array(reversedUids[offset ..< end])
let messageByUidsResult = try await apiFetcher.messagesByUids(
mailboxUuid: mailbox.uuid,
folderId: folder.id,
messageUids: newList
)
let uniqueUids: [String] = getUniqueUids(folder: folder, remoteUids: shortUids)
let messageByUidsResult = try await apiFetcher.messagesByUids(
mailboxUuid: mailbox.uuid,
folderId: folder.id,
messageUids: uniqueUids
)

await backgroundRealm.execute { [self] realm in
if let folder = folder.fresh(using: realm) {
createMultiMessagesThreads(messageByUids: messageByUidsResult, folder: folder, using: realm)
}
SentryDebug.sendMissingMessagesSentry(
sentUids: newList,
receivedMessages: messageByUidsResult.messages,
folder: folder,
newCursor: newCursor
)
await backgroundRealm.execute { [self] realm in
if let folder = folder.fresh(using: realm) {
createMultiMessagesThreads(messageByUids: messageByUidsResult, folder: folder, using: realm)
}

offset += pageSize
SentryDebug.sendMissingMessagesSentry(
sentUids: uniqueUids,
receivedMessages: messageByUidsResult.messages,
folder: folder,
newCursor: newCursor
)
}
}

private func createMultiMessagesThreads(messageByUids: MessageByUidsResult, folder: Folder, using realm: Realm) {
var threadsToUpdate = Set<Thread>()
try? realm.safeWrite {
for message in messageByUids.messages {
if realm.object(ofType: Message.self, forPrimaryKey: message.uid) == nil {
message.inTrash = folder.role == .trash
message.computeReference()
let existingThreads = Array(realm.objects(Thread.self)
.where { $0.messageIds.containsAny(in: message.linkedUids) })

if let newThread = createNewThreadIfRequired(
for: message,
folder: folder,
existingThreads: existingThreads
) {
threadsToUpdate.insert(newThread)
}
message.inTrash = folder.role == .trash
message.computeReference()
let existingThreads = Array(realm.objects(Thread.self)
.where { $0.messageIds.containsAny(in: message.linkedUids) })

var allExistingMessages = Set(existingThreads.flatMap(\.messages))
allExistingMessages.insert(message)
if let newThread = createNewThreadIfRequired(
for: message,
folder: folder,
existingThreads: existingThreads
) {
threadsToUpdate.insert(newThread)
}

for thread in existingThreads {
for existingMessage in allExistingMessages {
if !thread.messages.map(\.uid).contains(existingMessage.uid) {
thread.addMessageIfNeeded(newMessage: message.fresh(using: realm) ?? message)
}
}
var allExistingMessages = Set(existingThreads.flatMap(\.messages))
allExistingMessages.insert(message)

threadsToUpdate.insert(thread)
for thread in existingThreads {
for existingMessage in allExistingMessages {
if !thread.messages.map(\.uid).contains(existingMessage.uid) {
thread.addMessageIfNeeded(newMessage: message.fresh(using: realm) ?? message)
}
}

if let message = realm.objects(Message.self).first(where: { $0.uid == message.uid }) {
folder.messages.insert(message)
}
threadsToUpdate.insert(thread)
}

if let message = realm.objects(Message.self).first(where: { $0.uid == message.uid }) {
folder.messages.insert(message)
}
}
self.updateThreads(threads: threadsToUpdate)
Expand Down
10 changes: 10 additions & 0 deletions MailCore/Models/Message.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ import Foundation
import MailResources
import RealmSwift

public enum NewMessagesDirection: String {
case previous
case following
}

public struct PaginationInfo {
let offsetUid: String
let direction: NewMessagesDirection
}

public class MessageUidsResult: Decodable {
public let messageShortUids: [String]
public let cursor: String
Expand Down
6 changes: 6 additions & 0 deletions MailCore/Utils/Array+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,9 @@ public extension LazyMapSequence {
return Array(self)
}
}

public extension Set {
func toArray() -> [Element] {
return Array(self)
}
}

0 comments on commit 22d3205

Please sign in to comment.