2024-07-15 18:08:18 +02:00
|
|
|
import { assertMainOrNode, isAndroidApp, isApp, isBrowser, isDesktop, isElectronClient, isIOSApp, isTest } from "../common/api/common/Env.js"
|
2024-07-10 16:40:04 +02:00
|
|
|
import { EventController } from "../common/api/main/EventController.js"
|
|
|
|
|
import { SearchModel } from "./search/model/SearchModel.js"
|
2024-08-07 16:29:40 +02:00
|
|
|
import { type MailboxDetail, MailboxModel } from "../common/mailFunctionality/MailboxModel.js"
|
2024-07-10 16:40:04 +02:00
|
|
|
import { MinimizedMailEditorViewModel } from "./mail/model/MinimizedMailEditorViewModel.js"
|
|
|
|
|
import { ContactModel } from "../common/contactsFunctionality/ContactModel.js"
|
|
|
|
|
import { EntityClient } from "../common/api/common/EntityClient.js"
|
|
|
|
|
import { ProgressTracker } from "../common/api/main/ProgressTracker.js"
|
|
|
|
|
import { CredentialsProvider } from "../common/misc/credentials/CredentialsProvider.js"
|
|
|
|
|
import { bootstrapWorker, WorkerClient } from "../common/api/main/WorkerClient.js"
|
2024-11-19 15:42:39 +01:00
|
|
|
import { CALENDAR_MIME_TYPE, FileController, guiDownload, MAIL_MIME_TYPES, VCARD_MIME_TYPES } from "../common/file/FileController.js"
|
2024-07-10 16:40:04 +02:00
|
|
|
import { SecondFactorHandler } from "../common/misc/2fa/SecondFactorHandler.js"
|
|
|
|
|
import { WebauthnClient } from "../common/misc/2fa/webauthn/WebauthnClient.js"
|
|
|
|
|
import { LoginFacade } from "../common/api/worker/facades/LoginFacade.js"
|
|
|
|
|
import { LoginController } from "../common/api/main/LoginController.js"
|
|
|
|
|
import { AppHeaderAttrs, Header } from "../common/gui/Header.js"
|
|
|
|
|
import { CustomerFacade } from "../common/api/worker/facades/lazy/CustomerFacade.js"
|
|
|
|
|
import { GiftCardFacade } from "../common/api/worker/facades/lazy/GiftCardFacade.js"
|
|
|
|
|
import { GroupManagementFacade } from "../common/api/worker/facades/lazy/GroupManagementFacade.js"
|
|
|
|
|
import { ConfigurationDatabase } from "../common/api/worker/facades/lazy/ConfigurationDatabase.js"
|
|
|
|
|
import { CalendarFacade } from "../common/api/worker/facades/lazy/CalendarFacade.js"
|
|
|
|
|
import { MailFacade } from "../common/api/worker/facades/lazy/MailFacade.js"
|
|
|
|
|
import { ShareFacade } from "../common/api/worker/facades/lazy/ShareFacade.js"
|
|
|
|
|
import { CounterFacade } from "../common/api/worker/facades/lazy/CounterFacade.js"
|
2024-08-20 18:03:03 +02:00
|
|
|
import { Indexer } from "./workerUtils/index/Indexer.js"
|
|
|
|
|
import { SearchFacade } from "./workerUtils/index/SearchFacade.js"
|
2024-07-10 16:40:04 +02:00
|
|
|
import { BookingFacade } from "../common/api/worker/facades/lazy/BookingFacade.js"
|
|
|
|
|
import { MailAddressFacade } from "../common/api/worker/facades/lazy/MailAddressFacade.js"
|
|
|
|
|
import { BlobFacade } from "../common/api/worker/facades/lazy/BlobFacade.js"
|
|
|
|
|
import { UserManagementFacade } from "../common/api/worker/facades/lazy/UserManagementFacade.js"
|
|
|
|
|
import { RecoverCodeFacade } from "../common/api/worker/facades/lazy/RecoverCodeFacade.js"
|
|
|
|
|
import { ContactFacade } from "../common/api/worker/facades/lazy/ContactFacade.js"
|
|
|
|
|
import { UsageTestController } from "@tutao/tutanota-usagetests"
|
|
|
|
|
import { EphemeralUsageTestStorage, StorageBehavior, UsageTestModel } from "../common/misc/UsageTestModel.js"
|
|
|
|
|
import { NewsModel } from "../common/misc/news/NewsModel.js"
|
|
|
|
|
import { IServiceExecutor } from "../common/api/common/ServiceRequest.js"
|
|
|
|
|
import { CryptoFacade } from "../common/api/worker/crypto/CryptoFacade.js"
|
|
|
|
|
import { SearchTextInAppFacade } from "../common/native/common/generatedipc/SearchTextInAppFacade.js"
|
|
|
|
|
import { SettingsFacade } from "../common/native/common/generatedipc/SettingsFacade.js"
|
|
|
|
|
import { DesktopSystemFacade } from "../common/native/common/generatedipc/DesktopSystemFacade.js"
|
|
|
|
|
import { WebMobileFacade } from "../common/native/main/WebMobileFacade.js"
|
|
|
|
|
import { SystemPermissionHandler } from "../common/native/main/SystemPermissionHandler.js"
|
|
|
|
|
import { InterWindowEventFacadeSendDispatcher } from "../common/native/common/generatedipc/InterWindowEventFacadeSendDispatcher.js"
|
|
|
|
|
import { ExposedCacheStorage } from "../common/api/worker/rest/DefaultEntityRestCache.js"
|
|
|
|
|
import { WorkerFacade } from "../common/api/worker/facades/WorkerFacade.js"
|
|
|
|
|
import { PageContextLoginListener } from "../common/api/main/PageContextLoginListener.js"
|
|
|
|
|
import { WebsocketConnectivityModel } from "../common/misc/WebsocketConnectivityModel.js"
|
|
|
|
|
import { OperationProgressTracker } from "../common/api/main/OperationProgressTracker.js"
|
|
|
|
|
import { InfoMessageHandler } from "../common/gui/InfoMessageHandler.js"
|
|
|
|
|
import { NativeInterfaces } from "../common/native/main/NativeInterfaceFactory.js"
|
|
|
|
|
import { EntropyFacade } from "../common/api/worker/facades/EntropyFacade.js"
|
|
|
|
|
import { SqlCipherFacade } from "../common/native/common/generatedipc/SqlCipherFacade.js"
|
2024-11-07 17:28:05 +01:00
|
|
|
import { assert, assertNotNull, defer, DeferredObject, lazy, lazyAsync, LazyLoaded, lazyMemoized, noOp } from "@tutao/tutanota-utils"
|
2024-07-10 16:40:04 +02:00
|
|
|
import { RecipientsModel } from "../common/api/main/RecipientsModel.js"
|
|
|
|
|
import { NoZoneDateProvider } from "../common/api/common/utils/NoZoneDateProvider.js"
|
2024-10-09 15:45:38 +02:00
|
|
|
import { CalendarEvent, CalendarEventAttendee, Contact, Mail, MailboxProperties } from "../common/api/entities/tutanota/TypeRefs.js"
|
2024-07-10 16:40:04 +02:00
|
|
|
import { SendMailModel } from "../common/mailFunctionality/SendMailModel.js"
|
|
|
|
|
import { OfflineIndicatorViewModel } from "../common/gui/base/OfflineIndicatorViewModel.js"
|
|
|
|
|
import { Router, ScopedRouter, ThrottledRouter } from "../common/gui/ScopedRouter.js"
|
2024-10-10 16:05:02 +02:00
|
|
|
import { DeviceConfig, deviceConfig } from "../common/misc/DeviceConfig.js"
|
2024-07-10 16:40:04 +02:00
|
|
|
import { InboxRuleHandler } from "./mail/model/InboxRuleHandler.js"
|
|
|
|
|
import { SearchViewModel } from "./search/view/SearchViewModel.js"
|
2024-07-09 17:17:32 +02:00
|
|
|
import { SearchRouter } from "../common/search/view/SearchRouter.js"
|
2024-07-10 16:40:04 +02:00
|
|
|
import { MailOpenedListener } from "./mail/view/MailViewModel.js"
|
2024-07-30 12:07:58 +02:00
|
|
|
import { getEnabledMailAddressesWithUser } from "../common/mailFunctionality/SharedMailUtils.js"
|
2024-11-29 10:17:40 +01:00
|
|
|
import { CLIENT_ONLY_CALENDARS, Const, DEFAULT_CLIENT_ONLY_CALENDAR_COLORS, FeatureType, GroupType } from "../common/api/common/TutanotaConstants.js"
|
2024-07-10 16:40:04 +02:00
|
|
|
import { ShareableGroupType } from "../common/sharing/GroupUtils.js"
|
|
|
|
|
import { ReceivedGroupInvitationsModel } from "../common/sharing/model/ReceivedGroupInvitationsModel.js"
|
|
|
|
|
import { CalendarViewModel } from "../calendar-app/calendar/view/CalendarViewModel.js"
|
|
|
|
|
import { CalendarEventModel, CalendarOperation } from "../calendar-app/calendar/gui/eventeditor-model/CalendarEventModel.js"
|
2024-07-09 17:17:32 +02:00
|
|
|
import { CalendarEventsRepository } from "../common/calendar/date/CalendarEventsRepository.js"
|
2024-07-10 16:40:04 +02:00
|
|
|
import { showProgressDialog } from "../common/gui/dialogs/ProgressDialog.js"
|
2024-11-07 17:28:05 +01:00
|
|
|
import { ContactSuggestionProvider, RecipientsSearchModel } from "../common/misc/RecipientsSearchModel.js"
|
2024-07-10 16:40:04 +02:00
|
|
|
import { ConversationViewModel, ConversationViewModelFactory } from "./mail/view/ConversationViewModel.js"
|
|
|
|
|
import { CreateMailViewerOptions } from "./mail/view/MailViewer.js"
|
|
|
|
|
import { MailViewerViewModel } from "./mail/view/MailViewerViewModel.js"
|
2024-07-23 15:04:19 +02:00
|
|
|
import { ExternalLoginViewModel } from "./mail/view/ExternalLoginView.js"
|
2024-07-10 16:40:04 +02:00
|
|
|
import { NativeInterfaceMain } from "../common/native/main/NativeInterfaceMain.js"
|
|
|
|
|
import { NativeFileApp } from "../common/native/common/FileApp.js"
|
2024-07-03 11:08:01 +02:00
|
|
|
import type { NativePushServiceApp } from "../common/native/main/NativePushServiceApp.js"
|
2024-07-10 16:40:04 +02:00
|
|
|
import { CommonSystemFacade } from "../common/native/common/generatedipc/CommonSystemFacade.js"
|
|
|
|
|
import { ThemeFacade } from "../common/native/common/generatedipc/ThemeFacade.js"
|
|
|
|
|
import { MobileSystemFacade } from "../common/native/common/generatedipc/MobileSystemFacade.js"
|
|
|
|
|
import { MobileContactsFacade } from "../common/native/common/generatedipc/MobileContactsFacade.js"
|
|
|
|
|
import { NativeCredentialsFacade } from "../common/native/common/generatedipc/NativeCredentialsFacade.js"
|
2024-07-26 16:53:31 +02:00
|
|
|
import { MailAddressNameChanger, MailAddressTableModel } from "../common/settings/mailaddress/MailAddressTableModel.js"
|
2024-07-10 16:40:04 +02:00
|
|
|
import { GroupInfo } from "../common/api/entities/sys/TypeRefs.js"
|
|
|
|
|
import { DrawerMenuAttrs } from "../common/gui/nav/DrawerMenu.js"
|
|
|
|
|
import { DomainConfigProvider } from "../common/api/common/DomainConfigProvider.js"
|
|
|
|
|
import { CredentialRemovalHandler } from "../common/login/CredentialRemovalHandler.js"
|
|
|
|
|
import { LoginViewModel } from "../common/login/LoginViewModel.js"
|
|
|
|
|
import { ProgrammingError } from "../common/api/common/error/ProgrammingError.js"
|
|
|
|
|
import { EntropyCollector } from "../common/api/main/EntropyCollector.js"
|
|
|
|
|
import { notifications } from "../common/gui/Notifications.js"
|
|
|
|
|
import { windowFacade } from "../common/misc/WindowFacade.js"
|
|
|
|
|
import { BrowserWebauthn } from "../common/misc/2fa/webauthn/BrowserWebauthn.js"
|
|
|
|
|
import { FileControllerBrowser } from "../common/file/FileControllerBrowser.js"
|
|
|
|
|
import { FileControllerNative } from "../common/file/FileControllerNative.js"
|
|
|
|
|
import { CalendarInfo, CalendarModel } from "../calendar-app/calendar/model/CalendarModel.js"
|
|
|
|
|
import { CalendarInviteHandler } from "../calendar-app/calendar/view/CalendarInvites.js"
|
2024-07-09 17:17:32 +02:00
|
|
|
import { AlarmScheduler } from "../common/calendar/date/AlarmScheduler.js"
|
2024-07-10 16:40:04 +02:00
|
|
|
import { SchedulerImpl } from "../common/api/common/utils/Scheduler.js"
|
2024-10-09 15:45:38 +02:00
|
|
|
import type { CalendarEventPreviewViewModel } from "../calendar-app/calendar/gui/eventpopup/CalendarEventPreviewViewModel.js"
|
2024-07-10 16:40:04 +02:00
|
|
|
import { isCustomizationEnabledForCustomer } from "../common/api/common/utils/CustomerUtils.js"
|
|
|
|
|
import { NativeContactsSyncManager } from "./contacts/model/NativeContactsSyncManager.js"
|
|
|
|
|
import { PostLoginActions } from "../common/login/PostLoginActions.js"
|
|
|
|
|
import { CredentialFormatMigrator } from "../common/misc/credentials/CredentialFormatMigrator.js"
|
|
|
|
|
import { AddNotificationEmailDialog } from "./settings/AddNotificationEmailDialog.js"
|
2024-07-15 18:08:18 +02:00
|
|
|
import { NativeThemeFacade, ThemeController, WebThemeFacade } from "../common/gui/ThemeController.js"
|
|
|
|
|
import { HtmlSanitizer } from "../common/misc/HtmlSanitizer.js"
|
|
|
|
|
import { theme } from "../common/gui/theme.js"
|
2024-07-09 17:17:32 +02:00
|
|
|
import { SearchIndexStateInfo } from "../common/api/worker/search/SearchTypes.js"
|
2024-07-03 11:08:01 +02:00
|
|
|
import { MobilePaymentsFacade } from "../common/native/common/generatedipc/MobilePaymentsFacade.js"
|
2024-07-18 14:44:46 +02:00
|
|
|
import { MAIL_PREFIX } from "../common/misc/RouteChange.js"
|
2024-07-30 12:07:58 +02:00
|
|
|
import { getDisplayedSender } from "../common/api/common/CommonMailUtils.js"
|
2024-08-14 11:40:16 +02:00
|
|
|
import { MailModel } from "./mail/model/MailModel.js"
|
2024-08-30 16:41:51 +02:00
|
|
|
import { locator } from "../common/api/main/CommonLocator.js"
|
|
|
|
|
import { showSnackBar } from "../common/gui/base/SnackBar.js"
|
2024-08-20 18:03:03 +02:00
|
|
|
import { WorkerRandomizer } from "../common/api/worker/workerInterfaces.js"
|
|
|
|
|
import { SearchCategoryTypes } from "./search/model/SearchUtils.js"
|
|
|
|
|
import { WorkerInterface } from "./workerUtils/worker/WorkerImpl.js"
|
|
|
|
|
import { isMailInSpamOrTrash } from "./mail/model/MailChecks.js"
|
2024-08-15 15:35:08 +02:00
|
|
|
import type { ContactImporter } from "./contacts/ContactImporter.js"
|
2024-08-13 10:33:13 +02:00
|
|
|
import { ExternalCalendarFacade } from "../common/native/common/generatedipc/ExternalCalendarFacade.js"
|
2024-08-20 18:03:03 +02:00
|
|
|
import { AppType } from "../common/misc/ClientConstants.js"
|
|
|
|
|
import { ParsedEvent } from "../common/calendar/import/CalendarImporter.js"
|
2024-10-10 16:05:02 +02:00
|
|
|
import { lang } from "../common/misc/LanguageViewModel.js"
|
2024-10-09 15:45:38 +02:00
|
|
|
import type { CalendarContactPreviewViewModel } from "../calendar-app/calendar/gui/eventpopup/CalendarContactPreviewViewModel.js"
|
2024-10-29 15:55:32 +01:00
|
|
|
import { KeyLoaderFacade } from "../common/api/worker/facades/KeyLoaderFacade.js"
|
2024-11-07 17:28:05 +01:00
|
|
|
import { ContactSuggestion } from "../common/native/common/generatedipc/ContactSuggestion"
|
2024-11-19 15:42:39 +01:00
|
|
|
import { MailImporter } from "./mail/import/MailImporter.js"
|
2024-11-29 10:17:40 +01:00
|
|
|
import type { MailExportController } from "./native/main/MailExportController.js"
|
|
|
|
|
import { ExportFacade } from "../common/native/common/generatedipc/ExportFacade.js"
|
|
|
|
|
import { BulkMailLoader } from "./workerUtils/index/BulkMailLoader.js"
|
2024-12-09 15:31:25 +01:00
|
|
|
import { MailExportFacade } from "../common/api/worker/facades/lazy/MailExportFacade.js"
|
2025-02-17 06:48:28 +01:00
|
|
|
import { SyncTracker } from "../common/api/main/SyncTracker.js"
|
2024-07-10 16:40:04 +02:00
|
|
|
|
|
|
|
|
assertMainOrNode()
|
|
|
|
|
|
|
|
|
|
class MailLocator {
|
|
|
|
|
eventController!: EventController
|
|
|
|
|
search!: SearchModel
|
2024-08-07 16:29:40 +02:00
|
|
|
mailboxModel!: MailboxModel
|
2024-07-10 16:40:04 +02:00
|
|
|
mailModel!: MailModel
|
|
|
|
|
minimizedMailModel!: MinimizedMailEditorViewModel
|
|
|
|
|
contactModel!: ContactModel
|
|
|
|
|
entityClient!: EntityClient
|
|
|
|
|
progressTracker!: ProgressTracker
|
|
|
|
|
credentialsProvider!: CredentialsProvider
|
|
|
|
|
worker!: WorkerClient
|
|
|
|
|
fileController!: FileController
|
|
|
|
|
secondFactorHandler!: SecondFactorHandler
|
|
|
|
|
webAuthn!: WebauthnClient
|
|
|
|
|
loginFacade!: LoginFacade
|
|
|
|
|
logins!: LoginController
|
|
|
|
|
header!: Header
|
|
|
|
|
customerFacade!: CustomerFacade
|
2024-10-29 15:55:32 +01:00
|
|
|
keyLoaderFacade!: KeyLoaderFacade
|
2024-07-10 16:40:04 +02:00
|
|
|
giftCardFacade!: GiftCardFacade
|
|
|
|
|
groupManagementFacade!: GroupManagementFacade
|
|
|
|
|
configFacade!: ConfigurationDatabase
|
|
|
|
|
calendarFacade!: CalendarFacade
|
|
|
|
|
mailFacade!: MailFacade
|
|
|
|
|
shareFacade!: ShareFacade
|
|
|
|
|
counterFacade!: CounterFacade
|
|
|
|
|
indexerFacade!: Indexer
|
|
|
|
|
searchFacade!: SearchFacade
|
|
|
|
|
bookingFacade!: BookingFacade
|
|
|
|
|
mailAddressFacade!: MailAddressFacade
|
|
|
|
|
blobFacade!: BlobFacade
|
|
|
|
|
userManagementFacade!: UserManagementFacade
|
|
|
|
|
recoverCodeFacade!: RecoverCodeFacade
|
|
|
|
|
contactFacade!: ContactFacade
|
|
|
|
|
usageTestController!: UsageTestController
|
|
|
|
|
usageTestModel!: UsageTestModel
|
|
|
|
|
newsModel!: NewsModel
|
|
|
|
|
serviceExecutor!: IServiceExecutor
|
|
|
|
|
cryptoFacade!: CryptoFacade
|
|
|
|
|
searchTextFacade!: SearchTextInAppFacade
|
|
|
|
|
desktopSettingsFacade!: SettingsFacade
|
|
|
|
|
desktopSystemFacade!: DesktopSystemFacade
|
2024-11-29 10:17:40 +01:00
|
|
|
exportFacade!: ExportFacade
|
2024-07-10 16:40:04 +02:00
|
|
|
webMobileFacade!: WebMobileFacade
|
|
|
|
|
systemPermissionHandler!: SystemPermissionHandler
|
|
|
|
|
interWindowEventSender!: InterWindowEventFacadeSendDispatcher
|
|
|
|
|
cacheStorage!: ExposedCacheStorage
|
|
|
|
|
workerFacade!: WorkerFacade
|
|
|
|
|
loginListener!: PageContextLoginListener
|
|
|
|
|
random!: WorkerRandomizer
|
|
|
|
|
connectivityModel!: WebsocketConnectivityModel
|
|
|
|
|
operationProgressTracker!: OperationProgressTracker
|
|
|
|
|
infoMessageHandler!: InfoMessageHandler
|
2024-07-15 18:08:18 +02:00
|
|
|
themeController!: ThemeController
|
2024-07-10 16:40:04 +02:00
|
|
|
Const!: Record<string, any>
|
2024-11-29 10:17:40 +01:00
|
|
|
bulkMailLoader!: BulkMailLoader
|
2024-12-09 15:31:25 +01:00
|
|
|
mailExportFacade!: MailExportFacade
|
2025-02-17 06:48:28 +01:00
|
|
|
syncTracker!: SyncTracker
|
2024-07-10 16:40:04 +02:00
|
|
|
|
|
|
|
|
private nativeInterfaces: NativeInterfaces | null = null
|
2025-01-09 14:13:53 +01:00
|
|
|
private mailImporter: MailImporter | null = null
|
2024-07-10 16:40:04 +02:00
|
|
|
private entropyFacade!: EntropyFacade
|
|
|
|
|
private sqlCipherFacade!: SqlCipherFacade
|
|
|
|
|
|
|
|
|
|
readonly recipientsModel: lazyAsync<RecipientsModel> = lazyMemoized(async () => {
|
|
|
|
|
const { RecipientsModel } = await import("../common/api/main/RecipientsModel.js")
|
|
|
|
|
return new RecipientsModel(this.contactModel, this.logins, this.mailFacade, this.entityClient)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
async noZoneDateProvider(): Promise<NoZoneDateProvider> {
|
|
|
|
|
return new NoZoneDateProvider()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async sendMailModel(mailboxDetails: MailboxDetail, mailboxProperties: MailboxProperties): Promise<SendMailModel> {
|
|
|
|
|
const factory = await this.sendMailModelSyncFactory(mailboxDetails, mailboxProperties)
|
|
|
|
|
return factory()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private readonly redraw: lazyAsync<() => unknown> = lazyMemoized(async () => {
|
|
|
|
|
const m = await import("mithril")
|
|
|
|
|
return m.redraw
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
readonly offlineIndicatorViewModel = lazyMemoized(async () => {
|
|
|
|
|
return new OfflineIndicatorViewModel(
|
|
|
|
|
this.cacheStorage,
|
|
|
|
|
this.loginListener,
|
|
|
|
|
this.connectivityModel,
|
|
|
|
|
this.logins,
|
|
|
|
|
this.progressTracker,
|
|
|
|
|
await this.redraw(),
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
async appHeaderAttrs(): Promise<AppHeaderAttrs> {
|
|
|
|
|
return {
|
|
|
|
|
offlineIndicatorModel: await this.offlineIndicatorViewModel(),
|
|
|
|
|
newsModel: this.newsModel,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
readonly mailViewModel = lazyMemoized(async () => {
|
|
|
|
|
const { MailViewModel } = await import("../mail-app/mail/view/MailViewModel.js")
|
|
|
|
|
const conversationViewModelFactory = await this.conversationViewModelFactory()
|
|
|
|
|
const router = new ScopedRouter(this.throttledRouter(), "/mail")
|
|
|
|
|
return new MailViewModel(
|
2024-08-07 16:29:40 +02:00
|
|
|
this.mailboxModel,
|
2024-07-10 16:40:04 +02:00
|
|
|
this.mailModel,
|
|
|
|
|
this.entityClient,
|
|
|
|
|
this.eventController,
|
|
|
|
|
this.connectivityModel,
|
|
|
|
|
this.cacheStorage,
|
|
|
|
|
conversationViewModelFactory,
|
|
|
|
|
this.mailOpenedListener,
|
|
|
|
|
deviceConfig,
|
|
|
|
|
this.inboxRuleHanlder(),
|
|
|
|
|
router,
|
|
|
|
|
await this.redraw(),
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
2024-09-09 09:02:33 +02:00
|
|
|
readonly affiliateViewModel = lazyMemoized(async () => {
|
|
|
|
|
const { AffiliateViewModel } = await import("../common/settings/AffiliateViewModel.js")
|
|
|
|
|
return new AffiliateViewModel()
|
|
|
|
|
})
|
|
|
|
|
|
2024-07-10 16:40:04 +02:00
|
|
|
inboxRuleHanlder(): InboxRuleHandler {
|
2024-07-26 16:35:38 +02:00
|
|
|
return new InboxRuleHandler(this.mailFacade, this.logins)
|
2024-07-10 16:40:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async searchViewModelFactory(): Promise<() => SearchViewModel> {
|
|
|
|
|
const { SearchViewModel } = await import("../mail-app/search/view/SearchViewModel.js")
|
|
|
|
|
const conversationViewModelFactory = await this.conversationViewModelFactory()
|
|
|
|
|
const redraw = await this.redraw()
|
|
|
|
|
const searchRouter = await this.scopedSearchRouter()
|
2024-08-29 15:01:11 +02:00
|
|
|
const calendarEventsRepository = await this.calendarEventsRepository()
|
2024-07-10 16:40:04 +02:00
|
|
|
return () => {
|
|
|
|
|
return new SearchViewModel(
|
|
|
|
|
searchRouter,
|
|
|
|
|
this.search,
|
|
|
|
|
this.searchFacade,
|
2024-08-07 16:29:40 +02:00
|
|
|
this.mailboxModel,
|
2024-07-10 16:40:04 +02:00
|
|
|
this.logins,
|
|
|
|
|
this.indexerFacade,
|
|
|
|
|
this.entityClient,
|
|
|
|
|
this.eventController,
|
|
|
|
|
this.mailOpenedListener,
|
|
|
|
|
this.calendarFacade,
|
|
|
|
|
this.progressTracker,
|
|
|
|
|
conversationViewModelFactory,
|
2024-08-29 15:01:11 +02:00
|
|
|
calendarEventsRepository,
|
2024-07-10 16:40:04 +02:00
|
|
|
redraw,
|
|
|
|
|
deviceConfig.getMailAutoSelectBehavior(),
|
2024-10-10 16:05:02 +02:00
|
|
|
deviceConfig.getClientOnlyCalendars(),
|
2024-07-10 16:40:04 +02:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
readonly throttledRouter: lazy<Router> = lazyMemoized(() => new ThrottledRouter())
|
|
|
|
|
|
|
|
|
|
readonly scopedSearchRouter: lazyAsync<SearchRouter> = lazyMemoized(async () => {
|
2024-07-09 17:17:32 +02:00
|
|
|
const { SearchRouter } = await import("../common/search/view/SearchRouter.js")
|
2024-07-10 16:40:04 +02:00
|
|
|
return new SearchRouter(new ScopedRouter(this.throttledRouter(), "/search"))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
readonly unscopedSearchRouter: lazyAsync<SearchRouter> = lazyMemoized(async () => {
|
2024-07-09 17:17:32 +02:00
|
|
|
const { SearchRouter } = await import("../common/search/view/SearchRouter.js")
|
2024-07-10 16:40:04 +02:00
|
|
|
return new SearchRouter(this.throttledRouter())
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
readonly mailOpenedListener: MailOpenedListener = {
|
|
|
|
|
onEmailOpened: isDesktop()
|
|
|
|
|
? (mail) => {
|
2024-08-19 13:45:56 +02:00
|
|
|
this.desktopSystemFacade.sendSocketMessage(getDisplayedSender(mail).address)
|
|
|
|
|
}
|
2024-07-10 16:40:04 +02:00
|
|
|
: noOp,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
readonly contactViewModel = lazyMemoized(async () => {
|
|
|
|
|
const { ContactViewModel } = await import("../mail-app/contacts/view/ContactViewModel.js")
|
|
|
|
|
const router = new ScopedRouter(this.throttledRouter(), "/contact")
|
|
|
|
|
return new ContactViewModel(this.contactModel, this.entityClient, this.eventController, router, await this.redraw())
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
readonly contactListViewModel = lazyMemoized(async () => {
|
|
|
|
|
const { ContactListViewModel } = await import("../mail-app/contacts/view/ContactListViewModel.js")
|
|
|
|
|
const router = new ScopedRouter(this.throttledRouter(), "/contactlist")
|
|
|
|
|
return new ContactListViewModel(
|
|
|
|
|
this.entityClient,
|
|
|
|
|
this.groupManagementFacade,
|
|
|
|
|
this.logins,
|
|
|
|
|
this.eventController,
|
|
|
|
|
this.contactModel,
|
|
|
|
|
await this.receivedGroupInvitationsModel(GroupType.ContactList),
|
|
|
|
|
router,
|
|
|
|
|
await this.redraw(),
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
async receivedGroupInvitationsModel<TypeOfGroup extends ShareableGroupType>(groupType: TypeOfGroup): Promise<ReceivedGroupInvitationsModel<TypeOfGroup>> {
|
|
|
|
|
const { ReceivedGroupInvitationsModel } = await import("../common/sharing/model/ReceivedGroupInvitationsModel.js")
|
|
|
|
|
return new ReceivedGroupInvitationsModel<TypeOfGroup>(groupType, this.eventController, this.entityClient, this.logins)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
readonly calendarViewModel = lazyMemoized<Promise<CalendarViewModel>>(async () => {
|
|
|
|
|
const { CalendarViewModel } = await import("../calendar-app/calendar/view/CalendarViewModel.js")
|
2024-07-09 17:17:32 +02:00
|
|
|
const { DefaultDateProvider } = await import("../common/calendar/date/CalendarUtils")
|
2024-07-10 16:40:04 +02:00
|
|
|
const timeZone = new DefaultDateProvider().timeZone()
|
|
|
|
|
return new CalendarViewModel(
|
|
|
|
|
this.logins,
|
|
|
|
|
async (mode: CalendarOperation, event: CalendarEvent) => {
|
2024-08-07 16:29:40 +02:00
|
|
|
const mailboxDetail = await this.mailboxModel.getUserMailboxDetails()
|
|
|
|
|
const mailboxProperties = await this.mailboxModel.getMailboxProperties(mailboxDetail.mailboxGroupRoot)
|
2024-07-10 16:40:04 +02:00
|
|
|
return await this.calendarEventModel(mode, event, mailboxDetail, mailboxProperties, null)
|
|
|
|
|
},
|
|
|
|
|
(...args) => this.calendarEventPreviewModel(...args),
|
2024-10-09 15:45:38 +02:00
|
|
|
(...args) => this.calendarContactPreviewModel(...args),
|
2024-07-10 16:40:04 +02:00
|
|
|
await this.calendarModel(),
|
|
|
|
|
await this.calendarEventsRepository(),
|
|
|
|
|
this.entityClient,
|
|
|
|
|
this.eventController,
|
|
|
|
|
this.progressTracker,
|
|
|
|
|
deviceConfig,
|
|
|
|
|
await this.receivedGroupInvitationsModel(GroupType.Calendar),
|
|
|
|
|
timeZone,
|
2024-08-07 16:29:40 +02:00
|
|
|
this.mailboxModel,
|
2024-08-29 15:01:11 +02:00
|
|
|
this.contactModel,
|
2024-07-10 16:40:04 +02:00
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
readonly calendarEventsRepository: lazyAsync<CalendarEventsRepository> = lazyMemoized(async () => {
|
2024-07-09 17:17:32 +02:00
|
|
|
const { CalendarEventsRepository } = await import("../common/calendar/date/CalendarEventsRepository.js")
|
|
|
|
|
const { DefaultDateProvider } = await import("../common/calendar/date/CalendarUtils")
|
2024-07-10 16:40:04 +02:00
|
|
|
const timeZone = new DefaultDateProvider().timeZone()
|
2024-10-10 16:05:02 +02:00
|
|
|
return new CalendarEventsRepository(
|
|
|
|
|
await this.calendarModel(),
|
|
|
|
|
this.calendarFacade,
|
|
|
|
|
timeZone,
|
|
|
|
|
this.entityClient,
|
|
|
|
|
this.eventController,
|
|
|
|
|
this.contactModel,
|
|
|
|
|
this.logins,
|
|
|
|
|
)
|
2024-07-10 16:40:04 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
/** This ugly bit exists because CalendarEventWhoModel wants a sync factory. */
|
|
|
|
|
private async sendMailModelSyncFactory(mailboxDetails: MailboxDetail, mailboxProperties: MailboxProperties): Promise<() => SendMailModel> {
|
|
|
|
|
const { SendMailModel } = await import("../common/mailFunctionality/SendMailModel.js")
|
|
|
|
|
const recipientsModel = await this.recipientsModel()
|
|
|
|
|
const dateProvider = await this.noZoneDateProvider()
|
|
|
|
|
return () =>
|
|
|
|
|
new SendMailModel(
|
|
|
|
|
this.mailFacade,
|
|
|
|
|
this.entityClient,
|
|
|
|
|
this.logins,
|
2024-08-07 16:29:40 +02:00
|
|
|
this.mailboxModel,
|
2024-07-10 16:40:04 +02:00
|
|
|
this.contactModel,
|
|
|
|
|
this.eventController,
|
|
|
|
|
mailboxDetails,
|
|
|
|
|
recipientsModel,
|
|
|
|
|
dateProvider,
|
|
|
|
|
mailboxProperties,
|
2024-08-07 16:29:40 +02:00
|
|
|
async (mail: Mail) => {
|
2024-08-14 11:40:16 +02:00
|
|
|
return await isMailInSpamOrTrash(mail, mailLocator.mailModel)
|
2024-08-07 16:29:40 +02:00
|
|
|
},
|
2024-07-10 16:40:04 +02:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async calendarEventModel(
|
|
|
|
|
editMode: CalendarOperation,
|
|
|
|
|
event: Partial<CalendarEvent>,
|
|
|
|
|
mailboxDetail: MailboxDetail,
|
|
|
|
|
mailboxProperties: MailboxProperties,
|
|
|
|
|
responseTo: Mail | null,
|
|
|
|
|
): Promise<CalendarEventModel | null> {
|
|
|
|
|
const [{ makeCalendarEventModel }, { getTimeZone }, { calendarNotificationSender }] = await Promise.all([
|
|
|
|
|
import("../calendar-app/calendar/gui/eventeditor-model/CalendarEventModel.js"),
|
2024-07-09 17:17:32 +02:00
|
|
|
import("../common/calendar/date/CalendarUtils.js"),
|
2024-07-10 16:40:04 +02:00
|
|
|
import("../calendar-app/calendar/view/CalendarNotificationSender.js"),
|
|
|
|
|
])
|
|
|
|
|
const sendMailModelFactory = await this.sendMailModelSyncFactory(mailboxDetail, mailboxProperties)
|
|
|
|
|
const showProgress = <T>(p: Promise<T>) => showProgressDialog("pleaseWait_msg", p)
|
|
|
|
|
|
|
|
|
|
return await makeCalendarEventModel(
|
|
|
|
|
editMode,
|
|
|
|
|
event,
|
|
|
|
|
await this.recipientsModel(),
|
|
|
|
|
await this.calendarModel(),
|
|
|
|
|
this.logins,
|
|
|
|
|
mailboxDetail,
|
|
|
|
|
mailboxProperties,
|
|
|
|
|
sendMailModelFactory,
|
|
|
|
|
calendarNotificationSender,
|
|
|
|
|
this.entityClient,
|
|
|
|
|
responseTo,
|
|
|
|
|
getTimeZone(),
|
|
|
|
|
showProgress,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async recipientsSearchModel(): Promise<RecipientsSearchModel> {
|
|
|
|
|
const { RecipientsSearchModel } = await import("../common/misc/RecipientsSearchModel.js")
|
2024-11-07 17:28:05 +01:00
|
|
|
const suggestionsProvider = await this.contactSuggestionProvider()
|
2024-07-10 16:40:04 +02:00
|
|
|
return new RecipientsSearchModel(await this.recipientsModel(), this.contactModel, suggestionsProvider, this.entityClient)
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-07 17:28:05 +01:00
|
|
|
private async contactSuggestionProvider(): Promise<ContactSuggestionProvider> {
|
|
|
|
|
if (isApp()) {
|
|
|
|
|
const { MobileContactSuggestionProvider } = await import("../common/native/main/MobileContactSuggestionProvider.js")
|
|
|
|
|
return new MobileContactSuggestionProvider(this.mobileContactsFacade)
|
|
|
|
|
} else {
|
|
|
|
|
return {
|
2025-01-03 10:16:07 +01:00
|
|
|
async getContactSuggestions(_query: string): Promise<readonly ContactSuggestion[]> {
|
2024-11-07 17:28:05 +01:00
|
|
|
return []
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-10 16:40:04 +02:00
|
|
|
readonly conversationViewModelFactory: lazyAsync<ConversationViewModelFactory> = async () => {
|
|
|
|
|
const { ConversationViewModel } = await import("../mail-app/mail/view/ConversationViewModel.js")
|
|
|
|
|
const factory = await this.mailViewerViewModelFactory()
|
|
|
|
|
const m = await import("mithril")
|
|
|
|
|
return (options: CreateMailViewerOptions) => {
|
|
|
|
|
return new ConversationViewModel(
|
|
|
|
|
options,
|
|
|
|
|
(options) => factory(options),
|
|
|
|
|
this.entityClient,
|
|
|
|
|
this.eventController,
|
|
|
|
|
deviceConfig,
|
|
|
|
|
this.mailModel,
|
|
|
|
|
m.redraw,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async conversationViewModel(options: CreateMailViewerOptions): Promise<ConversationViewModel> {
|
|
|
|
|
const factory = await this.conversationViewModelFactory()
|
|
|
|
|
return factory(options)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
contactImporter = async (): Promise<ContactImporter> => {
|
|
|
|
|
const { ContactImporter } = await import("../mail-app/contacts/ContactImporter.js")
|
2024-08-26 19:05:48 +02:00
|
|
|
return new ContactImporter(
|
|
|
|
|
this.contactFacade,
|
|
|
|
|
this.systemPermissionHandler,
|
|
|
|
|
isApp() ? this.mobileContactsFacade : null,
|
|
|
|
|
isApp() ? this.nativeContactsSyncManager() : null,
|
|
|
|
|
)
|
2024-07-10 16:40:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async mailViewerViewModelFactory(): Promise<(options: CreateMailViewerOptions) => MailViewerViewModel> {
|
|
|
|
|
const { MailViewerViewModel } = await import("../mail-app/mail/view/MailViewerViewModel.js")
|
|
|
|
|
return ({ mail, showFolder }) =>
|
|
|
|
|
new MailViewerViewModel(
|
|
|
|
|
mail,
|
|
|
|
|
showFolder,
|
|
|
|
|
this.entityClient,
|
2024-08-07 16:29:40 +02:00
|
|
|
this.mailboxModel,
|
2024-07-10 16:40:04 +02:00
|
|
|
this.mailModel,
|
|
|
|
|
this.contactModel,
|
|
|
|
|
this.configFacade,
|
|
|
|
|
this.fileController,
|
|
|
|
|
this.logins,
|
|
|
|
|
async (mailboxDetails) => {
|
2024-08-07 16:29:40 +02:00
|
|
|
const mailboxProperties = await this.mailboxModel.getMailboxProperties(mailboxDetails.mailboxGroupRoot)
|
2024-07-10 16:40:04 +02:00
|
|
|
return this.sendMailModel(mailboxDetails, mailboxProperties)
|
|
|
|
|
},
|
|
|
|
|
this.eventController,
|
|
|
|
|
this.workerFacade,
|
|
|
|
|
this.search,
|
|
|
|
|
this.mailFacade,
|
|
|
|
|
this.cryptoFacade,
|
|
|
|
|
() => this.contactImporter(),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async externalLoginViewModelFactory(): Promise<() => ExternalLoginViewModel> {
|
2024-07-23 15:04:19 +02:00
|
|
|
const { ExternalLoginViewModel } = await import("./mail/view/ExternalLoginView.js")
|
2024-07-10 16:40:04 +02:00
|
|
|
return () => new ExternalLoginViewModel(this.credentialsProvider)
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-10 16:05:02 +02:00
|
|
|
get deviceConfig(): DeviceConfig {
|
|
|
|
|
return deviceConfig
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-10 16:40:04 +02:00
|
|
|
get native(): NativeInterfaceMain {
|
|
|
|
|
return this.getNativeInterface("native")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get fileApp(): NativeFileApp {
|
|
|
|
|
return this.getNativeInterface("fileApp")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get pushService(): NativePushServiceApp {
|
|
|
|
|
return this.getNativeInterface("pushService")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get commonSystemFacade(): CommonSystemFacade {
|
|
|
|
|
return this.getNativeInterface("commonSystemFacade")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get themeFacade(): ThemeFacade {
|
|
|
|
|
return this.getNativeInterface("themeFacade")
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-13 10:33:13 +02:00
|
|
|
get externalCalendarFacade(): ExternalCalendarFacade {
|
|
|
|
|
return this.getNativeInterface("externalCalendarFacade")
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-10 16:40:04 +02:00
|
|
|
get systemFacade(): MobileSystemFacade {
|
|
|
|
|
return this.getNativeInterface("mobileSystemFacade")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get mobileContactsFacade(): MobileContactsFacade {
|
|
|
|
|
return this.getNativeInterface("mobileContactsFacade")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get nativeCredentialsFacade(): NativeCredentialsFacade {
|
|
|
|
|
return this.getNativeInterface("nativeCredentialsFacade")
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-03 11:08:01 +02:00
|
|
|
get mobilePaymentsFacade(): MobilePaymentsFacade {
|
|
|
|
|
return this.getNativeInterface("mobilePaymentsFacade")
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-10 16:40:04 +02:00
|
|
|
async mailAddressTableModelForOwnMailbox(): Promise<MailAddressTableModel> {
|
2024-07-26 16:53:31 +02:00
|
|
|
const { MailAddressTableModel } = await import("../common/settings/mailaddress/MailAddressTableModel.js")
|
2024-07-10 16:40:04 +02:00
|
|
|
const nameChanger = await this.ownMailAddressNameChanger()
|
|
|
|
|
return new MailAddressTableModel(
|
|
|
|
|
this.entityClient,
|
|
|
|
|
this.serviceExecutor,
|
|
|
|
|
this.mailAddressFacade,
|
|
|
|
|
this.logins,
|
|
|
|
|
this.eventController,
|
|
|
|
|
this.logins.getUserController().userGroupInfo,
|
|
|
|
|
nameChanger,
|
|
|
|
|
await this.redraw(),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async mailAddressTableModelForAdmin(mailGroupId: Id, userId: Id, userGroupInfo: GroupInfo): Promise<MailAddressTableModel> {
|
2024-07-26 16:53:31 +02:00
|
|
|
const { MailAddressTableModel } = await import("../common/settings/mailaddress/MailAddressTableModel.js")
|
2024-07-10 16:40:04 +02:00
|
|
|
const nameChanger = await this.adminNameChanger(mailGroupId, userId)
|
|
|
|
|
return new MailAddressTableModel(
|
|
|
|
|
this.entityClient,
|
|
|
|
|
this.serviceExecutor,
|
|
|
|
|
this.mailAddressFacade,
|
|
|
|
|
this.logins,
|
|
|
|
|
this.eventController,
|
|
|
|
|
userGroupInfo,
|
|
|
|
|
nameChanger,
|
|
|
|
|
await this.redraw(),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async ownMailAddressNameChanger(): Promise<MailAddressNameChanger> {
|
2024-08-20 18:03:03 +02:00
|
|
|
const { OwnMailAddressNameChanger } = await import("../common/settings/mailaddress/OwnMailAddressNameChanger.js")
|
2024-08-07 16:29:40 +02:00
|
|
|
return new OwnMailAddressNameChanger(this.mailboxModel, this.entityClient)
|
2024-07-10 16:40:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async adminNameChanger(mailGroupId: Id, userId: Id): Promise<MailAddressNameChanger> {
|
2024-08-20 18:03:03 +02:00
|
|
|
const { AnotherUserMailAddressNameChanger } = await import("../common/settings/mailaddress/AnotherUserMailAddressNameChanger.js")
|
2024-07-10 16:40:04 +02:00
|
|
|
return new AnotherUserMailAddressNameChanger(this.mailAddressFacade, mailGroupId, userId)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async drawerAttrsFactory(): Promise<() => DrawerMenuAttrs> {
|
|
|
|
|
return () => ({
|
|
|
|
|
logins: this.logins,
|
|
|
|
|
newsModel: this.newsModel,
|
|
|
|
|
desktopSystemFacade: this.desktopSystemFacade,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
domainConfigProvider(): DomainConfigProvider {
|
|
|
|
|
return new DomainConfigProvider()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async credentialsRemovalHandler(): Promise<CredentialRemovalHandler> {
|
|
|
|
|
const { NoopCredentialRemovalHandler, AppsCredentialRemovalHandler } = await import("../common/login/CredentialRemovalHandler.js")
|
|
|
|
|
return isBrowser()
|
|
|
|
|
? new NoopCredentialRemovalHandler()
|
2024-08-20 18:03:03 +02:00
|
|
|
: new AppsCredentialRemovalHandler(this.pushService, this.configFacade, async (login, userId) => {
|
|
|
|
|
if (isApp()) {
|
|
|
|
|
await mailLocator.nativeContactsSyncManager().disableSync(userId, login)
|
|
|
|
|
}
|
|
|
|
|
await mailLocator.indexerFacade.deleteIndex(userId)
|
2024-11-29 10:17:40 +01:00
|
|
|
if (isDesktop()) {
|
|
|
|
|
await mailLocator.exportFacade.clearExportState(userId)
|
|
|
|
|
}
|
2024-08-20 18:03:03 +02:00
|
|
|
})
|
2024-07-10 16:40:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async loginViewModelFactory(): Promise<lazy<LoginViewModel>> {
|
|
|
|
|
const { LoginViewModel } = await import("../common/login/LoginViewModel.js")
|
|
|
|
|
const credentialsRemovalHandler = await mailLocator.credentialsRemovalHandler()
|
|
|
|
|
const { MobileAppLock, NoOpAppLock } = await import("../common/login/AppLock.js")
|
|
|
|
|
const appLock = isApp()
|
|
|
|
|
? new MobileAppLock(assertNotNull(this.nativeInterfaces).mobileSystemFacade, assertNotNull(this.nativeInterfaces).nativeCredentialsFacade)
|
|
|
|
|
: new NoOpAppLock()
|
|
|
|
|
return () => {
|
|
|
|
|
const domainConfig = isBrowser()
|
|
|
|
|
? mailLocator.domainConfigProvider().getDomainConfigForHostname(location.hostname, location.protocol, location.port)
|
|
|
|
|
: // in this case, we know that we have a staticUrl set that we need to use
|
2024-08-19 13:45:56 +02:00
|
|
|
mailLocator.domainConfigProvider().getCurrentDomainConfig()
|
2024-07-10 16:40:04 +02:00
|
|
|
|
|
|
|
|
return new LoginViewModel(
|
|
|
|
|
mailLocator.logins,
|
|
|
|
|
mailLocator.credentialsProvider,
|
|
|
|
|
mailLocator.secondFactorHandler,
|
|
|
|
|
deviceConfig,
|
|
|
|
|
domainConfig,
|
|
|
|
|
credentialsRemovalHandler,
|
|
|
|
|
isBrowser() ? null : this.pushService,
|
|
|
|
|
appLock,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private getNativeInterface<T extends keyof NativeInterfaces>(name: T): NativeInterfaces[T] {
|
|
|
|
|
if (!this.nativeInterfaces) {
|
|
|
|
|
throw new ProgrammingError(`Tried to use ${name} in web`)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this.nativeInterfaces[name]
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-09 14:13:53 +01:00
|
|
|
public getMailImporter(): MailImporter {
|
|
|
|
|
if (this.mailImporter == null) {
|
|
|
|
|
throw new ProgrammingError(`Tried to use mail importer in web or mobile`)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this.mailImporter
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-10 16:40:04 +02:00
|
|
|
private readonly _workerDeferred: DeferredObject<WorkerClient>
|
|
|
|
|
private _entropyCollector!: EntropyCollector
|
|
|
|
|
private _deferredInitialized: DeferredObject<void> = defer()
|
|
|
|
|
|
|
|
|
|
get initialized(): Promise<void> {
|
|
|
|
|
return this._deferredInitialized.promise
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
|
this._workerDeferred = defer()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async init(): Promise<void> {
|
|
|
|
|
// Split init in two separate parts: creating modules and causing side effects.
|
|
|
|
|
// We would like to do both on normal init but on HMR we just want to replace modules without a new worker. If we create a new
|
|
|
|
|
// worker we end up losing state on the worker side (including our session).
|
|
|
|
|
this.worker = bootstrapWorker(this)
|
|
|
|
|
await this._createInstances()
|
|
|
|
|
this._entropyCollector = new EntropyCollector(this.entropyFacade, await this.scheduler(), window)
|
|
|
|
|
|
|
|
|
|
this._entropyCollector.start()
|
|
|
|
|
|
|
|
|
|
this._deferredInitialized.resolve()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async _createInstances() {
|
|
|
|
|
const {
|
|
|
|
|
loginFacade,
|
|
|
|
|
customerFacade,
|
|
|
|
|
giftCardFacade,
|
|
|
|
|
groupManagementFacade,
|
|
|
|
|
configFacade,
|
|
|
|
|
calendarFacade,
|
|
|
|
|
mailFacade,
|
|
|
|
|
shareFacade,
|
|
|
|
|
counterFacade,
|
|
|
|
|
indexerFacade,
|
|
|
|
|
searchFacade,
|
|
|
|
|
bookingFacade,
|
|
|
|
|
mailAddressFacade,
|
|
|
|
|
blobFacade,
|
|
|
|
|
userManagementFacade,
|
|
|
|
|
recoverCodeFacade,
|
|
|
|
|
restInterface,
|
|
|
|
|
serviceExecutor,
|
|
|
|
|
cryptoFacade,
|
|
|
|
|
cacheStorage,
|
|
|
|
|
random,
|
|
|
|
|
eventBus,
|
|
|
|
|
entropyFacade,
|
|
|
|
|
workerFacade,
|
|
|
|
|
sqlCipherFacade,
|
|
|
|
|
contactFacade,
|
2024-11-29 10:17:40 +01:00
|
|
|
bulkMailLoader,
|
2024-12-09 15:31:25 +01:00
|
|
|
mailExportFacade,
|
2024-08-20 18:03:03 +02:00
|
|
|
} = this.worker.getWorkerInterface() as WorkerInterface
|
2024-07-10 16:40:04 +02:00
|
|
|
this.loginFacade = loginFacade
|
|
|
|
|
this.customerFacade = customerFacade
|
|
|
|
|
this.giftCardFacade = giftCardFacade
|
|
|
|
|
this.groupManagementFacade = groupManagementFacade
|
|
|
|
|
this.configFacade = configFacade
|
|
|
|
|
this.calendarFacade = calendarFacade
|
|
|
|
|
this.mailFacade = mailFacade
|
|
|
|
|
this.shareFacade = shareFacade
|
|
|
|
|
this.counterFacade = counterFacade
|
|
|
|
|
this.indexerFacade = indexerFacade
|
|
|
|
|
this.searchFacade = searchFacade
|
|
|
|
|
this.bookingFacade = bookingFacade
|
|
|
|
|
this.mailAddressFacade = mailAddressFacade
|
|
|
|
|
this.blobFacade = blobFacade
|
|
|
|
|
this.userManagementFacade = userManagementFacade
|
|
|
|
|
this.recoverCodeFacade = recoverCodeFacade
|
|
|
|
|
this.contactFacade = contactFacade
|
|
|
|
|
this.serviceExecutor = serviceExecutor
|
|
|
|
|
this.sqlCipherFacade = sqlCipherFacade
|
2025-01-27 19:08:58 +01:00
|
|
|
this.logins = new LoginController(
|
|
|
|
|
this.loginFacade,
|
|
|
|
|
async () => this.loginListener,
|
|
|
|
|
() => this.worker.reset(),
|
|
|
|
|
)
|
2024-07-10 16:40:04 +02:00
|
|
|
// Should be called elsewhere later e.g. in CommonLocator
|
|
|
|
|
this.logins.init()
|
|
|
|
|
this.eventController = new EventController(mailLocator.logins)
|
|
|
|
|
this.progressTracker = new ProgressTracker()
|
2025-02-17 06:48:28 +01:00
|
|
|
this.syncTracker = new SyncTracker()
|
2024-07-10 16:40:04 +02:00
|
|
|
this.search = new SearchModel(this.searchFacade, () => this.calendarEventsRepository())
|
|
|
|
|
this.entityClient = new EntityClient(restInterface)
|
|
|
|
|
this.cryptoFacade = cryptoFacade
|
|
|
|
|
this.cacheStorage = cacheStorage
|
|
|
|
|
this.entropyFacade = entropyFacade
|
|
|
|
|
this.workerFacade = workerFacade
|
2024-11-29 10:17:40 +01:00
|
|
|
this.bulkMailLoader = bulkMailLoader
|
2024-12-09 15:31:25 +01:00
|
|
|
this.mailExportFacade = mailExportFacade
|
2024-07-10 16:40:04 +02:00
|
|
|
this.connectivityModel = new WebsocketConnectivityModel(eventBus)
|
2024-08-07 16:29:40 +02:00
|
|
|
this.mailboxModel = new MailboxModel(this.eventController, this.entityClient, this.logins)
|
2024-07-15 18:08:18 +02:00
|
|
|
this.mailModel = new MailModel(
|
|
|
|
|
notifications,
|
2024-08-07 16:29:40 +02:00
|
|
|
this.mailboxModel,
|
2024-07-15 18:08:18 +02:00
|
|
|
this.eventController,
|
|
|
|
|
this.entityClient,
|
|
|
|
|
this.logins,
|
2024-08-07 16:29:40 +02:00
|
|
|
this.mailFacade,
|
2024-07-15 18:08:18 +02:00
|
|
|
this.connectivityModel,
|
|
|
|
|
this.inboxRuleHanlder(),
|
|
|
|
|
)
|
2024-07-10 16:40:04 +02:00
|
|
|
this.operationProgressTracker = new OperationProgressTracker()
|
2024-07-09 17:17:32 +02:00
|
|
|
this.infoMessageHandler = new InfoMessageHandler((state: SearchIndexStateInfo) => {
|
|
|
|
|
mailLocator.search.indexState(state)
|
|
|
|
|
})
|
2024-07-10 16:40:04 +02:00
|
|
|
|
2024-08-13 10:32:57 +02:00
|
|
|
this.usageTestModel = new UsageTestModel(
|
|
|
|
|
{
|
|
|
|
|
[StorageBehavior.Persist]: deviceConfig,
|
|
|
|
|
[StorageBehavior.Ephemeral]: new EphemeralUsageTestStorage(),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
now(): number {
|
|
|
|
|
return Date.now()
|
|
|
|
|
},
|
|
|
|
|
timeZone(): string {
|
|
|
|
|
throw new Error("Not implemented by this provider")
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
this.serviceExecutor,
|
|
|
|
|
this.entityClient,
|
|
|
|
|
this.logins,
|
|
|
|
|
this.eventController,
|
|
|
|
|
() => this.usageTestController,
|
|
|
|
|
)
|
|
|
|
|
this.usageTestController = new UsageTestController(this.usageTestModel)
|
2024-07-10 16:40:04 +02:00
|
|
|
this.Const = Const
|
|
|
|
|
if (!isBrowser()) {
|
|
|
|
|
const { WebDesktopFacade } = await import("../common/native/main/WebDesktopFacade")
|
|
|
|
|
const { WebMobileFacade } = await import("../common/native/main/WebMobileFacade.js")
|
|
|
|
|
const { WebCommonNativeFacade } = await import("../common/native/main/WebCommonNativeFacade.js")
|
|
|
|
|
const { WebInterWindowEventFacade } = await import("../common/native/main/WebInterWindowEventFacade.js")
|
|
|
|
|
const { WebAuthnFacadeSendDispatcher } = await import("../common/native/common/generatedipc/WebAuthnFacadeSendDispatcher.js")
|
2024-10-02 13:09:17 +02:00
|
|
|
const { OpenMailboxHandler } = await import("./native/main/OpenMailboxHandler.js")
|
2024-07-10 16:40:04 +02:00
|
|
|
const { createNativeInterfaces, createDesktopInterfaces } = await import("../common/native/main/NativeInterfaceFactory.js")
|
2024-10-02 13:09:17 +02:00
|
|
|
const openMailboxHandler = new OpenMailboxHandler(this.logins, this.mailModel, this.mailboxModel)
|
|
|
|
|
const { OpenCalendarHandler } = await import("../common/native/main/OpenCalendarHandler.js")
|
|
|
|
|
const openCalendarHandler = new OpenCalendarHandler(this.logins)
|
2024-11-27 16:07:31 +01:00
|
|
|
const { OpenSettingsHandler } = await import("../common/native/main/OpenSettingsHandler.js")
|
|
|
|
|
const openSettingsHandler = new OpenSettingsHandler(this.logins)
|
2024-07-03 11:08:01 +02:00
|
|
|
|
2024-12-04 12:00:00 +01:00
|
|
|
this.webMobileFacade = new WebMobileFacade(this.connectivityModel, MAIL_PREFIX)
|
2024-08-07 16:29:40 +02:00
|
|
|
|
2024-07-10 16:40:04 +02:00
|
|
|
this.nativeInterfaces = createNativeInterfaces(
|
|
|
|
|
this.webMobileFacade,
|
2024-07-15 18:08:18 +02:00
|
|
|
new WebDesktopFacade(this.logins, async () => this.native),
|
2024-07-10 16:40:04 +02:00
|
|
|
new WebInterWindowEventFacade(this.logins, windowFacade, deviceConfig),
|
2024-07-15 18:08:18 +02:00
|
|
|
new WebCommonNativeFacade(
|
|
|
|
|
this.logins,
|
2024-08-07 16:29:40 +02:00
|
|
|
this.mailboxModel,
|
2024-07-15 18:08:18 +02:00
|
|
|
this.usageTestController,
|
|
|
|
|
async () => this.fileApp,
|
|
|
|
|
async () => this.pushService,
|
2024-08-15 15:35:08 +02:00
|
|
|
this.handleFileImport.bind(this),
|
2024-10-02 13:09:17 +02:00
|
|
|
(userId, address, requestedPath) => openMailboxHandler.openMailbox(userId, address, requestedPath),
|
|
|
|
|
(userId) => openCalendarHandler.openCalendar(userId),
|
2024-08-19 13:45:56 +02:00
|
|
|
AppType.Integrated,
|
2024-11-27 16:07:31 +01:00
|
|
|
(path) => openSettingsHandler.openSettings(path),
|
2024-07-15 18:08:18 +02:00
|
|
|
),
|
2024-07-10 16:40:04 +02:00
|
|
|
cryptoFacade,
|
|
|
|
|
calendarFacade,
|
|
|
|
|
this.entityClient,
|
|
|
|
|
this.logins,
|
2024-07-03 11:08:01 +02:00
|
|
|
AppType.Integrated,
|
2024-07-10 16:40:04 +02:00
|
|
|
)
|
|
|
|
|
|
2024-11-19 15:42:39 +01:00
|
|
|
this.credentialsProvider = await this.createCredentialsProvider()
|
2024-07-10 16:40:04 +02:00
|
|
|
if (isElectronClient()) {
|
|
|
|
|
const desktopInterfaces = createDesktopInterfaces(this.native)
|
|
|
|
|
this.searchTextFacade = desktopInterfaces.searchTextFacade
|
|
|
|
|
this.interWindowEventSender = desktopInterfaces.interWindowEventSender
|
|
|
|
|
this.webAuthn = new WebauthnClient(new WebAuthnFacadeSendDispatcher(this.native), this.domainConfigProvider(), isApp())
|
|
|
|
|
if (isDesktop()) {
|
|
|
|
|
this.desktopSettingsFacade = desktopInterfaces.desktopSettingsFacade
|
|
|
|
|
this.desktopSystemFacade = desktopInterfaces.desktopSystemFacade
|
2025-01-09 14:13:53 +01:00
|
|
|
this.mailImporter = new MailImporter(
|
|
|
|
|
this.domainConfigProvider(),
|
|
|
|
|
this.logins,
|
|
|
|
|
this.mailboxModel,
|
|
|
|
|
this.entityClient,
|
|
|
|
|
this.eventController,
|
|
|
|
|
this.credentialsProvider,
|
|
|
|
|
desktopInterfaces.nativeMailImportFacade,
|
|
|
|
|
openSettingsHandler,
|
|
|
|
|
)
|
2024-11-29 10:17:40 +01:00
|
|
|
this.exportFacade = desktopInterfaces.exportFacade
|
2024-07-10 16:40:04 +02:00
|
|
|
}
|
|
|
|
|
} else if (isAndroidApp() || isIOSApp()) {
|
|
|
|
|
const { SystemPermissionHandler } = await import("../common/native/main/SystemPermissionHandler.js")
|
|
|
|
|
this.systemPermissionHandler = new SystemPermissionHandler(this.systemFacade)
|
|
|
|
|
this.webAuthn = new WebauthnClient(new WebAuthnFacadeSendDispatcher(this.native), this.domainConfigProvider(), isApp())
|
|
|
|
|
}
|
2024-11-19 15:42:39 +01:00
|
|
|
} else {
|
|
|
|
|
this.credentialsProvider = await this.createCredentialsProvider()
|
2024-07-10 16:40:04 +02:00
|
|
|
}
|
2024-11-19 15:42:39 +01:00
|
|
|
|
2024-07-10 16:40:04 +02:00
|
|
|
if (this.webAuthn == null) {
|
|
|
|
|
this.webAuthn = new WebauthnClient(
|
|
|
|
|
new BrowserWebauthn(navigator.credentials, this.domainConfigProvider().getCurrentDomainConfig()),
|
|
|
|
|
this.domainConfigProvider(),
|
|
|
|
|
isApp(),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
this.secondFactorHandler = new SecondFactorHandler(
|
|
|
|
|
this.eventController,
|
|
|
|
|
this.entityClient,
|
|
|
|
|
this.webAuthn,
|
|
|
|
|
this.loginFacade,
|
|
|
|
|
this.domainConfigProvider(),
|
|
|
|
|
)
|
2024-11-19 15:42:39 +01:00
|
|
|
|
2024-07-25 17:54:06 +02:00
|
|
|
this.loginListener = new PageContextLoginListener(this.secondFactorHandler, this.credentialsProvider)
|
2024-07-10 16:40:04 +02:00
|
|
|
this.random = random
|
|
|
|
|
|
|
|
|
|
this.newsModel = new NewsModel(this.serviceExecutor, deviceConfig, async (name: string) => {
|
|
|
|
|
switch (name) {
|
2025-01-03 10:16:07 +01:00
|
|
|
case "usageOptIn": {
|
2024-07-10 16:40:04 +02:00
|
|
|
const { UsageOptInNews } = await import("../common/misc/news/items/UsageOptInNews.js")
|
|
|
|
|
return new UsageOptInNews(this.newsModel, this.usageTestModel)
|
2025-01-03 10:16:07 +01:00
|
|
|
}
|
|
|
|
|
case "recoveryCode": {
|
2024-07-10 16:40:04 +02:00
|
|
|
const { RecoveryCodeNews } = await import("../common/misc/news/items/RecoveryCodeNews.js")
|
|
|
|
|
return new RecoveryCodeNews(this.newsModel, this.logins.getUserController(), this.recoverCodeFacade)
|
2025-01-03 10:16:07 +01:00
|
|
|
}
|
|
|
|
|
case "pinBiometrics": {
|
2024-07-10 16:40:04 +02:00
|
|
|
const { PinBiometricsNews } = await import("../common/misc/news/items/PinBiometricsNews.js")
|
|
|
|
|
return new PinBiometricsNews(this.newsModel, this.credentialsProvider, this.logins.getUserController().userId)
|
2025-01-03 10:16:07 +01:00
|
|
|
}
|
|
|
|
|
case "referralLink": {
|
2024-07-10 16:40:04 +02:00
|
|
|
const { ReferralLinkNews } = await import("../common/misc/news/items/ReferralLinkNews.js")
|
|
|
|
|
const dateProvider = await this.noZoneDateProvider()
|
|
|
|
|
return new ReferralLinkNews(this.newsModel, dateProvider, this.logins.getUserController())
|
2025-01-03 10:16:07 +01:00
|
|
|
}
|
|
|
|
|
case "richNotifications": {
|
2024-07-30 18:36:45 +02:00
|
|
|
const { RichNotificationsNews } = await import("../common/misc/news/items/RichNotificationsNews.js")
|
|
|
|
|
return new RichNotificationsNews(this.newsModel, isApp() || isDesktop() ? this.pushService : null)
|
2025-01-03 10:16:07 +01:00
|
|
|
}
|
2024-07-10 16:40:04 +02:00
|
|
|
default:
|
|
|
|
|
console.log(`No implementation for news named '${name}'`)
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
this.fileController =
|
|
|
|
|
this.nativeInterfaces == null
|
|
|
|
|
? new FileControllerBrowser(blobFacade, guiDownload)
|
|
|
|
|
: new FileControllerNative(blobFacade, guiDownload, this.nativeInterfaces.fileApp)
|
|
|
|
|
|
|
|
|
|
const { ContactModel } = await import("../common/contactsFunctionality/ContactModel.js")
|
2024-08-20 18:03:03 +02:00
|
|
|
this.contactModel = new ContactModel(
|
|
|
|
|
this.entityClient,
|
|
|
|
|
this.logins,
|
|
|
|
|
this.eventController,
|
|
|
|
|
async (query: string, field: string, minSuggestionCount: number, maxResults?: number) => {
|
|
|
|
|
const { createRestriction } = await import("./search/model/SearchUtils.js")
|
|
|
|
|
return mailLocator.searchFacade.search(
|
|
|
|
|
query,
|
|
|
|
|
createRestriction(SearchCategoryTypes.contact, null, null, field, [], null),
|
|
|
|
|
minSuggestionCount,
|
|
|
|
|
maxResults,
|
|
|
|
|
)
|
|
|
|
|
},
|
|
|
|
|
)
|
2024-07-10 16:40:04 +02:00
|
|
|
this.minimizedMailModel = new MinimizedMailEditorViewModel()
|
2024-07-15 18:08:18 +02:00
|
|
|
|
|
|
|
|
// THEME
|
|
|
|
|
// We need it because we want to run tests in node and real HTMLSanitizer does not work there.
|
|
|
|
|
const sanitizerStub: Partial<HtmlSanitizer> = {
|
|
|
|
|
sanitizeHTML: () => {
|
|
|
|
|
return {
|
|
|
|
|
html: "",
|
|
|
|
|
blockedExternalContent: 0,
|
|
|
|
|
inlineImageCids: [],
|
|
|
|
|
links: [],
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
sanitizeSVG(svg, configExtra?) {
|
|
|
|
|
throw new Error("stub!")
|
|
|
|
|
},
|
|
|
|
|
sanitizeFragment(html, configExtra?) {
|
|
|
|
|
throw new Error("stub!")
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
const selectedThemeFacade =
|
|
|
|
|
isApp() || isDesktop() ? new NativeThemeFacade(new LazyLoaded<ThemeFacade>(async () => mailLocator.themeFacade)) : new WebThemeFacade(deviceConfig)
|
|
|
|
|
const lazySanitizer = isTest()
|
|
|
|
|
? () => Promise.resolve(sanitizerStub as HtmlSanitizer)
|
|
|
|
|
: () => import("../common/misc/HtmlSanitizer").then(({ htmlSanitizer }) => htmlSanitizer)
|
|
|
|
|
|
2024-09-09 16:26:42 +02:00
|
|
|
this.themeController = new ThemeController(theme, selectedThemeFacade, lazySanitizer, AppType.Mail)
|
2024-07-15 18:08:18 +02:00
|
|
|
|
|
|
|
|
// For native targets WebCommonNativeFacade notifies themeController because Android and Desktop do not seem to work reliably via media queries
|
|
|
|
|
if (selectedThemeFacade instanceof WebThemeFacade) {
|
|
|
|
|
selectedThemeFacade.addDarkListener(() => mailLocator.themeController.reloadTheme())
|
|
|
|
|
}
|
2024-07-10 16:40:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
readonly calendarModel: () => Promise<CalendarModel> = lazyMemoized(async () => {
|
2024-07-09 17:17:32 +02:00
|
|
|
const { DefaultDateProvider } = await import("../common/calendar/date/CalendarUtils")
|
2024-07-10 16:40:04 +02:00
|
|
|
const { CalendarModel } = await import("../calendar-app/calendar/model/CalendarModel")
|
|
|
|
|
const timeZone = new DefaultDateProvider().timeZone()
|
|
|
|
|
return new CalendarModel(
|
|
|
|
|
notifications,
|
|
|
|
|
this.alarmScheduler,
|
|
|
|
|
this.eventController,
|
|
|
|
|
this.serviceExecutor,
|
|
|
|
|
this.logins,
|
|
|
|
|
this.progressTracker,
|
|
|
|
|
this.entityClient,
|
2024-08-07 16:29:40 +02:00
|
|
|
this.mailboxModel,
|
2024-07-10 16:40:04 +02:00
|
|
|
this.calendarFacade,
|
|
|
|
|
this.fileController,
|
|
|
|
|
timeZone,
|
2024-08-13 10:33:13 +02:00
|
|
|
!isBrowser() ? this.externalCalendarFacade : null,
|
|
|
|
|
deviceConfig,
|
2024-09-11 16:05:47 +02:00
|
|
|
!isBrowser() ? this.pushService : null,
|
2025-02-17 06:48:28 +01:00
|
|
|
this.syncTracker,
|
2024-07-10 16:40:04 +02:00
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
readonly calendarInviteHandler: () => Promise<CalendarInviteHandler> = lazyMemoized(async () => {
|
|
|
|
|
const { CalendarInviteHandler } = await import("../calendar-app/calendar/view/CalendarInvites.js")
|
|
|
|
|
const { calendarNotificationSender } = await import("../calendar-app/calendar/view/CalendarNotificationSender.js")
|
2024-08-07 16:29:40 +02:00
|
|
|
return new CalendarInviteHandler(this.mailboxModel, await this.calendarModel(), this.logins, calendarNotificationSender, (...arg) =>
|
2024-07-10 16:40:04 +02:00
|
|
|
this.sendMailModel(...arg),
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
2024-08-15 15:35:08 +02:00
|
|
|
private async handleFileImport(filesUris: ReadonlyArray<string>) {
|
|
|
|
|
const files = await this.fileApp.getFilesMetaData(filesUris)
|
2024-08-19 13:45:56 +02:00
|
|
|
const areAllFilesVCard = files.every((file) => file.mimeType === VCARD_MIME_TYPES.X_VCARD || file.mimeType === VCARD_MIME_TYPES.VCARD)
|
|
|
|
|
const areAllFilesICS = files.every((file) => file.mimeType === CALENDAR_MIME_TYPE)
|
2024-11-19 15:42:39 +01:00
|
|
|
const areAllFilesMail = files.every((file) => file.mimeType === MAIL_MIME_TYPES.EML || file.mimeType === MAIL_MIME_TYPES.MBOX)
|
2024-08-15 15:35:08 +02:00
|
|
|
|
|
|
|
|
if (areAllFilesVCard) {
|
|
|
|
|
const importer = await this.contactImporter()
|
|
|
|
|
const { parseContacts } = await import("../mail-app/contacts/ContactImporter.js")
|
|
|
|
|
// For now, we just handle .vcf files, so we don't need to care about the file type
|
|
|
|
|
const contacts = await parseContacts(files, this.fileApp)
|
|
|
|
|
const vCardData = contacts.join("\n")
|
|
|
|
|
const contactListId = assertNotNull(await this.contactModel.getContactListId())
|
|
|
|
|
|
|
|
|
|
await importer.importContactsFromFile(vCardData, contactListId)
|
|
|
|
|
} else if (areAllFilesICS) {
|
|
|
|
|
const calendarModel = await this.calendarModel()
|
|
|
|
|
const groupSettings = this.logins.getUserController().userSettingsGroupRoot.groupSettings
|
|
|
|
|
const calendarInfos = await calendarModel.getCalendarInfos()
|
|
|
|
|
const groupColors: Map<Id, string> = groupSettings.reduce((acc, gc) => {
|
|
|
|
|
acc.set(gc.group, gc.color)
|
|
|
|
|
return acc
|
|
|
|
|
}, new Map())
|
|
|
|
|
|
2024-08-19 13:39:31 +02:00
|
|
|
const { calendarSelectionDialog, parseCalendarFile } = await import("../common/calendar/import/CalendarImporter.js")
|
2024-08-13 10:33:13 +02:00
|
|
|
const { handleCalendarImport } = await import("../common/calendar/import/CalendarImporterDialog.js")
|
2024-08-15 15:35:08 +02:00
|
|
|
|
|
|
|
|
let parsedEvents: ParsedEvent[] = []
|
|
|
|
|
|
|
|
|
|
for (const fileRef of files) {
|
|
|
|
|
const dataFile = await this.fileApp.readDataFile(fileRef.location)
|
|
|
|
|
if (dataFile == null) continue
|
|
|
|
|
|
|
|
|
|
const data = parseCalendarFile(dataFile)
|
|
|
|
|
parsedEvents.push(...data.contents)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
calendarSelectionDialog(Array.from(calendarInfos.values()), this.logins.getUserController(), groupColors, (dialog, selectedCalendar) => {
|
|
|
|
|
dialog.close()
|
2024-08-13 10:33:13 +02:00
|
|
|
handleCalendarImport(selectedCalendar.groupRoot, parsedEvents)
|
2024-08-15 15:35:08 +02:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-10 16:40:04 +02:00
|
|
|
private alarmScheduler: () => Promise<AlarmScheduler> = lazyMemoized(async () => {
|
2024-07-09 17:17:32 +02:00
|
|
|
const { AlarmScheduler } = await import("../common/calendar/date/AlarmScheduler")
|
|
|
|
|
const { DefaultDateProvider } = await import("../common/calendar/date/CalendarUtils")
|
2024-07-10 16:40:04 +02:00
|
|
|
const dateProvider = new DefaultDateProvider()
|
|
|
|
|
return new AlarmScheduler(dateProvider, await this.scheduler())
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
private async scheduler(): Promise<SchedulerImpl> {
|
|
|
|
|
const dateProvider = await this.noZoneDateProvider()
|
|
|
|
|
return new SchedulerImpl(dateProvider, window, window)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async calendarEventPreviewModel(selectedEvent: CalendarEvent, calendars: ReadonlyMap<string, CalendarInfo>): Promise<CalendarEventPreviewViewModel> {
|
|
|
|
|
const { findAttendeeInAddresses } = await import("../common/api/common/utils/CommonCalendarUtils.js")
|
|
|
|
|
const { getEventType } = await import("../calendar-app/calendar/gui/CalendarGuiUtils.js")
|
|
|
|
|
const { CalendarEventPreviewViewModel } = await import("../calendar-app/calendar/gui/eventpopup/CalendarEventPreviewViewModel.js")
|
|
|
|
|
|
2024-08-07 16:29:40 +02:00
|
|
|
const mailboxDetails = await this.mailboxModel.getUserMailboxDetails()
|
2024-07-10 16:40:04 +02:00
|
|
|
|
2024-08-07 16:29:40 +02:00
|
|
|
const mailboxProperties = await this.mailboxModel.getMailboxProperties(mailboxDetails.mailboxGroupRoot)
|
2024-07-10 16:40:04 +02:00
|
|
|
|
|
|
|
|
const userController = this.logins.getUserController()
|
|
|
|
|
const customer = await userController.loadCustomer()
|
|
|
|
|
const ownMailAddresses = getEnabledMailAddressesWithUser(mailboxDetails, userController.userGroupInfo)
|
|
|
|
|
const ownAttendee: CalendarEventAttendee | null = findAttendeeInAddresses(selectedEvent.attendees, ownMailAddresses)
|
2024-08-13 10:33:13 +02:00
|
|
|
const eventType = getEventType(selectedEvent, calendars, ownMailAddresses, userController)
|
2024-07-10 16:40:04 +02:00
|
|
|
const hasBusinessFeature = isCustomizationEnabledForCustomer(customer, FeatureType.BusinessFeatureEnabled) || (await userController.isNewPaidPlan())
|
|
|
|
|
const lazyIndexEntry = async () => (selectedEvent.uid != null ? this.calendarFacade.getEventsByUid(selectedEvent.uid) : null)
|
|
|
|
|
const popupModel = new CalendarEventPreviewViewModel(
|
|
|
|
|
selectedEvent,
|
|
|
|
|
await this.calendarModel(),
|
|
|
|
|
eventType,
|
|
|
|
|
hasBusinessFeature,
|
|
|
|
|
ownAttendee,
|
|
|
|
|
lazyIndexEntry,
|
|
|
|
|
async (mode: CalendarOperation) => this.calendarEventModel(mode, selectedEvent, mailboxDetails, mailboxProperties, null),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// If we have a preview model we want to display the description
|
|
|
|
|
// so makes sense to already sanitize it after building the event
|
|
|
|
|
await popupModel.sanitizeDescription()
|
|
|
|
|
|
|
|
|
|
return popupModel
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-09 15:45:38 +02:00
|
|
|
async calendarContactPreviewModel(event: CalendarEvent, contact: Contact, canEdit: boolean): Promise<CalendarContactPreviewViewModel> {
|
|
|
|
|
const { CalendarContactPreviewViewModel } = await import("../calendar-app/calendar/gui/eventpopup/CalendarContactPreviewViewModel.js")
|
|
|
|
|
return new CalendarContactPreviewViewModel(event, contact, canEdit)
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-26 19:05:48 +02:00
|
|
|
readonly nativeContactsSyncManager: () => NativeContactsSyncManager = lazyMemoized(() => {
|
2024-07-10 16:40:04 +02:00
|
|
|
assert(isApp(), "isApp")
|
|
|
|
|
return new NativeContactsSyncManager(this.logins, this.mobileContactsFacade, this.entityClient, this.eventController, this.contactModel, deviceConfig)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
postLoginActions: () => Promise<PostLoginActions> = lazyMemoized(async () => {
|
|
|
|
|
const { PostLoginActions } = await import("../common/login/PostLoginActions")
|
|
|
|
|
return new PostLoginActions(
|
|
|
|
|
this.credentialsProvider,
|
|
|
|
|
this.secondFactorHandler,
|
|
|
|
|
this.connectivityModel,
|
|
|
|
|
this.logins,
|
|
|
|
|
await this.noZoneDateProvider(),
|
|
|
|
|
this.entityClient,
|
|
|
|
|
this.userManagementFacade,
|
|
|
|
|
this.customerFacade,
|
2024-07-24 13:06:03 +02:00
|
|
|
this.themeController,
|
2025-02-17 06:48:28 +01:00
|
|
|
this.syncTracker,
|
2024-07-10 16:40:04 +02:00
|
|
|
() => this.showSetupWizard(),
|
2024-10-10 16:05:02 +02:00
|
|
|
() => this.setUpClientOnlyCalendars(),
|
2025-02-14 15:21:55 +01:00
|
|
|
() => this.updateClients(),
|
2024-07-10 16:40:04 +02:00
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
showSetupWizard = async () => {
|
|
|
|
|
if (isApp()) {
|
|
|
|
|
const { showSetupWizard } = await import("../common/native/main/wizard/SetupWizard.js")
|
|
|
|
|
return showSetupWizard(
|
|
|
|
|
this.systemPermissionHandler,
|
|
|
|
|
this.webMobileFacade,
|
|
|
|
|
await this.contactImporter(),
|
|
|
|
|
this.systemFacade,
|
|
|
|
|
this.credentialsProvider,
|
|
|
|
|
await this.nativeContactsSyncManager(),
|
|
|
|
|
deviceConfig,
|
|
|
|
|
true,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-08-30 16:41:51 +02:00
|
|
|
|
2024-08-29 15:01:11 +02:00
|
|
|
setUpClientOnlyCalendars() {
|
|
|
|
|
let configs = deviceConfig.getClientOnlyCalendars()
|
|
|
|
|
|
|
|
|
|
for (const [id, name] of CLIENT_ONLY_CALENDARS.entries()) {
|
|
|
|
|
const calendarId = `${this.logins.getUserController().userId}#${id}`
|
|
|
|
|
const config = configs.get(calendarId)
|
2024-11-07 17:28:05 +01:00
|
|
|
if (!config)
|
|
|
|
|
deviceConfig.updateClientOnlyCalendars(calendarId, {
|
|
|
|
|
name: lang.get(name),
|
|
|
|
|
color: DEFAULT_CLIENT_ONLY_CALENDAR_COLORS.get(id)!,
|
|
|
|
|
})
|
2024-08-29 15:01:11 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-14 15:21:55 +01:00
|
|
|
async updateClients(): Promise<void> {
|
|
|
|
|
if (isDesktop()) {
|
|
|
|
|
await this.desktopSettingsFacade.manualUpdate()
|
|
|
|
|
} else if (isApp()) {
|
|
|
|
|
if (isAndroidApp()) {
|
|
|
|
|
this.nativeInterfaces?.mobileSystemFacade.openLink("market://details?id=de.tutao.tutanota")
|
|
|
|
|
} else if (isIOSApp()) {
|
|
|
|
|
this.nativeInterfaces?.mobileSystemFacade.openLink("itms-apps://itunes.apple.com/app/id922429609")
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// web version
|
|
|
|
|
const registration = await navigator.serviceWorker?.getRegistration()
|
|
|
|
|
if (registration?.waiting) {
|
|
|
|
|
registration.waiting.postMessage("update")
|
|
|
|
|
} else {
|
|
|
|
|
windowFacade.reload({})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-10 16:40:04 +02:00
|
|
|
readonly credentialFormatMigrator: () => Promise<CredentialFormatMigrator> = lazyMemoized(async () => {
|
|
|
|
|
const { CredentialFormatMigrator } = await import("../common/misc/credentials/CredentialFormatMigrator.js")
|
|
|
|
|
if (isDesktop()) {
|
|
|
|
|
return new CredentialFormatMigrator(deviceConfig, this.nativeCredentialsFacade, null)
|
|
|
|
|
} else if (isApp()) {
|
|
|
|
|
return new CredentialFormatMigrator(deviceConfig, this.nativeCredentialsFacade, this.systemFacade)
|
|
|
|
|
} else {
|
|
|
|
|
return new CredentialFormatMigrator(deviceConfig, null, null)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
async addNotificationEmailDialog(): Promise<AddNotificationEmailDialog> {
|
|
|
|
|
const { AddNotificationEmailDialog } = await import("../mail-app/settings/AddNotificationEmailDialog.js")
|
|
|
|
|
return new AddNotificationEmailDialog(this.logins, this.entityClient)
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-21 15:43:00 +01:00
|
|
|
readonly mailExportController: () => Promise<MailExportController> = lazyMemoized(async () => {
|
2024-11-29 10:17:40 +01:00
|
|
|
const { htmlSanitizer } = await import("../common/misc/HtmlSanitizer")
|
|
|
|
|
const { MailExportController } = await import("./native/main/MailExportController.js")
|
2024-12-10 16:06:43 +01:00
|
|
|
return new MailExportController(this.mailExportFacade, htmlSanitizer, this.exportFacade, this.logins, this.mailboxModel, await this.scheduler())
|
2024-11-21 15:43:00 +01:00
|
|
|
})
|
|
|
|
|
|
2024-07-10 16:40:04 +02:00
|
|
|
/**
|
|
|
|
|
* Factory method for credentials provider that will return an instance injected with the implementations appropriate for the platform.
|
|
|
|
|
*/
|
|
|
|
|
private async createCredentialsProvider(): Promise<CredentialsProvider> {
|
|
|
|
|
const { CredentialsProvider } = await import("../common/misc/credentials/CredentialsProvider.js")
|
|
|
|
|
if (isDesktop() || isApp()) {
|
|
|
|
|
return new CredentialsProvider(this.nativeCredentialsFacade, this.sqlCipherFacade, isDesktop() ? this.interWindowEventSender : null)
|
|
|
|
|
} else {
|
|
|
|
|
const { WebCredentialsFacade } = await import("../common/misc/credentials/WebCredentialsFacade.js")
|
|
|
|
|
return new CredentialsProvider(new WebCredentialsFacade(deviceConfig), null, null)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export type IMailLocator = Readonly<MailLocator>
|
|
|
|
|
|
|
|
|
|
export const mailLocator: IMailLocator = new MailLocator()
|
|
|
|
|
|
|
|
|
|
if (typeof window !== "undefined") {
|
|
|
|
|
window.tutao.locator = mailLocator
|
|
|
|
|
}
|