Add ConversationListModel

- This list model groups all emails by conversation in the mail list
- Also adds setting for grouping by conversation (i.e. enables this)
- The setting is overridden for SENT and DRAFT folder types (it uses the
  other per-mail list model instead)
- Adds MailSetListModel which provides a common interface for the
  MailViewModel so that additional list models can be created; it is
  used for MailListModel and the new ConversationListModel, but it can
  be extended to even more list models in the future if it is ever
  desired.

Note: This does not include actions. Any action you do (delete, archive,
etc.) will be applied to the selected email. The purpose of this commit
is to just implement the list model. See #5051 for more information.

Closes #8223

Co-authored-by: bir <bir@tutao.de>
Co-authored-by: ivk <ivk@tutao.de>
This commit is contained in:
paw 2025-01-22 15:33:46 +01:00 committed by paw
parent 0e06f427d2
commit 0321b53e3e
13 changed files with 1756 additions and 163 deletions

View file

@ -1,5 +1,5 @@
import o from "../../../../packages/otest/dist/otest"
import { LoadedMail, MailListModel } from "../../../../src/mail-app/mail/model/MailListModel"
import { MailListModel } from "../../../../src/mail-app/mail/model/MailListModel"
import {
createMailSetEntry,
Mail,
@ -27,6 +27,7 @@ import {
getElementId,
getListId,
isSameId,
listIdPart,
} from "../../../../src/common/api/common/utils/EntityUtils"
import { PageSize } from "../../../../src/common/gui/base/ListUtils"
import { createTestEntity } from "../../TestUtils"
@ -36,6 +37,7 @@ import { MailboxDetail } from "../../../../src/common/mailFunctionality/MailboxM
import { GroupInfoTypeRef, GroupTypeRef } from "../../../../src/common/api/entities/sys/TypeRefs"
import { ConnectionError } from "../../../../src/common/api/common/error/RestError"
import { clamp, pad } from "@tutao/tutanota-utils"
import { LoadedMail } from "../../../../src/mail-app/mail/model/MailSetListModel"
o.spec("MailListModelTest", () => {
let model: MailListModel
@ -153,8 +155,8 @@ o.spec("MailListModelTest", () => {
}
endingIndex = clamp(endingIndex, 0, mailSetEntries.length)
let startingIndex = clamp(endingIndex - count, 0, endingIndex)
return mailSetEntries.slice(startingIndex, endingIndex)
const startingIndex = clamp(endingIndex - count, 0, endingIndex)
return mailSetEntries.slice(startingIndex, endingIndex).reverse()
}
async function getMailsMock(_mailTypeRef: any, mailBag: string, elements: Id[]): Promise<Mail[]> {
@ -249,19 +251,19 @@ o.spec("MailListModelTest", () => {
const pages = 5
await setUpTestData(PageSize * pages, labels, false)
await model.loadInitial() // will have the first page loaded
const unloadedMail = makeMailSetElementId(1) // a mail that will be on the bottom of the list
const unloadedMail = elementIdPart(makeMailId(1)) // a mail that will be on the bottom of the list
// This mail is not loaded until we load a few more pages.
for (let loadedPageCount = 1; loadedPageCount < pages; loadedPageCount++) {
o(model.items.length).equals(PageSize * loadedPageCount)
const mail = model.getMailSetEntry(unloadedMail)
const mail = model.getMail(unloadedMail)
o(mail).equals(null)
await model.loadMore()
}
// Everything is loaded including that mail we wanted from before
o(model.items.length).equals(PageSize * pages)
const mail = model.getMailSetEntry(unloadedMail)
const mail = model.getMail(unloadedMail)
o(mail).notEquals(null)
})
@ -347,8 +349,8 @@ o.spec("MailListModelTest", () => {
const entityUpdateData = {
application: MailSetEntryTypeRef.app,
type: MailSetEntryTypeRef.type,
instanceListId: getListId(someMail.mailSetEntry),
instanceId: getElementId(someMail.mailSetEntry),
instanceListId: listIdPart(someMail.mailSetEntryId),
instanceId: elementIdPart(someMail.mailSetEntryId),
operation: OperationType.DELETE,
}