Skip to content

Commit

Permalink
Added updating the thread details during swipe-to-refresh and other u…
Browse files Browse the repository at this point in the history
…pdates.| #74
  • Loading branch information
DenBond7 committed Nov 14, 2024
1 parent efdfa89 commit 08cca9b
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,9 @@ import com.flowcrypt.email.database.entity.AccountEntity
import com.flowcrypt.email.database.entity.AttachmentEntity
import com.flowcrypt.email.database.entity.MessageEntity
import com.flowcrypt.email.extensions.com.google.api.services.gmail.model.disposition
import com.flowcrypt.email.extensions.com.google.api.services.gmail.model.extractSubject
import com.flowcrypt.email.extensions.com.google.api.services.gmail.model.getAttachmentInfoList
import com.flowcrypt.email.extensions.com.google.api.services.gmail.model.getDraftsCount
import com.flowcrypt.email.extensions.com.google.api.services.gmail.model.getUniqueLabelsSet
import com.flowcrypt.email.extensions.com.google.api.services.gmail.model.getUniqueRecipients
import com.flowcrypt.email.extensions.com.google.api.services.gmail.model.hasAttachments
import com.flowcrypt.email.extensions.com.google.api.services.gmail.model.hasPgp
import com.flowcrypt.email.extensions.com.google.api.services.gmail.model.hasUnreadMessages
import com.flowcrypt.email.extensions.com.google.api.services.gmail.model.isMimeType
import com.flowcrypt.email.extensions.com.google.api.services.gmail.model.toThreadInfo
import com.flowcrypt.email.extensions.java.lang.printStackTraceIfDebugOnly
import com.flowcrypt.email.extensions.uid
import com.flowcrypt.email.model.KeyImportDetails
Expand Down Expand Up @@ -419,8 +413,7 @@ class GmailApiHelper {
responseHeaders: HttpHeaders?
) {
t?.let { thread ->
val gmailThreadInfo = extractGmailThreadInfo(accountEntity, thread, context)
listResult.add(gmailThreadInfo)
listResult.add(thread.toThreadInfo(context, accountEntity))
}
}

Expand All @@ -442,39 +435,26 @@ class GmailApiHelper {
format: String = RESPONSE_FORMAT_FULL,
metadataHeaders: List<String>? = null,
fields: List<String>? = null
): GmailThreadInfo = withContext(Dispatchers.IO)
): GmailThreadInfo? = withContext(Dispatchers.IO)
{
val gmailApiService = generateGmailApiService(context, accountEntity)

val request = gmailApiService
.users()
.threads()
.get(DEFAULT_USER_ID, threadId)
.setFormat(format)

metadataHeaders?.let { metadataHeaders ->
request.metadataHeaders = metadataHeaders
}

fields?.let { fields ->
request.fields = fields.joinToString(separator = ",")
}

val thread = request.execute()
val gmailThreadInfo = extractGmailThreadInfo(accountEntity, thread, context)

return@withContext gmailThreadInfo
return@withContext getThread(
context = context,
accountEntity = accountEntity,
threadId = threadId,
format = format,
metadataHeaders = metadataHeaders,
fields = fields
)?.toThreadInfo(context, accountEntity)
}

suspend fun loadMessagesInThread(
suspend fun getThread(
context: Context,
accountEntity: AccountEntity,
threadId: String,
format: String = RESPONSE_FORMAT_FULL,
metadataHeaders: List<String>? = null,
fields: List<String>? = null
): List<Message> = withContext(Dispatchers.IO)
{
): Thread? = withContext(Dispatchers.IO) {
val gmailApiService = generateGmailApiService(context, accountEntity)

val request = gmailApiService
Expand All @@ -491,7 +471,7 @@ class GmailApiHelper {
request.fields = fields.joinToString(separator = ",")
}

return@withContext request.execute()?.messages ?: emptyList()
return@withContext request.execute()
}

suspend fun loadMsgs(
Expand Down Expand Up @@ -1291,29 +1271,5 @@ class GmailApiHelper {
} ?: e
}
}

