diff --git a/Mail/Views/Thread/MessageBodyView.swift b/Mail/Views/Thread/MessageBodyView.swift index 9099fa36b..0876a0fed 100644 --- a/Mail/Views/Thread/MessageBodyView.swift +++ b/Mail/Views/Thread/MessageBodyView.swift @@ -29,47 +29,54 @@ 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" { - SelectableTextView(text: body.value) - .padding(.horizontal, 16) - } else { - GeometryReader { proxy in + ZStack { + VStack { + if let body = presentableBody.body { + if body.type == "text/plain" { + SelectableTextView(text: body.value) + .padding(.horizontal, 16) + .onAppear { + withAnimation { + contentLoading = false + } + } + } else { WebView( model: $model, shortHeight: $webViewShortHeight, completeHeight: $webViewCompleteHeight, - withQuote: $showBlockQuote, - proxy: proxy + loading: $contentLoading, + withQuote: $showBlockQuote ) - } - .frame(height: showBlockQuote ? webViewCompleteHeight : webViewShortHeight) - .onAppear { - loadBody() - } - .onChange(of: presentableBody) { _ in - loadBody() - } - .onChange(of: showBlockQuote) { _ in - loadBody() - } + .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) + 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 e01e5bff2..0d981ce48 100644 --- a/Mail/Views/Thread/WebViewModel.swift +++ b/Mail/Views/Thread/WebViewModel.swift @@ -28,8 +28,8 @@ struct WebView: UIViewRepresentable { @Binding var model: WebViewModel @Binding var shortHeight: CGFloat @Binding var completeHeight: CGFloat + @Binding var loading: Bool @Binding var withQuote: Bool - var proxy: GeometryProxy var webView: WKWebView { return model.webView @@ -40,15 +40,15 @@ struct WebView: UIViewRepresentable { init(_ parent: WebView) { self.parent = parent - parent.model.proxy = parent.proxy } 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 { @@ -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) } } @@ -107,17 +103,14 @@ 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 { - return "" + return "" } init() { @@ -144,6 +137,17 @@ class WebViewModel: ObservableObject { 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) 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)