Skip to content

Commit 130ea85

Browse files
Merge pull request #1731 from Infomaniak/optimize-print
Optimize opening of Thread to handle both ThreadFragment & PrintFragment
2 parents 0460ad1 + 75474ca commit 130ea85

File tree

7 files changed

+114
-82
lines changed

7 files changed

+114
-82
lines changed

.idea/navEditor.xml

Lines changed: 17 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/src/main/java/com/infomaniak/mail/data/cache/mailboxContent/MessageController.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ class MessageController @Inject constructor(private val mailboxContentRealm: Rea
4444
?.messages?.query("${Message::isDeletedOnApi.name} == false")
4545
?.sort(Message::date.name, Sort.ASCENDING)
4646
}
47-
4847
//endregion
4948

5049
//region Get data
@@ -134,6 +133,10 @@ class MessageController @Inject constructor(private val mailboxContentRealm: Rea
134133
fun getSortedAndNotDeletedMessagesAsync(threadUid: String): Flow<ResultsChange<Message>>? {
135134
return getSortedAndNotDeletedMessagesQuery(threadUid)?.asFlow()
136135
}
136+
137+
fun getMessagesAsync(messageUid: String): Flow<ResultsChange<Message>> {
138+
return getMessagesQuery(messageUid, mailboxContentRealm()).asFlow()
139+
}
137140
//endregion
138141

139142
//region Edit data
@@ -162,8 +165,12 @@ class MessageController @Inject constructor(private val mailboxContentRealm: Rea
162165
.limit(fibonacci)
163166
}
164167

168+
private fun getMessagesQuery(messageUid: String, realm: TypedRealm): RealmQuery<Message> {
169+
return realm.query<Message>("${Message::uid.name} == $0", messageUid)
170+
}
171+
165172
private fun getMessageQuery(uid: String, realm: TypedRealm): RealmSingleQuery<Message> {
166-
return realm.query<Message>("${Message::uid.name} == $0", uid).first()
173+
return getMessagesQuery(uid, realm).first()
167174
}
168175
//endregion
169176

app/src/main/java/com/infomaniak/mail/data/models/message/Message.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ class Message : RealmObject {
300300
fun shouldBeExpanded(index: Int, lastIndex: Int) = !isDraft && (!isSeen || index == lastIndex)
301301

302302
fun toThread() = Thread().apply {
303-
uid = this@Message.uid
303+
uid = this@Message.uid // TODO: Check if we can use random UUID instead ?
304304
folderId = this@Message.folderId
305305
messagesIds += this@Message.messageIds
306306
messages += this@Message

app/src/main/java/com/infomaniak/mail/ui/main/thread/PrintMailFragment.kt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,16 @@ class PrintMailFragment : Fragment() {
5353
return FragmentPrintMailBinding.inflate(inflater, container, false).also { binding = it }.root
5454
}
5555

56-
override fun onViewCreated(view: View, savedInstanceState: Bundle?): Unit = with(threadViewModel) {
56+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
5757
super.onViewCreated(view, savedInstanceState)
5858

5959
setupAdapter()
6060

61-
messagesLive.observe(viewLifecycleOwner) { (items, _) ->
62-
threadAdapter.submitList(listOf(items.single { it is Message && it.uid == navigationArgs.messageUid }))
61+
threadViewModel.messagesLive.observe(viewLifecycleOwner) { (items, _) ->
62+
threadAdapter.submitList(items)
6363
}
6464

65-
navigationArgs.openThreadUid?.let {
66-
reassignMessagesLive(it, withSuperCollapsedBlock = false)
67-
}
65+
threadViewModel.reassignMessagesLiveWithoutSuperCollapsedBlock(navigationArgs.messageUid)
6866
}
6967

7068
private fun setupAdapter() {

app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadViewModel.kt

Lines changed: 80 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import io.realm.kotlin.query.RealmResults
4747
import io.sentry.Sentry
4848
import io.sentry.SentryLevel
4949
import kotlinx.coroutines.*
50+
import kotlinx.coroutines.flow.Flow
5051
import kotlinx.coroutines.flow.map
5152
import javax.inject.Inject
5253
import kotlin.collections.set
@@ -128,95 +129,78 @@ class ThreadViewModel @Inject constructor(
128129
}
129130
}
130131

131-
fun reassignMessagesLive(threadUid: String, withSuperCollapsedBlock: Boolean = true) {
132+
fun reassignMessagesLive(threadUid: String) {
133+
reassignMessages {
134+
messageController.getSortedAndNotDeletedMessagesAsync(threadUid)?.map { mapRealmMessagesResult(it.list, threadUid) }
135+
}
136+
}
137+
138+
fun reassignMessagesLiveWithoutSuperCollapsedBlock(messageUid: String) {
139+
reassignMessages {
140+
messageController.getMessagesAsync(messageUid).map { mapRealmMessagesResultWithoutSuperCollapsedBlock(it.list) }
141+
}
142+
}
143+
144+
private fun reassignMessages(messagesFlow: (() -> Flow<Pair<ThreadAdapterItems, MessagesWithoutHeavyData>>?)) {
132145
messagesLiveJob?.cancel()
133146
messagesLiveJob = viewModelScope.launch(ioCoroutineContext) {
134-
messageController.getSortedAndNotDeletedMessagesAsync(threadUid)
135-
?.map { mapRealmMessagesResult(it.list, threadUid, withSuperCollapsedBlock) }
136-
?.collect(messagesLive::postValue)
147+
messagesFlow()?.collect(messagesLive::postValue)
137148
}
138149
}
139150

140151
private suspend fun mapRealmMessagesResult(
141152
messages: RealmResults<Message>,
142153
threadUid: String,
143-
withSuperCollapsedBlock: Boolean,
144154
): Pair<ThreadAdapterItems, MessagesWithoutHeavyData> {
145155

146156
superCollapsedBlock = superCollapsedBlock ?: SuperCollapsedBlock()
147157

148-
val items = mutableListOf<Any>()
149-
val messagesToFetch = mutableListOf<Message>()
150-
val thread = messages.firstOrNull()?.threads?.firstOrNull { it.uid == threadUid } ?: return items to messagesToFetch
158+
val thread = messages.first().threads.single { it.uid == threadUid }
151159
val firstIndexAfterBlock = computeFirstIndexAfterBlock(thread, messages)
152-
superCollapsedBlock!!.shouldBeDisplayed =
153-
shouldBlockBeDisplayed(messages.count(), firstIndexAfterBlock, withSuperCollapsedBlock)
160+
superCollapsedBlock!!.shouldBeDisplayed = shouldBlockBeDisplayed(messages.count(), firstIndexAfterBlock)
154161

155-
suspend fun addMessage(message: Message) {
156-
splitBody(message).let {
157-
items += it
158-
if (!it.isFullyDownloaded()) messagesToFetch += it
159-
}
160-
}
161-
162-
suspend fun mapListWithNewBlock() {
163-
messages.forEachIndexed { index, message ->
162+
suspend fun formatListWithNewBlock(): Pair<ThreadAdapterItems, MessagesWithoutHeavyData> {
163+
return formatLists(messages) { index, _ ->
164164
when (index) {
165-
0 -> { // First Message
166-
addMessage(message)
167-
}
168-
in 1..<firstIndexAfterBlock -> { // All Messages that should go in block
169-
superCollapsedBlock!!.messagesUids.add(message.uid)
170-
}
171-
firstIndexAfterBlock -> { // First Message not in block
172-
items += superCollapsedBlock!!
173-
addMessage(message.apply { shouldHideDivider = true })
174-
}
175-
else -> { // All following Messages
176-
addMessage(message)
177-
}
165+
0 -> MessageBehavior.DISPLAYED // First Message
166+
in 1 until firstIndexAfterBlock -> MessageBehavior.COLLAPSED // All Messages that should go in block
167+
firstIndexAfterBlock -> MessageBehavior.FIRST_AFTER_BLOCK // First Message not in block
168+
else -> MessageBehavior.DISPLAYED // All following Messages
178169
}
179170
}
180171
}
181172

182-
suspend fun mapListWithExistingBlock() {
173+
suspend fun formatListWithExistingBlock(): Pair<ThreadAdapterItems, MessagesWithoutHeavyData> {
183174

184175
var isStillInBlock = true
185176
val previousBlock = superCollapsedBlock!!.messagesUids.toSet()
186177

187178
superCollapsedBlock!!.messagesUids.clear()
188179

189-
messages.forEachIndexed { index, message ->
180+
return formatLists(messages) { index, messageUid ->
190181
when {
191-
index == 0 -> { // First Message
192-
addMessage(message)
193-
}
194-
previousBlock.contains(message.uid) && isStillInBlock -> { // All Messages already in block
195-
superCollapsedBlock!!.messagesUids.add(message.uid)
196-
}
197-
!previousBlock.contains(message.uid) && isStillInBlock -> { // First Message not in block
182+
index == 0 -> MessageBehavior.DISPLAYED // First Message
183+
previousBlock.contains(messageUid) && isStillInBlock -> MessageBehavior.COLLAPSED // All Messages already in block
184+
!previousBlock.contains(messageUid) && isStillInBlock -> { // First Message not in block
198185
isStillInBlock = false
199-
items += superCollapsedBlock!!
200-
addMessage(message.apply { shouldHideDivider = true })
201-
}
202-
else -> { // All following Messages
203-
addMessage(message)
186+
MessageBehavior.FIRST_AFTER_BLOCK
204187
}
188+
else -> MessageBehavior.DISPLAYED // All following Messages
205189
}
206190
}
207191
}
208192

209-
suspend fun mapFullList() {
210-
messages.forEach { addMessage(it) }
211-
}
212-
213-
if (superCollapsedBlock!!.shouldBeDisplayed) {
214-
if (superCollapsedBlock!!.isFirstTime()) mapListWithNewBlock() else mapListWithExistingBlock()
193+
return if (superCollapsedBlock!!.shouldBeDisplayed) {
194+
if (superCollapsedBlock!!.isFirstTime()) formatListWithNewBlock() else formatListWithExistingBlock()
215195
} else {
216-
mapFullList()
196+
formatLists(messages) { _, _ -> MessageBehavior.DISPLAYED }
217197
}
198+
}
218199

219-
return items to messagesToFetch
200+
private suspend fun mapRealmMessagesResultWithoutSuperCollapsedBlock(
201+
messages: RealmResults<Message>,
202+
): Pair<ThreadAdapterItems, MessagesWithoutHeavyData> {
203+
return formatLists(messages) { _, _ -> MessageBehavior.DISPLAYED }
220204
}
221205

222206
private fun computeFirstIndexAfterBlock(thread: Thread, list: RealmResults<Message>): Int {
@@ -235,14 +219,47 @@ class ThreadViewModel @Inject constructor(
235219
* - If there's any unread Message in between, it will be displayed (hence, all following Messages will be displayed too).
236220
* After all these Messages are displayed, if there's at least 2 remaining Messages, they're gonna be collapsed in the Block.
237221
*/
238-
private fun shouldBlockBeDisplayed(messagesCount: Int, firstIndexAfterBlock: Int, withSuperCollapsedBlock: Boolean): Boolean {
239-
return withSuperCollapsedBlock && // When we want to print a mail, we need the full list of Messages
240-
superCollapsedBlock?.shouldBeDisplayed == true && // If the Block was hidden for any reason, we mustn't ever display it again
222+
private fun shouldBlockBeDisplayed(messagesCount: Int, firstIndexAfterBlock: Int): Boolean {
223+
return superCollapsedBlock?.shouldBeDisplayed == true && // If the Block was hidden for any reason, we mustn't ever display it again
241224
!hasSuperCollapsedBlockBeenClicked && // Block hasn't been expanded by the user
242225
messagesCount >= SUPER_COLLAPSED_BLOCK_MINIMUM_MESSAGES_LIMIT && // At least 5 Messages in the Thread
243226
firstIndexAfterBlock >= SUPER_COLLAPSED_BLOCK_FIRST_INDEX_LIMIT // At least 2 Messages in the Block
244227
}
245228

229+
// If we add a fourth case in the `when`, don't forget to add a fourth 'o' in the function name.
230+
private suspend fun formatLists(
231+
messages: List<Message>,
232+
computeBehavior: (Int, String) -> MessageBehavior,
233+
): Pair<MutableList<Any>, MutableList<Message>> {
234+
235+
val items = mutableListOf<Any>()
236+
val messagesToFetch = mutableListOf<Message>()
237+
238+
suspend fun addMessage(message: Message) {
239+
splitBody(message).let {
240+
items += it
241+
if (!it.isFullyDownloaded()) messagesToFetch += it
242+
}
243+
}
244+
245+
messages.forEachIndexed { index, message ->
246+
when (computeBehavior(index, message.uid)) {
247+
MessageBehavior.DISPLAYED -> {
248+
addMessage(message)
249+
}
250+
MessageBehavior.COLLAPSED -> {
251+
superCollapsedBlock!!.messagesUids.add(message.uid)
252+
}
253+
MessageBehavior.FIRST_AFTER_BLOCK -> {
254+
items += superCollapsedBlock!!
255+
addMessage(message.apply { shouldHideDivider = true })
256+
}
257+
}
258+
}
259+
260+
return items to messagesToFetch
261+
}
262+
246263
private suspend fun splitBody(message: Message): Message = withContext(ioDispatcher) {
247264
if (message.body == null) return@withContext message
248265

@@ -437,6 +454,12 @@ class ThreadViewModel @Inject constructor(
437454
val menuId: Int,
438455
)
439456

457+
private enum class MessageBehavior {
458+
DISPLAYED,
459+
COLLAPSED,
460+
FIRST_AFTER_BLOCK,
461+
}
462+
440463
companion object {
441464
private const val SUPER_COLLAPSED_BLOCK_MINIMUM_MESSAGES_LIMIT = 5
442465
private const val SUPER_COLLAPSED_BLOCK_FIRST_INDEX_LIMIT = 3

app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ class MessageActionsBottomSheetDialog : MailActionsBottomSheetDialog() {
156156
trackBottomSheetMessageActionsEvent(ACTION_PRINT_NAME)
157157
safeNavigate(
158158
resId = R.id.printMailFragment,
159-
args = PrintMailFragmentArgs(threadUid, messageUid).toBundle(),
159+
args = PrintMailFragmentArgs(messageUid).toBundle(),
160160
currentClassName = MessageActionsBottomSheetDialog::class.java.name,
161161
)
162162
}

app/src/main/res/navigation/main_navigation.xml

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -573,22 +573,14 @@
573573
android:id="@+id/action_attachmentActionsBottomSheetDialog_to_downloadAttachmentProgressDialog"
574574
app:destination="@id/downloadAttachmentProgressDialog" />
575575
</dialog>
576+
576577
<fragment
577578
android:id="@+id/printMailFragment"
578579
android:name="com.infomaniak.mail.ui.main.thread.PrintMailFragment"
579580
android:label="PrintMailFragment">
580-
581-
<argument
582-
android:name="openThreadUid"
583-
android:defaultValue="@null"
584-
app:argType="string"
585-
app:nullable="true" />
586-
587581
<argument
588582
android:name="messageUid"
589-
android:defaultValue="@null"
590-
app:argType="string"
591-
app:nullable="true" />
583+
app:argType="string" />
592584
</fragment>
593585

594586
<dialog

0 commit comments

Comments
 (0)