private fun extractGmailThreadInfo(
accountEntity: AccountEntity,
thread: Thread,
context: Context
): GmailThreadInfo {
val receiverEmail = accountEntity.email
val gmailThreadInfo = GmailThreadInfo(
id = thread.id,
lastMessage = requireNotNull(
thread.messages?.lastOrNull {
!it.labelIds.contains(LABEL_DRAFT)
} ?: thread.messages.first()),
messagesCount = thread.messages?.size ?: 0,
draftsCount = thread.getDraftsCount(),
recipients = thread.getUniqueRecipients(receiverEmail),
subject = thread.extractSubject(context, receiverEmail),
labels = thread.getUniqueLabelsSet(),
hasAttachments = thread.hasAttachments(),
hasPgpThings = thread.hasPgp(),
hasUnreadMessages = thread.hasUnreadMessages()
)
return gmailThreadInfo
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import com.flowcrypt.email.api.email.gmail.GmailApiHelper.Companion.LABEL_TRASH
import com.flowcrypt.email.api.email.gmail.GmailApiHelper.Companion.labelsToImapFlags
import com.flowcrypt.email.api.email.gmail.api.GmaiAPIMimeMessage
import com.flowcrypt.email.api.email.model.LocalFolder
import com.flowcrypt.email.api.email.model.MessageFlag
import com.flowcrypt.email.database.FlowCryptRoomDatabase
import com.flowcrypt.email.database.entity.AccountEntity
import com.flowcrypt.email.database.entity.MessageEntity
Expand Down Expand Up @@ -182,9 +181,7 @@ object GmailHistoryHandler {
threadDraftsCount = thread.draftsCount,
labelIds = thread.labels.joinToString(separator = LABEL_IDS_SEPARATOR),
hasAttachments = thread.hasAttachments,
fromAddresses = InternetAddress.toString(
thread.recipients.toTypedArray()
),
fromAddresses = InternetAddress.toString(thread.recipients.toTypedArray()),
hasPgp = thread.hasPgpThings
)
}
Expand All @@ -211,26 +208,7 @@ object GmailHistoryHandler {
onlyPgpModeEnabled = accountEntity.showOnlyEncrypted ?: false
) { message, messageEntity ->
if (message.threadId == thread.id) {
messageEntity.copy(
id = threadMessageEntity.id,
uid = threadMessageEntity.uid,
subject = thread.subject,
threadMessagesCount = thread.messagesCount,
threadDraftsCount = thread.draftsCount,
labelIds = thread.labels.joinToString(separator = LABEL_IDS_SEPARATOR),
hasAttachments = thread.hasAttachments,
fromAddresses = InternetAddress.toString(thread.recipients.toTypedArray()),
hasPgp = thread.hasPgpThings,
flags = if (thread.hasUnreadMessages) {
threadMessageEntity.flags?.replace(MessageFlag.SEEN.value, "")
} else {
if (threadMessageEntity.flags?.contains(MessageFlag.SEEN.value) == true) {
threadMessageEntity.flags
} else {
threadMessageEntity.flags.plus("${MessageFlag.SEEN.value} ")
}
}
)
messageEntity.toUpdatedThreadCopy(threadMessageEntity, thread)
} else messageEntity
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import com.flowcrypt.email.api.email.EmailUtil
import com.flowcrypt.email.api.email.FoldersManager
import com.flowcrypt.email.api.email.JavaEmailConstants
import com.flowcrypt.email.api.email.gmail.api.GmaiAPIMimeMessage
import com.flowcrypt.email.api.email.gmail.model.GmailThreadInfo
import com.flowcrypt.email.api.email.model.MessageFlag
import com.flowcrypt.email.api.email.model.OutgoingMessageInfo
import com.flowcrypt.email.database.MessageState
Expand Down Expand Up @@ -460,6 +461,29 @@ data class MessageEntity(
} else charSequence
}

