From f0b37ebf999b259f7d6800b9396bb76995d110b8 Mon Sep 17 00:00:00 2001 From: Philippe Weidmann Date: Tue, 18 Apr 2023 13:27:15 +0200 Subject: [PATCH 1/5] fix(WebViewModel): Remove Geometry reader --- Mail/Views/Thread/MessageBodyView.swift | 17 +++++++---------- Mail/Views/Thread/WebViewModel.swift | 7 ++----- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/Mail/Views/Thread/MessageBodyView.swift b/Mail/Views/Thread/MessageBodyView.swift index 6b8bb3a77..68324f57e 100644 --- a/Mail/Views/Thread/MessageBodyView.swift +++ b/Mail/Views/Thread/MessageBodyView.swift @@ -39,16 +39,13 @@ struct MessageBodyView: View { .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal, 16) } else { - GeometryReader { proxy in - WebView( - model: $model, - shortHeight: $webViewShortHeight, - completeHeight: $webViewCompleteHeight, - withQuote: $showBlockQuote, - proxy: proxy - ) - } - .frame(height: showBlockQuote ? webViewCompleteHeight : webViewShortHeight) + WebView( + model: $model, + shortHeight: $webViewShortHeight, + completeHeight: $webViewCompleteHeight, + withQuote: $showBlockQuote + ) + .frame(minHeight: showBlockQuote ? webViewCompleteHeight : webViewShortHeight) .onAppear { loadBody() } diff --git a/Mail/Views/Thread/WebViewModel.swift b/Mail/Views/Thread/WebViewModel.swift index e01e5bff2..a40a8fe7b 100644 --- a/Mail/Views/Thread/WebViewModel.swift +++ b/Mail/Views/Thread/WebViewModel.swift @@ -29,7 +29,6 @@ struct WebView: UIViewRepresentable { @Binding var shortHeight: CGFloat @Binding var completeHeight: CGFloat @Binding var withQuote: Bool - var proxy: GeometryProxy var webView: WKWebView { return model.webView @@ -40,7 +39,6 @@ struct WebView: UIViewRepresentable { init(_ parent: WebView) { self.parent = parent - parent.model.proxy = parent.proxy } private func updateHeight(height: CGFloat) { @@ -107,13 +105,12 @@ struct WebView: UIViewRepresentable { } } -class WebViewModel: ObservableObject { +class WebViewModel { let webView: WKWebView - var proxy: GeometryProxy? let css: String? = try? String(contentsOfFile: Bundle.main.path(forResource: "style", ofType: "css") ?? "", encoding: .utf8) .replacingOccurrences(of: "\n", with: "") var viewport: String { - return "" + return "" } var style: String { From 1c5ba52f6cec311a5333cb04dad54820504a6033 Mon Sep 17 00:00:00 2001 From: Philippe Weidmann Date: Tue, 18 Apr 2023 13:53:27 +0200 Subject: [PATCH 2/5] feat: Improved animation for loading content --- Mail/Views/Thread/MessageBodyView.swift | 78 ++++++++++++++----------- Mail/Views/Thread/WebViewModel.swift | 6 +- 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/Mail/Views/Thread/MessageBodyView.swift b/Mail/Views/Thread/MessageBodyView.swift index 68324f57e..997a91c1b 100644 --- a/Mail/Views/Thread/MessageBodyView.swift +++ b/Mail/Views/Thread/MessageBodyView.swift @@ -29,46 +29,56 @@ struct MessageBodyView: View { @State private var webViewCompleteHeight: CGFloat = .zero @State private var showBlockQuote = false + @State private var contentLoading = true var body: some View { - VStack { - if let body = presentableBody.body { - if body.type == "text/plain" { - Text(body.value ?? "") - .textStyle(.body) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.horizontal, 16) - } else { - WebView( - model: $model, - shortHeight: $webViewShortHeight, - completeHeight: $webViewCompleteHeight, - withQuote: $showBlockQuote - ) - .frame(minHeight: showBlockQuote ? webViewCompleteHeight : webViewShortHeight) - .onAppear { - loadBody() - } - .onChange(of: presentableBody) { _ in - loadBody() - } - .onChange(of: showBlockQuote) { _ in - loadBody() - } - - if presentableBody.quote != nil { - MailButton(label: showBlockQuote - ? MailResourcesStrings.Localizable.messageHideQuotedText - : MailResourcesStrings.Localizable.messageShowQuotedText) { - showBlockQuote.toggle() - } - .mailButtonStyle(.smallLink) + ZStack { + VStack { + if let body = presentableBody.body { + if body.type == "text/plain" { + Text(body.value ?? "") + .textStyle(.body) .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal, 16) + .onAppear { + withAnimation { + contentLoading = false + } + } + } else { + WebView( + model: $model, + shortHeight: $webViewShortHeight, + completeHeight: $webViewCompleteHeight, + loading: $contentLoading, + withQuote: $showBlockQuote + ) + .frame(minHeight: showBlockQuote ? webViewCompleteHeight : webViewShortHeight) + .onAppear { + loadBody() + } + .onChange(of: presentableBody) { _ in + loadBody() + } + .onChange(of: showBlockQuote) { _ in + loadBody() + } + + if presentableBody.quote != nil { + MailButton(label: showBlockQuote + ? MailResourcesStrings.Localizable.messageHideQuotedText + : MailResourcesStrings.Localizable.messageShowQuotedText) { + showBlockQuote.toggle() + } + .mailButtonStyle(.smallLink) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 16) + } } } - } else { - // Display a shimmer while the body is loading + } + .opacity(contentLoading ? 0 : 1) + if contentLoading { ShimmerView() } } diff --git a/Mail/Views/Thread/WebViewModel.swift b/Mail/Views/Thread/WebViewModel.swift index a40a8fe7b..801933476 100644 --- a/Mail/Views/Thread/WebViewModel.swift +++ b/Mail/Views/Thread/WebViewModel.swift @@ -28,6 +28,7 @@ struct WebView: UIViewRepresentable { @Binding var model: WebViewModel @Binding var shortHeight: CGFloat @Binding var completeHeight: CGFloat + @Binding var loading: Bool @Binding var withQuote: Bool var webView: WKWebView { @@ -44,9 +45,10 @@ struct WebView: UIViewRepresentable { private func updateHeight(height: CGFloat) { if !parent.withQuote { if parent.shortHeight < height { + parent.shortHeight = height + parent.completeHeight = height withAnimation { - parent.shortHeight = height - parent.completeHeight = height + parent.loading = false } } } else if parent.completeHeight < height { From 38e9ce2908f60b9ffba987df550546d950eb1062 Mon Sep 17 00:00:00 2001 From: Philippe Weidmann Date: Tue, 18 Apr 2023 14:42:18 +0200 Subject: [PATCH 3/5] fix(WebViewModel): Remove useless asyncAfter + Use Task --- Mail/Views/Thread/WebViewModel.swift | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Mail/Views/Thread/WebViewModel.swift b/Mail/Views/Thread/WebViewModel.swift index 801933476..d3e9784bf 100644 --- a/Mail/Views/Thread/WebViewModel.swift +++ b/Mail/Views/Thread/WebViewModel.swift @@ -57,17 +57,13 @@ struct WebView: UIViewRepresentable { } func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - webView.evaluateJavaScript("document.readyState") { complete, _ in - if complete != nil { - webView.evaluateJavaScript("document.documentElement.scrollHeight") { height, _ in - guard let height = height as? CGFloat else { return } - DispatchQueue.main.async { [weak self] in - self?.updateHeight(height: height) - } - } - } - } + Task { @MainActor in + let readyState = try await webView.evaluateJavaScript("document.readyState") as? String + guard readyState == "complete" else { return } + + let scrollHeight = try await webView.evaluateJavaScript("document.documentElement.scrollHeight") as? CGFloat + guard let scrollHeight else { return } + updateHeight(height: scrollHeight) } } From e925c88a9b8b608d614123bbb9d1d217ffdd6e4e Mon Sep 17 00:00:00 2001 From: Philippe Weidmann Date: Tue, 18 Apr 2023 15:04:21 +0200 Subject: [PATCH 4/5] fix: Resize image when > device-width --- Mail/Views/Thread/WebViewModel.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Mail/Views/Thread/WebViewModel.swift b/Mail/Views/Thread/WebViewModel.swift index d3e9784bf..03fb90b97 100644 --- a/Mail/Views/Thread/WebViewModel.swift +++ b/Mail/Views/Thread/WebViewModel.swift @@ -139,6 +139,17 @@ class WebViewModel { head = try parsedHtml.appendElement("head") } + let allImages = try parsedHtml.select("img[width]").array() + let maxWidth = webView.frame.width + for image in allImages { + if let widthString = image.getAttributes()?.get(key: "width"), + let width = Double(widthString), + width > maxWidth { + try image.attr("width", "\(maxWidth)") + try image.attr("height", "auto") + } + } + try head.append(viewport) try head.append(style) From 6ede40e84404a2cbbe46a9304ad253f8fd8ad976 Mon Sep 17 00:00:00 2001 From: Philippe Weidmann Date: Tue, 18 Apr 2023 15:08:46 +0200 Subject: [PATCH 5/5] perf: Load webview css only once --- Mail/Views/Thread/WebViewModel.swift | 4 +--- MailCore/Utils/Constants.swift | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Mail/Views/Thread/WebViewModel.swift b/Mail/Views/Thread/WebViewModel.swift index 03fb90b97..0d981ce48 100644 --- a/Mail/Views/Thread/WebViewModel.swift +++ b/Mail/Views/Thread/WebViewModel.swift @@ -105,14 +105,12 @@ struct WebView: UIViewRepresentable { class WebViewModel { let webView: WKWebView - let css: String? = try? String(contentsOfFile: Bundle.main.path(forResource: "style", ofType: "css") ?? "", encoding: .utf8) - .replacingOccurrences(of: "\n", with: "") var viewport: String { return "" } var style: String { - return "" + return "" } init() { diff --git a/MailCore/Utils/Constants.swift b/MailCore/Utils/Constants.swift index cc7d945d7..2bfb3c84c 100644 --- a/MailCore/Utils/Constants.swift +++ b/MailCore/Utils/Constants.swift @@ -66,6 +66,8 @@ public enum Constants { }() public static let extendedWhitelist = Whitelist.extendedWhitelist + public static let customCss = (try? String(contentsOfFile: Bundle.main.path(forResource: "style", ofType: "css") ?? "", + encoding: .utf8).replacingOccurrences(of: "\n", with: "")) ?? "" public static func forwardQuote(message: Message) -> String { let date = DateFormatter.localizedString(from: message.date, dateStyle: .medium, timeStyle: .short)