From a6cd7a23f1ff3b961530ab652b9e7f4331d9f8c3 Mon Sep 17 00:00:00 2001 From: jhm <17314077+jomapp@users.noreply.github.com> Date: Tue, 14 Jan 2025 11:30:05 +0100 Subject: [PATCH] wip fix import ui states --- .../generated_ipc/UnencryptedCredentials.kt | 1 - .../GeneratedIpc/UnencryptedCredentials.swift | 5 +- .../facades/NativeMailImportFacade.json | 16 ++++-- ipc-schema/types/UnencryptedCredentials.json | 3 +- .../mailimport/DesktopMailImportFacade.ts | 14 +++--- .../generatedipc/NativeMailImportFacade.ts | 9 ++-- ...NativeMailImportFacadeReceiveDispatcher.ts | 19 ++++--- .../NativeMailImportFacadeSendDispatcher.ts | 7 +-- .../generatedipc/UnencryptedCredentials.ts | 1 - src/mail-app/mail/import/MailImporter.ts | 50 ++++++++++--------- .../settings/MailImportSettingsViewer.ts | 8 +-- 11 files changed, 67 insertions(+), 66 deletions(-) diff --git a/app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/UnencryptedCredentials.kt b/app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/UnencryptedCredentials.kt index 3330445ee068..20170615e6aa 100644 --- a/app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/UnencryptedCredentials.kt +++ b/app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/UnencryptedCredentials.kt @@ -17,5 +17,4 @@ data class UnencryptedCredentials( val databaseKey: DataWrapper?, val encryptedPassword: String, val encryptedPassphraseKey: DataWrapper?, - val apiUrl: String, ) diff --git a/app-ios/TutanotaSharedFramework/GeneratedIpc/UnencryptedCredentials.swift b/app-ios/TutanotaSharedFramework/GeneratedIpc/UnencryptedCredentials.swift index a4e70f5a7b0f..0384e9f65820 100644 --- a/app-ios/TutanotaSharedFramework/GeneratedIpc/UnencryptedCredentials.swift +++ b/app-ios/TutanotaSharedFramework/GeneratedIpc/UnencryptedCredentials.swift @@ -10,20 +10,17 @@ public struct UnencryptedCredentials : Codable { accessToken: String, databaseKey: DataWrapper?, encryptedPassword: String, - encryptedPassphraseKey: DataWrapper?, - apiUrl: String + encryptedPassphraseKey: DataWrapper? ) { self.credentialInfo = credentialInfo self.accessToken = accessToken self.databaseKey = databaseKey self.encryptedPassword = encryptedPassword self.encryptedPassphraseKey = encryptedPassphraseKey - self.apiUrl = apiUrl } public let credentialInfo: CredentialsInfo public let accessToken: String public let databaseKey: DataWrapper? public let encryptedPassword: String public let encryptedPassphraseKey: DataWrapper? - public let apiUrl: String } diff --git a/ipc-schema/facades/NativeMailImportFacade.json b/ipc-schema/facades/NativeMailImportFacade.json index 9e806aa8fd6c..8cd771e2e1d2 100644 --- a/ipc-schema/facades/NativeMailImportFacade.json +++ b/ipc-schema/facades/NativeMailImportFacade.json @@ -5,7 +5,7 @@ "receivers": ["desktop"], "doc": "Facade implemented by the native desktop client enabling mail imports, both from files, and via IMAP.", "methods": { - "getResumeableImport": { + "getResumableImport": { "doc": "@returns the mail import state id of the import that might be resumed", "arg": [ { @@ -16,6 +16,9 @@ }, { "unencryptedTutaCredentials": "UnencryptedCredentials" + }, + { + "apiUrl": "string" } ], "ret": "IdTuple?" @@ -26,17 +29,20 @@ { "mailboxId": "string" }, - { - "unencryptedTutaCredentials": "UnencryptedCredentials" - }, { "targetOwnerGroup": "string" }, { - "targetMailset": "List" + "targetMailSet": "List" }, { "filePaths": "List" + }, + { + "unencryptedTutaCredentials": "UnencryptedCredentials" + }, + { + "apiUrl": "string" } ], "ret": "IdTuple" diff --git a/ipc-schema/types/UnencryptedCredentials.json b/ipc-schema/types/UnencryptedCredentials.json index 79cda71df3b9..c9f584886c10 100644 --- a/ipc-schema/types/UnencryptedCredentials.json +++ b/ipc-schema/types/UnencryptedCredentials.json @@ -7,7 +7,6 @@ "accessToken": "string", "databaseKey": "bytes?", "encryptedPassword": "string", - "encryptedPassphraseKey": "bytes?", - "apiUrl": "string" + "encryptedPassphraseKey": "bytes?" } } diff --git a/src/common/desktop/mailimport/DesktopMailImportFacade.ts b/src/common/desktop/mailimport/DesktopMailImportFacade.ts index 49a1fb8e088d..a26b0fb9cc90 100644 --- a/src/common/desktop/mailimport/DesktopMailImportFacade.ts +++ b/src/common/desktop/mailimport/DesktopMailImportFacade.ts @@ -16,12 +16,13 @@ export class DesktopMailImportFacade implements NativeMailImportFacade { ImporterApi.deinitLog() } - async getResumeableImport( + async getResumableImport( mailboxId: string, targetOwnerGroup: string, unencryptedTutaCredentials: UnencryptedCredentials, + apiUrl: string, ): Promise { - const tutaCredentials = this.createTutaCredentials(unencryptedTutaCredentials) + const tutaCredentials = this.createTutaCredentials(unencryptedTutaCredentials, apiUrl) const importerApi = await ImporterApi.getResumableImport(mailboxId, this.configDirectory, targetOwnerGroup, tutaCredentials) if (importerApi != null) { @@ -36,12 +37,13 @@ export class DesktopMailImportFacade implements NativeMailImportFacade { async prepareNewImport( mailboxId: string, - unencryptedTutaCredentials: UnencryptedCredentials, targetOwnerGroup: string, targetMailset: readonly string[], filePaths: readonly string[], + unencryptedTutaCredentials: UnencryptedCredentials, + apiUrl: string, ): Promise { - const tutaCredentials = this.createTutaCredentials(unencryptedTutaCredentials) + const tutaCredentials = this.createTutaCredentials(unencryptedTutaCredentials, apiUrl) if (this.importerApis.has(mailboxId)) { // todo: error type? throw new Error("already have a running import for this mailbox") @@ -68,14 +70,14 @@ export class DesktopMailImportFacade implements NativeMailImportFacade { await importerApi.setProgressAction(progressAction) } - private createTutaCredentials(unencTutaCredentials: UnencryptedCredentials) { + private createTutaCredentials(unencTutaCredentials: UnencryptedCredentials, apiUrl: string) { const tutaCredentials: TutaCredentials = { accessToken: unencTutaCredentials?.accessToken, isInternalCredential: unencTutaCredentials.credentialInfo.type === CredentialType.Internal, encryptedPassphraseKey: unencTutaCredentials.encryptedPassphraseKey ? Array.from(unencTutaCredentials.encryptedPassphraseKey) : [], login: unencTutaCredentials.credentialInfo.login, userId: unencTutaCredentials.credentialInfo.userId, - apiUrl: unencTutaCredentials.apiUrl, + apiUrl: apiUrl, clientVersion: env.versionNumber, } return tutaCredentials diff --git a/src/common/native/common/generatedipc/NativeMailImportFacade.ts b/src/common/native/common/generatedipc/NativeMailImportFacade.ts index a1cc1d95b3cd..92ca9f4d1335 100644 --- a/src/common/native/common/generatedipc/NativeMailImportFacade.ts +++ b/src/common/native/common/generatedipc/NativeMailImportFacade.ts @@ -8,23 +8,22 @@ export interface NativeMailImportFacade { /** * @returns the mail import state id of the import that might be resumed */ - getResumeableImport(mailboxId: string, targetOwnerGroup: string, unencryptedTutaCredentials: UnencryptedCredentials): Promise + getResumableImport(mailboxId: string, targetOwnerGroup: string, unencryptedTutaCredentials: UnencryptedCredentials, apiUrl: string): Promise /** * set up a new import state for the given parameters and return the ID of the new state entity on the server */ prepareNewImport( mailboxId: string, - unencryptedTutaCredentials: UnencryptedCredentials, targetOwnerGroup: string, - targetMailset: ReadonlyArray, + targetMailSet: ReadonlyArray, filePaths: ReadonlyArray, + unencryptedTutaCredentials: UnencryptedCredentials, + apiUrl: string, ): Promise /** * Sets progress action for next import iteration */ setProgressAction(mailboxId: string, importProgressAction: number): Promise - - deinitLogger(): Promise } diff --git a/src/common/native/common/generatedipc/NativeMailImportFacadeReceiveDispatcher.ts b/src/common/native/common/generatedipc/NativeMailImportFacadeReceiveDispatcher.ts index 41682c447082..df8706c10b03 100644 --- a/src/common/native/common/generatedipc/NativeMailImportFacadeReceiveDispatcher.ts +++ b/src/common/native/common/generatedipc/NativeMailImportFacadeReceiveDispatcher.ts @@ -7,28 +7,27 @@ export class NativeMailImportFacadeReceiveDispatcher { constructor(private readonly facade: NativeMailImportFacade) {} async dispatch(method: string, arg: Array): Promise { switch (method) { - case "getResumeableImport": { + case "getResumableImport": { const mailboxId: string = arg[0] const targetOwnerGroup: string = arg[1] const unencryptedTutaCredentials: UnencryptedCredentials = arg[2] - return this.facade.getResumeableImport(mailboxId, targetOwnerGroup, unencryptedTutaCredentials) + const apiUrl: string = arg[3] + return this.facade.getResumableImport(mailboxId, targetOwnerGroup, unencryptedTutaCredentials, apiUrl) } case "prepareNewImport": { const mailboxId: string = arg[0] - const unencryptedTutaCredentials: UnencryptedCredentials = arg[1] - const targetOwnerGroup: string = arg[2] - const targetMailset: ReadonlyArray = arg[3] - const filePaths: ReadonlyArray = arg[4] - return this.facade.prepareNewImport(mailboxId, unencryptedTutaCredentials, targetOwnerGroup, targetMailset, filePaths) + const targetOwnerGroup: string = arg[1] + const targetMailSet: ReadonlyArray = arg[2] + const filePaths: ReadonlyArray = arg[3] + const unencryptedTutaCredentials: UnencryptedCredentials = arg[4] + const apiUrl: string = arg[5] + return this.facade.prepareNewImport(mailboxId, targetOwnerGroup, targetMailSet, filePaths, unencryptedTutaCredentials, apiUrl) } case "setProgressAction": { const mailboxId: string = arg[0] const importProgressAction: number = arg[1] return this.facade.setProgressAction(mailboxId, importProgressAction) } - case "deinitLogger": { - return this.facade.deinitLogger() - } } } } diff --git a/src/common/native/common/generatedipc/NativeMailImportFacadeSendDispatcher.ts b/src/common/native/common/generatedipc/NativeMailImportFacadeSendDispatcher.ts index 3e83eeaf3cf7..5e8f8f319421 100644 --- a/src/common/native/common/generatedipc/NativeMailImportFacadeSendDispatcher.ts +++ b/src/common/native/common/generatedipc/NativeMailImportFacadeSendDispatcher.ts @@ -7,8 +7,8 @@ interface NativeInterface { } export class NativeMailImportFacadeSendDispatcher implements NativeMailImportFacade { constructor(private readonly transport: NativeInterface) {} - async getResumeableImport(...args: Parameters) { - return this.transport.invokeNative("ipc", ["NativeMailImportFacade", "getResumeableImport", ...args]) + async getResumableImport(...args: Parameters) { + return this.transport.invokeNative("ipc", ["NativeMailImportFacade", "getResumableImport", ...args]) } async prepareNewImport(...args: Parameters) { return this.transport.invokeNative("ipc", ["NativeMailImportFacade", "prepareNewImport", ...args]) @@ -16,7 +16,4 @@ export class NativeMailImportFacadeSendDispatcher implements NativeMailImportFac async setProgressAction(...args: Parameters) { return this.transport.invokeNative("ipc", ["NativeMailImportFacade", "setProgressAction", ...args]) } - async deinitLogger(...args: Parameters) { - return this.transport.invokeNative("ipc", ["NativeMailImportFacade", "deinitLogger", ...args]) - } } diff --git a/src/common/native/common/generatedipc/UnencryptedCredentials.ts b/src/common/native/common/generatedipc/UnencryptedCredentials.ts index 68199c5649a2..802fc99f2760 100644 --- a/src/common/native/common/generatedipc/UnencryptedCredentials.ts +++ b/src/common/native/common/generatedipc/UnencryptedCredentials.ts @@ -10,5 +10,4 @@ export interface UnencryptedCredentials { readonly databaseKey: Uint8Array | null readonly encryptedPassword: string readonly encryptedPassphraseKey: Uint8Array | null - readonly apiUrl: string } diff --git a/src/mail-app/mail/import/MailImporter.ts b/src/mail-app/mail/import/MailImporter.ts index 32b666afb4a8..72b8119cf34a 100644 --- a/src/mail-app/mail/import/MailImporter.ts +++ b/src/mail-app/mail/import/MailImporter.ts @@ -43,7 +43,8 @@ export class MailImporter { private progress: number = DEFAULT_PROGRESS private finalisedImportStates: Map = new Map() - private activeImportState: IdTuple | null = null + private activeImportId: IdTuple | null = null + private activeImportStartTimestamp: number | null = null private uiStatus: UiImportStatus private eventController: EventController @@ -76,18 +77,19 @@ export class MailImporter { async initImportMailStates(): Promise { const importFacade = assertNotNull(this.nativeMailImportFacade) - if (this.activeImportState === null) { + if (this.activeImportId === null) { const mailbox = await this.getMailbox() - const mailOwnerGroup = (await this.mailboxModel.getUserMailboxDetails()).mailGroup + const mailOwnerGroupId = assertNotNull(mailbox._ownerGroup) const userId = this.loginController.getUserController().userId const unencryptedCredentials = assertNotNull(await this.credentialsProvider?.getDecryptedCredentialsByUserId(userId)) - this.activeImportState = await importFacade.getResumeableImport(mailbox._id, mailOwnerGroup._id, unencryptedCredentials) + const apiUrl = getApiBaseUrl(this.domainConfigProvider.getCurrentDomainConfig()) + this.activeImportId = await importFacade.getResumableImport(mailbox._id, mailOwnerGroupId, unencryptedCredentials, apiUrl) } - if (this.activeImportState) { + if (this.activeImportId) { // we can't use the result of loadAll (see below) as that might only read from offline cache and // not include a new ImportMailState that was created without sending an entity event - const importMailState = await this.entityClient.load(ImportMailStateTypeRef, this.activeImportState) + const importMailState = await this.entityClient.load(ImportMailStateTypeRef, this.activeImportId) const remoteStatus = parseInt(importMailState.status) as ImportStatus switch (remoteStatus) { @@ -105,7 +107,7 @@ export class MailImporter { const importMailStatesCollection = await this.entityClient.loadAll(ImportMailStateTypeRef, (await this.getMailbox()).mailImportStates) for (const importMailState of importMailStatesCollection) { - if (importMailState._id != this.activeImportState) { + if (importMailState._id != this.activeImportId) { this.updateFinalisedImport(elementIdPart(importMailState._id), importMailState) } } @@ -124,17 +126,20 @@ export class MailImporter { this.resetStatus() const apiUrl = getApiBaseUrl(this.domainConfigProvider.getCurrentDomainConfig()) - const ownerGroup = assertNotNull(targetFolder._ownerGroup) + const mailbox = await this.getMailbox() + const mailboxId = mailbox._id + const mailOwnerGroupId = assertNotNull(mailbox._ownerGroup) const userId = this.loginController.getUserController().userId const importFacade = assertNotNull(this.nativeMailImportFacade) - const unencryptedCredentials = assertNotNull(await this.credentialsProvider?.getDecryptedCredentialsByUserId(userId)) + const unencryptedCredentials = assertNotNull(await this.credentialsProvider?.getDecryptedCredentialsByUserId(userId)) this.uiStatus = UiImportStatus.Starting this.startProgressEstimation() m.redraw() - // todo: - // call setProgressAction::Continue + this.activeImportId = await importFacade.prepareNewImport(mailboxId, mailOwnerGroupId, targetFolder._id, filePaths, unencryptedCredentials, apiUrl) + const nativeImportFacade = assertNotNull(this.nativeMailImportFacade) + await nativeImportFacade.setProgressAction(mailboxId, ImportProgressAction.Continue) } async onPauseBtnClick() { @@ -153,7 +158,7 @@ export class MailImporter { async onResumeBtnClick() { if (!this.shouldShowResumeButton()) throw new ProgrammingError("can't change state to resuming") - if (!this.activeImportState) throw new ProgrammingError("can't change state to resuming") + if (!this.activeImportId) throw new ProgrammingError("can't change state to resuming") this.uiStatus = UiImportStatus.Resuming this.startProgressEstimation() @@ -165,7 +170,7 @@ export class MailImporter { } async onCancelBtnClick() { - if (!this.shouldShowCancelButton()) throw new ProgrammingError("can't change state to cancelling") + if (!this.shouldRenderCancelButton()) throw new ProgrammingError("can't change state to cancelling") this.uiStatus = UiImportStatus.Cancelling this.stopProgressEstimation() @@ -191,7 +196,7 @@ export class MailImporter { ) } - shouldShowPauseButton(): boolean { + shouldRenderPauseButton(): boolean { return this.uiStatus === UiImportStatus.Running || this.uiStatus === UiImportStatus.Starting || this.uiStatus === UiImportStatus.Pausing } @@ -207,7 +212,7 @@ export class MailImporter { return this.uiStatus === UiImportStatus.Resuming || this.uiStatus === UiImportStatus.Starting } - shouldShowCancelButton(): boolean { + shouldRenderCancelButton(): boolean { return ( this.uiStatus === UiImportStatus.Paused || this.uiStatus === UiImportStatus.Running || @@ -220,7 +225,7 @@ export class MailImporter { return this.uiStatus === UiImportStatus.Cancelling || this.uiStatus === UiImportStatus.Pausing || this.uiStatus === UiImportStatus.Starting } - shouldShowProcessedMails(): boolean { + shouldRenderProcessedMails(): boolean { return ( this.uiStatus === UiImportStatus.Running || this.uiStatus === UiImportStatus.Resuming || @@ -262,14 +267,12 @@ export class MailImporter { private startProgressEstimation() { clearInterval(this.progressEstimation) - + this.activeImportStartTimestamp = Date.now() this.progressEstimation = setInterval(() => { - let now = Date.now() let completedMails = this.progressMonitor?.workCompleted if (completedMails) { - // todo - // make it similar to: this.activeImport?.start_timestamp ?? now - let startTimestamp = null ?? now + let now = Date.now() + let startTimestamp = this.activeImportStartTimestamp ?? now let durationSinceStartSeconds = (now - startTimestamp) / 1000 let mailsPerSecond = completedMails / durationSinceStartSeconds let mailsPerSecondEstimate = Math.max(1, mailsPerSecond * PROGRESS_ESTIMATION_MAILS_PER_SECOND_SCALING_RATIO) @@ -283,10 +286,11 @@ export class MailImporter { private stopProgressEstimation() { clearInterval(this.progressEstimation) + this.activeImportStartTimestamp = null } async newImportStateFromServer(serverState: ImportMailState) { - const wasUpdatedForThisImport = isSameId(this.activeImportState ?? null, serverState._id) + const wasUpdatedForThisImport = isSameId(this.activeImportId ?? null, serverState._id) if (wasUpdatedForThisImport) { const remoteStatus = parseInt(serverState.status) as ImportStatus @@ -301,7 +305,7 @@ export class MailImporter { } private resetStatus() { - this.activeImportState = null + this.activeImportId = null this.progressMonitor = null this.progress = 0 this.stopProgressEstimation() diff --git a/src/mail-app/settings/MailImportSettingsViewer.ts b/src/mail-app/settings/MailImportSettingsViewer.ts index 90af177f765b..a7ced4441f5f 100644 --- a/src/mail-app/settings/MailImportSettingsViewer.ts +++ b/src/mail-app/settings/MailImportSettingsViewer.ts @@ -200,22 +200,22 @@ export class MailImportSettingsViewer implements UpdatableSettingsViewer { } let buttonControls = [] - if (this.mailImporter.shouldShowPauseButton()) { + if (this.mailImporter.shouldRenderPauseButton()) { buttonControls.push(m(IconButton, pauseMailImportIconButtonAttrs)) } if (this.mailImporter.shouldShowResumeButton()) { buttonControls.push(m(IconButton, resumeMailImportIconButtonAttrs)) } - if (this.mailImporter.shouldShowCancelButton()) { + if (this.mailImporter.shouldRenderCancelButton()) { buttonControls.push(m(IconButton, cancelMailImportIconButtonAttrs)) } return [ [ m( - ".flex-space-between.p.small.mt-l", + ".flex-space-between.p.small.mt-m", getReadableUiImportStatus(this.mailImporter.getUiStatus()), - this.mailImporter.shouldShowProcessedMails() ? processedMailsCountLabel : null, + this.mailImporter.shouldRenderProcessedMails() ? processedMailsCountLabel : null, ), ], [m(".flex-space-between.border-radius-big.mt-s.rel.nav-bg.full-width", this.renderMailImportProgressBar(), ...buttonControls)],