Skip to content

Commit

Permalink
Add ConversationListModel
Browse files Browse the repository at this point in the history
- 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>
  • Loading branch information
3 people committed Feb 10, 2025
1 parent 7da9144 commit 76824b8
Show file tree
Hide file tree
Showing 15 changed files with 1,760 additions and 166 deletions.
29 changes: 25 additions & 4 deletions src/common/misc/DeviceConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ export enum ListAutoSelectBehavior {
OLDER,
NEWER,
}
export const enum MailListDisplayMode {
CONVERSATIONS = "conversations",
MAILS = "mails",
}

export type LastExternalCalendarSyncEntry = {
lastSuccessfulSync: number | undefined | null
Expand Down Expand Up @@ -53,6 +57,7 @@ interface ConfigObject {
_testAssignments: PersistedAssignmentData | null
offlineTimeRangeDaysByUser: Record<Id, number>
conversationViewShowOnlySelectedMail: boolean
mailListDisplayMode: MailListDisplayMode
/** Stores each users' definition about contact synchronization */
syncContactsWithPhonePreference: Record<Id, boolean>
/** Whether mobile calendar navigation is in the "per week" or "per month" mode */
Expand Down Expand Up @@ -87,13 +92,13 @@ interface ConfigObject {
* Device config for internal user auto login. Only one config per device is stored.
*/
export class DeviceConfig implements UsageTestStorage, NewsItemStorage {
public static Version = 4
public static LocalStorageKey = "tutanotaConfig"
public static readonly Version = 5
public static readonly LocalStorageKey = "tutanotaConfig"

private config!: ConfigObject
private lastSyncStream: Stream<Map<Id, LastExternalCalendarSyncEntry>> = stream(new Map())

constructor(private readonly _version: number, private readonly localStorage: Storage | null) {
constructor(private readonly localStorage: Storage | null) {
this.init()
}

Expand Down Expand Up @@ -134,6 +139,7 @@ export class DeviceConfig implements UsageTestStorage, NewsItemStorage {
_signupToken: signupToken,
offlineTimeRangeDaysByUser: loadedConfig.offlineTimeRangeDaysByUser ?? {},
conversationViewShowOnlySelectedMail: loadedConfig.conversationViewShowOnlySelectedMail ?? false,
mailListDisplayMode: loadedConfig.mailListDisplayMode ?? MailListDisplayMode.CONVERSATIONS,
syncContactsWithPhonePreference: loadedConfig.syncContactsWithPhonePreference ?? {},
isCalendarDaySelectorExpanded: loadedConfig.isCalendarDaySelectorExpanded ?? false,
mailAutoSelectBehavior: loadedConfig.mailAutoSelectBehavior ?? (isApp() ? ListAutoSelectBehavior.NONE : ListAutoSelectBehavior.OLDER),
Expand Down Expand Up @@ -398,6 +404,15 @@ export class DeviceConfig implements UsageTestStorage, NewsItemStorage {
this.writeToStorage()
}

getMailListDisplayMode(): MailListDisplayMode {
return this.config.mailListDisplayMode
}

setMailListDisplayMode(setting: MailListDisplayMode) {
this.config.mailListDisplayMode = setting
this.writeToStorage()
}

getUserSyncContactsWithPhonePreference(id: Id): boolean | null {
return this.config.syncContactsWithPhonePreference[id] ?? null
}
Expand Down Expand Up @@ -510,6 +525,12 @@ export function migrateConfig(loadedConfig: any) {
if (loadedConfig._version < 3) {
migrateConfigV2to3(loadedConfig)
}

// version 4 had no migration

if (loadedConfig._version < 5) {
loadedConfig.mailListDisplayMode = MailListDisplayMode.MAILS
}
}

/**
Expand Down Expand Up @@ -558,4 +579,4 @@ export interface DeviceConfigCredentials {
readonly encryptedPassphraseKey: Base64 | null
}

export const deviceConfig: DeviceConfig = new DeviceConfig(DeviceConfig.Version, client.localStorage() ? localStorage : null)
export const deviceConfig: DeviceConfig = new DeviceConfig(client.localStorage() ? localStorage : null)
2 changes: 2 additions & 0 deletions src/common/misc/ListModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,8 @@ export class ListModel<ItemType, IdType> {

canInsertItem(entity: ItemType): boolean {
if (this.state.loadingStatus === ListLoadingState.Done) {
// If the entire list is loaded, it is always safe to add items, because we can assume we have the entire
// range loaded
return true
}

Expand Down
4 changes: 4 additions & 0 deletions src/common/misc/TranslationKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1900,3 +1900,7 @@ export type TranslationKeyType =
| "yourMessage_label"
| "you_label"
| "emptyString_msg"
| "mailListGrouping_label"
| "mailListGroupingDontGroup_label"
| "mailListGroupingGroupByConversation_label"
| "mailListGroupingHelp_msg"
Loading

0 comments on commit 76824b8

Please sign in to comment.