Skip to content

Commit

Permalink
wip fix import ui states
Browse files Browse the repository at this point in the history
  • Loading branch information
jomapp committed Jan 14, 2025
1 parent 3b1c83a commit a6cd7a2
Show file tree
Hide file tree
Showing 11 changed files with 67 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,4 @@ data class UnencryptedCredentials(
val databaseKey: DataWrapper?,
val encryptedPassword: String,
val encryptedPassphraseKey: DataWrapper?,
val apiUrl: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
16 changes: 11 additions & 5 deletions ipc-schema/facades/NativeMailImportFacade.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
{
Expand All @@ -16,6 +16,9 @@
},
{
"unencryptedTutaCredentials": "UnencryptedCredentials"
},
{
"apiUrl": "string"
}
],
"ret": "IdTuple?"
Expand All @@ -26,17 +29,20 @@
{
"mailboxId": "string"
},
{
"unencryptedTutaCredentials": "UnencryptedCredentials"
},
{
"targetOwnerGroup": "string"
},
{
"targetMailset": "List<string>"
"targetMailSet": "List<string>"
},
{
"filePaths": "List<string>"
},
{
"unencryptedTutaCredentials": "UnencryptedCredentials"
},
{
"apiUrl": "string"
}
],
"ret": "IdTuple"
Expand Down
3 changes: 1 addition & 2 deletions ipc-schema/types/UnencryptedCredentials.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"accessToken": "string",
"databaseKey": "bytes?",
"encryptedPassword": "string",
"encryptedPassphraseKey": "bytes?",
"apiUrl": "string"
"encryptedPassphraseKey": "bytes?"
}
}
14 changes: 8 additions & 6 deletions src/common/desktop/mailimport/DesktopMailImportFacade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ export class DesktopMailImportFacade implements NativeMailImportFacade {
ImporterApi.deinitLog()
}

async getResumeableImport(
async getResumableImport(
mailboxId: string,
targetOwnerGroup: string,
unencryptedTutaCredentials: UnencryptedCredentials,
apiUrl: string,
): Promise<readonly [string, string] | null> {
const tutaCredentials = this.createTutaCredentials(unencryptedTutaCredentials)
const tutaCredentials = this.createTutaCredentials(unencryptedTutaCredentials, apiUrl)
const importerApi = await ImporterApi.getResumableImport(mailboxId, this.configDirectory, targetOwnerGroup, tutaCredentials)

if (importerApi != null) {
Expand All @@ -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<readonly [string, string]> {
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")
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<IdTuple | null>
getResumableImport(mailboxId: string, targetOwnerGroup: string, unencryptedTutaCredentials: UnencryptedCredentials, apiUrl: string): Promise<IdTuple | null>

/**
* 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<string>,
targetMailSet: ReadonlyArray<string>,
filePaths: ReadonlyArray<string>,
unencryptedTutaCredentials: UnencryptedCredentials,
apiUrl: string,
): Promise<IdTuple>

/**
* Sets progress action for next import iteration
*/
setProgressAction(mailboxId: string, importProgressAction: number): Promise<void>

deinitLogger(): Promise<void>
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,27 @@ export class NativeMailImportFacadeReceiveDispatcher {
constructor(private readonly facade: NativeMailImportFacade) {}
async dispatch(method: string, arg: Array<any>): Promise<any> {
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<string> = arg[3]
const filePaths: ReadonlyArray<string> = arg[4]
return this.facade.prepareNewImport(mailboxId, unencryptedTutaCredentials, targetOwnerGroup, targetMailset, filePaths)
const targetOwnerGroup: string = arg[1]
const targetMailSet: ReadonlyArray<string> = arg[2]
const filePaths: ReadonlyArray<string> = 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()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,13 @@ interface NativeInterface {
}
export class NativeMailImportFacadeSendDispatcher implements NativeMailImportFacade {
constructor(private readonly transport: NativeInterface) {}
async getResumeableImport(...args: Parameters<NativeMailImportFacade["getResumeableImport"]>) {
return this.transport.invokeNative("ipc", ["NativeMailImportFacade", "getResumeableImport", ...args])
async getResumableImport(...args: Parameters<NativeMailImportFacade["getResumableImport"]>) {
return this.transport.invokeNative("ipc", ["NativeMailImportFacade", "getResumableImport", ...args])
}
async prepareNewImport(...args: Parameters<NativeMailImportFacade["prepareNewImport"]>) {
return this.transport.invokeNative("ipc", ["NativeMailImportFacade", "prepareNewImport", ...args])
}
async setProgressAction(...args: Parameters<NativeMailImportFacade["setProgressAction"]>) {
return this.transport.invokeNative("ipc", ["NativeMailImportFacade", "setProgressAction", ...args])
}
async deinitLogger(...args: Parameters<NativeMailImportFacade["deinitLogger"]>) {
return this.transport.invokeNative("ipc", ["NativeMailImportFacade", "deinitLogger", ...args])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,4 @@ export interface UnencryptedCredentials {
readonly databaseKey: Uint8Array | null
readonly encryptedPassword: string
readonly encryptedPassphraseKey: Uint8Array | null
readonly apiUrl: string
}
50 changes: 27 additions & 23 deletions src/mail-app/mail/import/MailImporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ export class MailImporter {
private progress: number = DEFAULT_PROGRESS

private finalisedImportStates: Map<Id, ImportMailState> = new Map()
private activeImportState: IdTuple | null = null
private activeImportId: IdTuple | null = null
private activeImportStartTimestamp: number | null = null
private uiStatus: UiImportStatus

private eventController: EventController
Expand Down Expand Up @@ -76,18 +77,19 @@ export class MailImporter {
async initImportMailStates(): Promise<void> {
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) {
Expand All @@ -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)
}
}
Expand All @@ -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() {
Expand All @@ -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()
Expand All @@ -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()
Expand All @@ -191,7 +196,7 @@ export class MailImporter {
)
}

shouldShowPauseButton(): boolean {
shouldRenderPauseButton(): boolean {
return this.uiStatus === UiImportStatus.Running || this.uiStatus === UiImportStatus.Starting || this.uiStatus === UiImportStatus.Pausing
}

Expand All @@ -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 ||
Expand All @@ -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 ||
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -301,7 +305,7 @@ export class MailImporter {
}

private resetStatus() {
this.activeImportState = null
this.activeImportId = null
this.progressMonitor = null
this.progress = 0
this.stopProgressEstimation()
Expand Down
Loading

0 comments on commit a6cd7a2

Please sign in to comment.