Skip to content

Commit

Permalink
Defer rendering mail body until viewSlider animation is over, fix #3099
Browse files Browse the repository at this point in the history
This avoids big frame losses by separating re-layouts. ViewSlider
animations trigger layout and showing/scaling mail body does too.
  • Loading branch information
charlag authored and mpfau committed May 26, 2021
1 parent 5d49c95 commit c03d671
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 8 deletions.
4 changes: 2 additions & 2 deletions src/gui/base/ViewSlider.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,8 @@ export class ViewSlider implements IViewSlider {
}


focus(viewColumn: ViewColumn) {
this._busy.then(() => {
focus(viewColumn: ViewColumn): Promise<void> {
return this._busy.then(() => {
// hide the foreground column if the column is in foreground
if (this.focusedColumn.isInForeground) {
this._busy = this._slideForegroundColumn(this.focusedColumn, false)
Expand Down
14 changes: 11 additions & 3 deletions src/mail/view/MailView.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {createDeleteMailFolderData} from "../../api/entities/tutanota/DeleteMail
import {createDeleteMailData} from "../../api/entities/tutanota/DeleteMailData"
import type {Mail} from "../../api/entities/tutanota/Mail"
import {MailTypeRef} from "../../api/entities/tutanota/Mail"
import {lazyMemoized, neverNull, noOp} from "../../api/common/utils/Utils"
import {defer, lazyMemoized, neverNull, noOp} from "../../api/common/utils/Utils"
import {MailListView} from "./MailListView"
import {assertMainOrNode, isApp} from "../../api/common/Env"
import type {Shortcut} from "../../misc/KeyManager"
Expand Down Expand Up @@ -689,9 +689,11 @@ export class MailView implements CurrentView {

elementSelected: (mails: Mail[], elementClicked: boolean, selectionChanged: boolean, multiSelectOperation: boolean) => void =
(mails, elementClicked, selectionChanged, multiSelectOperation) => {
const animationOverDeferred = defer()

if (mails.length === 1 && !multiSelectOperation && (selectionChanged || !this.mailViewer)) {
// set or update the visible mail
this.mailViewer = new MailViewer(mails[0], false, locator.entityClient, locator.mailModel, locator.contactModel)
this.mailViewer = new MailViewer(mails[0], false, locator.entityClient, locator.mailModel, locator.contactModel, animationOverDeferred.promise)
let url = `/mail/${mails[0]._id.join("/")}`
if (this.selectedFolder) {
this._folderToUrl[this.selectedFolder._id[1]] = url
Expand Down Expand Up @@ -722,8 +724,13 @@ export class MailView implements CurrentView {
if (elementClicked) {
this.mailList.list._loading.then(() => {
this.viewSlider.focus(this.mailColumn)
.then(() => animationOverDeferred.resolve())
})
} else {
animationOverDeferred.resolve()
}
} else {
animationOverDeferred.resolve()
}
}

Expand Down Expand Up @@ -758,7 +765,8 @@ export class MailView implements CurrentView {
if (operation === OperationType.UPDATE && this.mailViewer
&& isSameId(this.mailViewer.mail._id, [neverNull(instanceListId), instanceId])) {
return load(MailTypeRef, this.mailViewer.mail._id).then(updatedMail => {
this.mailViewer = new MailViewer(updatedMail, false, locator.entityClient, locator.mailModel, locator.contactModel)
this.mailViewer = new MailViewer(updatedMail, false, locator.entityClient, locator.mailModel, locator.contactModel,
Promise.resolve())
}).catch(() => {
// ignore. might happen if a mail was just sent
})
Expand Down
18 changes: 16 additions & 2 deletions src/mail/view/MailViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ import {ActionBanner} from "../../gui/base/icons/ActionBanner"
import type {Link} from "../../misc/HtmlSanitizer"
import {stringifyFragment} from "../../gui/HtmlUtils"
import {IndexingNotSupportedError} from "../../api/common/error/IndexingNotSupportedError"
import {delay} from "../../api/common/utils/PromiseUtils"

assertMainOrNode()

Expand Down Expand Up @@ -177,8 +178,11 @@ export class MailViewer {
_entityClient: EntityClient;
_mailModel: MailModel;
_contactModel: ContactModel;
_delayBodyRenderingUntil: Promise<*>

constructor(mail: Mail, showFolder: boolean, entityClient: EntityClient, mailModel: MailModel, contactModel: ContactModel) {
constructor(mail: Mail, showFolder: boolean, entityClient: EntityClient, mailModel: MailModel, contactModel: ContactModel,
delayBodyRenderingUntil: Promise<*>) {
this._delayBodyRenderingUntil = delayBodyRenderingUntil
if (isDesktop()) {
import("../../native/common/NativeWrapper").then(({nativeApp}) =>
nativeApp.invokeNative(new Request('sendSocketMessage', [{mailAddress: mail.sender.address}])))
Expand Down Expand Up @@ -253,6 +257,13 @@ export class MailViewer {
.catch(NotFoundError, e => console.log("could load conversation entry as it has been moved/deleted already", e))
})

let delayIsOver = false
delayBodyRenderingUntil
.then(() => {
delayIsOver = true
m.redraw()
})

this.view = () => {
const dateTime = formatDateWithWeekday(this.mail.receivedDate) + " • " + formatTime(this.mail.receivedDate)
return [
Expand Down Expand Up @@ -354,7 +365,7 @@ export class MailViewer {
}
},
},
this.renderMailBodySection()
delayIsOver ? this.renderMailBodySection() : null
)
],
),
Expand Down Expand Up @@ -876,6 +887,9 @@ export class MailViewer {
const isAllowedAndAuthenticatedExternalSender = isAllowListedExternalSender
&& mail.authStatus === MailAuthenticationStatus.AUTHENTICATED

// We should not try to sanitize body while we still animate because it's a heavy operation.
await this._delayBodyRenderingUntil

const sanitizeResult = await this.setSanitizedMailBodyFromMail(mail, !isAllowedAndAuthenticatedExternalSender)

this._checkMailForPhishing(mail, sanitizeResult.links)
Expand Down
2 changes: 1 addition & 1 deletion src/search/view/SearchResultDetailsViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export class SearchResultDetailsViewer {
showEntity(entity: Object, entitySelected: boolean): void {
if (isSameTypeRef(MailTypeRef, entity._type)) {
let mail = ((entity: any): Mail)
this._viewer = new MailViewer(mail, true, locator.entityClient, locator.mailModel, locator.contactModel)
this._viewer = new MailViewer(mail, true, locator.entityClient, locator.mailModel, locator.contactModel, Promise.resolve())
this._viewerEntityId = mail._id
if (entitySelected && mail.unread && !mail._errors) {
mail.unread = false
Expand Down

0 comments on commit c03d671

Please sign in to comment.