fun toUpdatedThreadCopy(messageEntity: MessageEntity, thread: GmailThreadInfo): MessageEntity {
return copy(
id = messageEntity.id,
uid = messageEntity.uid,
subject = thread.subject,
threadMessagesCount = thread.messagesCount,
threadDraftsCount = thread.draftsCount,
labelIds = thread.labels.joinToString(separator = LABEL_IDS_SEPARATOR),
hasAttachments = thread.hasAttachments,
fromAddresses = InternetAddress.toString(thread.recipients.toTypedArray()),
hasPgp = thread.hasPgpThings,
flags = if (thread.hasUnreadMessages) {
messageEntity.flags?.replace(MessageFlag.SEEN.value, "")
} else {
if (messageEntity.flags?.contains(MessageFlag.SEEN.value) == true) {
messageEntity.flags
} else {
messageEntity.flags.plus("${MessageFlag.SEEN.value} ")
}
}
)
}

private fun prepareDateHeaderValue(context: Context): String {
val dateInMilliseconds: Long =
if (JavaEmailConstants.FOLDER_OUTBOX.equals(folder, ignoreCase = true)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ package com.flowcrypt.email.extensions.com.google.api.services.gmail.model
import android.content.Context
import com.flowcrypt.email.R
import com.flowcrypt.email.api.email.gmail.GmailApiHelper
import com.flowcrypt.email.api.email.gmail.GmailApiHelper.Companion.LABEL_DRAFT
import com.flowcrypt.email.api.email.gmail.model.GmailThreadInfo
import com.flowcrypt.email.database.entity.AccountEntity
import com.flowcrypt.email.extensions.kotlin.asInternetAddresses
import com.google.api.services.gmail.model.Thread
import jakarta.mail.internet.InternetAddress
Expand Down Expand Up @@ -56,7 +59,7 @@ fun Thread.getUniqueLabelsSet(): Set<String> {
}

fun Thread.getDraftsCount(): Int {
return messages?.filter { it.labelIds.contains(GmailApiHelper.LABEL_DRAFT) }?.size ?: 0
return messages?.filter { it.labelIds.contains(LABEL_DRAFT) }?.size ?: 0
}

fun Thread.hasUnreadMessages(): Boolean {
Expand Down Expand Up @@ -90,4 +93,27 @@ fun Thread.extractSubject(context: Context, receiverEmail: String): String {
}?.getSubject()
?: messages?.getOrNull(0)?.getSubject()
?: context.getString(R.string.no_subject)
}

fun Thread.toThreadInfo(
context: Context,
accountEntity: AccountEntity
): GmailThreadInfo {
val receiverEmail = accountEntity.email
val gmailThreadInfo = GmailThreadInfo(
id = id,
lastMessage = requireNotNull(
messages?.lastOrNull {
!it.labelIds.contains(LABEL_DRAFT)
} ?: messages.first()),
messagesCount = messages?.size ?: 0,
draftsCount = getDraftsCount(),
recipients = getUniqueRecipients(receiverEmail),
subject = extractSubject(context, receiverEmail),
labels = getUniqueLabelsSet(),
hasAttachments = hasAttachments(),
hasPgpThings = hasPgp(),
hasUnreadMessages = hasUnreadMessages()
)
return gmailThreadInfo
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.flowcrypt.email.api.email.FoldersManager
import com.flowcrypt.email.api.email.JavaEmailConstants
import com.flowcrypt.email.api.email.MsgsCacheManager
import com.flowcrypt.email.api.email.gmail.GmailApiHelper
import com.flowcrypt.email.api.email.gmail.model.GmailThreadInfo
import com.flowcrypt.email.api.email.model.LocalFolder
import com.flowcrypt.email.api.email.model.MessageFlag
import com.flowcrypt.email.api.retrofit.response.base.Result
Expand All @@ -20,6 +21,7 @@ import com.flowcrypt.email.database.entity.MessageEntity
import com.flowcrypt.email.extensions.com.google.api.services.gmail.model.getInReplyTo
import com.flowcrypt.email.extensions.com.google.api.services.gmail.model.getMessageId
import com.flowcrypt.email.extensions.com.google.api.services.gmail.model.isDraft
import com.flowcrypt.email.extensions.com.google.api.services.gmail.model.toThreadInfo
import com.flowcrypt.email.extensions.java.lang.printStackTraceIfDebugOnly
import com.flowcrypt.email.model.MessageAction
import com.flowcrypt.email.ui.adapter.MessagesInThreadListAdapter
Expand Down Expand Up @@ -369,15 +371,32 @@ class ThreadDetailsViewModel(
if (threadMessageEntity.threadIdAsHEX.isNullOrEmpty() || !activeAccount.isGoogleSignInAccount) {
return Result.success(Data(silentUpdate = silentUpdate, list = listOf()))
} else {
val threadHeader =
prepareThreadHeader(roomDatabase.msgDao().getMsgById(threadMessageEntityId))

try {
val messagesInThread = GmailApiHelper.loadMessagesInThread(
getApplication(),
activeAccount,
threadMessageEntity.threadIdAsHEX
).toMutableList().apply {
val thread = GmailApiHelper.getThread(
context = getApplication(),
accountEntity = activeAccount,
threadId = threadMessageEntity.threadIdAsHEX,
format = GmailApiHelper.RESPONSE_FORMAT_FULL
) ?: error("Thread not found")

val threadInfo = thread.toThreadInfo(getApplication(), activeAccount)
//keep thread info updated in the local cache
roomDatabase.msgDao().updateSuspend(
MessageEntity.genMessageEntities(
context = getApplication(),
account = activeAccount.email,
accountType = activeAccount.accountType,
label = localFolder.fullName,
msgsList = listOf(threadInfo.lastMessage),
isNew = false,
onlyPgpModeEnabled = activeAccount.showOnlyEncrypted ?: false
) { message, messageEntity ->
if (message.threadId == threadMessageEntity.threadIdAsHEX) {
messageEntity.toUpdatedThreadCopy(threadMessageEntity, threadInfo)
} else messageEntity
})

val messagesInThread = (thread.messages ?: emptyList()).toMutableList().apply {
//try to put drafts in the right position
val drafts = filter { it.isDraft() }
drafts.forEach { draft ->
Expand Down Expand Up @@ -406,10 +425,6 @@ class ThreadDetailsViewModel(
).associateBy({ it.message.id }, { it.id })
} else emptyMap()

//update the actual thread size
roomDatabase.msgDao()
.updateSuspend(threadMessageEntity.copy(threadMessagesCount = messagesInThread.size))

val isOnlyPgpModeEnabled = activeAccount.showOnlyEncrypted ?: false
val messageEntitiesBasedOnServerResult = MessageEntity.genMessageEntities(
context = getApplication(),
Expand Down Expand Up @@ -503,6 +518,11 @@ class ThreadDetailsViewModel(
} ?: messageItemBasedOnDataFromServer
}

val threadHeader = prepareThreadHeader(
messageEntity = roomDatabase.msgDao().getMsgById(threadMessageEntityId),
threadInfo = threadInfo
)

return Result.success(
Data(
silentUpdate = silentUpdate,
Expand All @@ -516,7 +536,10 @@ class ThreadDetailsViewModel(
}
}

private suspend fun prepareThreadHeader(messageEntity: MessageEntity?): MessagesInThreadListAdapter.ThreadHeader =
private suspend fun prepareThreadHeader(
messageEntity: MessageEntity?,
threadInfo: GmailThreadInfo? = null
): MessagesInThreadListAdapter.ThreadHeader =
withContext(Dispatchers.IO) {
val account =
getActiveAccountSuspend() ?: return@withContext MessagesInThreadListAdapter.ThreadHeader(
Expand All @@ -530,13 +553,13 @@ class ThreadDetailsViewModel(

return@withContext try {
//try to get the last changes from a server
val latestLabelIds = GmailApiHelper.loadThreadInfo(
val latestLabelIds = (threadInfo ?: GmailApiHelper.loadThreadInfo(
context = getApplication(),
accountEntity = account,
threadId = messageEntity?.threadIdAsHEX ?: "",
fields = listOf("id", "messages/labelIds"),
format = GmailApiHelper.RESPONSE_FORMAT_MINIMAL
).labels
))?.labels ?: error("Thread not found")
if (cachedLabelIds == null
|| !(latestLabelIds.containsAll(cachedLabelIds)
&& cachedLabelIds.containsAll(latestLabelIds))
Expand Down

0 comments on commit 08cca9b

Please sign in to comment.