Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Improve mail display #730

Merged
merged 45 commits into from
May 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
888db3b
feat: Clean style.css
valentinperignon Apr 19, 2023
35a36a1
feat: Use CSS variables
valentinperignon Apr 19, 2023
383d36a
fix: Update WebView height
valentinperignon Apr 19, 2023
2ace583
feat: Clean head and body from html document
valentinperignon Apr 27, 2023
75d527f
refactor: Clean WebViewModel
valentinperignon Apr 27, 2023
bcb7182
feat: Add js file
valentinperignon May 2, 2023
9d70212
feat: Update documentation
valentinperignon May 2, 2023
2ac5488
feat: Improve script
valentinperignon May 3, 2023
38b1be0
fix: Fix <pre> elements (wrap)
valentinperignon May 3, 2023
16abaa3
feat: Add several logs in script
valentinperignon May 3, 2023
ef91a7c
fix: Add new elements to whitelist
valentinperignon May 3, 2023
1819bca
feat: Improve mail resizing
valentinperignon May 4, 2023
83287eb
feat: Remove some style properties from css
valentinperignon May 4, 2023
162cdbe
feat: Update javascript files
valentinperignon May 8, 2023
97937f7
feat: Update javascripts
valentinperignon May 9, 2023
a8cfc24
feat: Execute scripts
valentinperignon May 9, 2023
eae25a0
feat: Update attributes and protocols
valentinperignon May 9, 2023
84293e9
feat: Keep body attributes
valentinperignon May 9, 2023
47cb048
feat: Add javaScriptBridge script
valentinperignon May 9, 2023
b4a4199
refactor: Set wrapper id as a Constant
valentinperignon May 9, 2023
6ac240a
refactor: Move WebView to a separate file
valentinperignon May 10, 2023
6e3d42d
feat: Get messageUid for Sentry
valentinperignon May 10, 2023
bdee88d
feat: WebViewModel conforms to WKScriptMessageHandler
valentinperignon May 10, 2023
f61dbf6
refactor: Move captureLog to a separate file and improve scripts loading
valentinperignon May 10, 2023
0281346
feat: Send errors to Sentry
valentinperignon May 10, 2023
99faff1
fix: Set body width to auto
valentinperignon May 10, 2023
4440333
feat: Update sentry functions
valentinperignon May 10, 2023
8cf79b5
fix: Separate CSS
valentinperignon May 10, 2023
2a6f057
fix: Load CSS
valentinperignon May 11, 2023
1bb02c8
refactor: Load files from Bundle
valentinperignon May 11, 2023
a057567
refactor: Clean code with Constants
valentinperignon May 11, 2023
3231fee
refactor: Reduce Cognitive Complexity of removeCSSProperty
valentinperignon May 11, 2023
f84267e
feat: Remove height 100%
valentinperignon May 15, 2023
81a363c
feat: Get correct webview height
valentinperignon May 15, 2023
03bb8a0
refactor: Transform WebViewModel into an ObservableObject
valentinperignon May 15, 2023
1058d2b
feat: Wait slightly longer to get the correct size
valentinperignon May 16, 2023
a0509d6
feat: Compute message size
valentinperignon May 16, 2023
f7c8f7f
feat: Compute width with overflow
valentinperignon May 16, 2023
c728cbc
refactor: Move to a separate file
valentinperignon May 16, 2023
73f83bd
feat: Change both sizes
valentinperignon May 16, 2023
fc192e8
feat: Observe changes
valentinperignon May 16, 2023
e5d2be8
refactor: Clean code
valentinperignon May 16, 2023
c8673e2
feat: Better method to compute size
valentinperignon May 17, 2023
e1ada26
refactor: Clean code
valentinperignon May 17, 2023
b3820e6
feat: Improve code
valentinperignon May 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/Infomaniak/ios-notifications",
"state" : {
"revision" : "0da8aa11e60766acaa62e34c84a8049eeb6e933f",
"version" : "2.0.1"
"revision" : "4609e33800a11f9f7acd38df66d1fbaaba0e60a8",
"version" : "2.1.0"
}
},
{
Expand Down
23 changes: 4 additions & 19 deletions Mail/Helpers/RichTextEditor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -148,25 +148,10 @@ class MailEditorView: SQTextEditorView {
config.userContentController.add(self, name: jsMessageName.rawValue)
}

// inject css to html
if customCss == nil,
let cssURL = Bundle(for: SQTextEditorView.self).url(forResource: "editor", withExtension: "css"),
let css = try? String(contentsOf: cssURL, encoding: .utf8) {
customCss = css
}

if let css = customCss {
let cssStyle = """
javascript:(function() {
var parent = document.getElementsByTagName('head').item(0);
var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = window.atob('\(encodeStringTo64(fromString: css))');
parent.appendChild(style)})()
"""
let cssScript = WKUserScript(source: cssStyle, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
config.userContentController.addUserScript(cssScript)
}
let css = customCss ?? MessageWebViewUtils.generateCSS(for: .editor)
let cssStyle = "(() => { document.head.innerHTML += `\(css)`; })()"
let cssScript = WKUserScript(source: cssStyle, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
config.userContentController.addUserScript(cssScript)

let _webView = WKWebView(frame: .zero, configuration: config)
_webView.translatesAutoresizingMaskIntoConstraints = false
Expand Down
52 changes: 22 additions & 30 deletions Mail/Views/Thread/MessageBodyView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,11 @@ import RealmSwift
import SwiftUI

struct MessageBodyView: View {
@Binding var presentableBody: PresentableBody
@StateObject private var model = WebViewModel()

@State var model = WebViewModel()
@State private var webViewShortHeight: CGFloat = .zero
@State private var webViewCompleteHeight: CGFloat = .zero
@Binding var presentableBody: PresentableBody

@State private var showBlockQuote = false
@State private var contentLoading = true
let messageUid: String

var body: some View {
ZStack {
Expand All @@ -40,33 +37,27 @@ struct MessageBodyView: View {
.padding(.horizontal, 16)
.onAppear {
withAnimation {
contentLoading = false
model.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()
}
WebView(model: model, messageUid: messageUid)
.frame(height: model.webViewHeight)
.onAppear {
loadBody()
}
.onChange(of: presentableBody) { _ in
loadBody()
}
.onChange(of: model.showBlockQuote) { _ in
loadBody()
}

if presentableBody.quote != nil {
MailButton(label: showBlockQuote
MailButton(label: model.showBlockQuote
? MailResourcesStrings.Localizable.messageHideQuotedText
: MailResourcesStrings.Localizable.messageShowQuotedText) {
showBlockQuote.toggle()
model.showBlockQuote.toggle()
}
.mailButtonStyle(.smallLink)
.frame(maxWidth: .infinity, alignment: .leading)
Expand All @@ -75,20 +66,21 @@ struct MessageBodyView: View {
}
}
}
.opacity(contentLoading ? 0 : 1)
if contentLoading {
.opacity(model.contentLoading ? 0 : 1)

if model.contentLoading {
ShimmerView()
}
}
}

private func loadBody() {
model.loadHTMLString(value: showBlockQuote ? presentableBody.body?.value : presentableBody.compactBody)
model.loadHTMLString(value: model.showBlockQuote ? presentableBody.body?.value : presentableBody.compactBody)
}
}

struct MessageBodyView_Previews: PreviewProvider {
static var previews: some View {
MessageBodyView(presentableBody: .constant(PreviewHelper.samplePresentableBody))
MessageBodyView(presentableBody: .constant(PreviewHelper.samplePresentableBody), messageUid: "message_uid")
}
}
4 changes: 2 additions & 2 deletions Mail/Views/Thread/MessageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ struct MessageView: View {
.padding(.horizontal, 16)

if isMessageExpanded {
if !message.attachments.filter { $0.disposition == .attachment || $0.contentId == nil }.isEmpty {
if !message.attachments.filter({ $0.disposition == .attachment || $0.contentId == nil }).isEmpty {
AttachmentsView(message: message)
.padding(.top, 24)
}
MessageBodyView(presentableBody: $presentableBody)
MessageBodyView(presentableBody: $presentableBody, messageUid: message.uid)
.padding(.top, 16)
}
}
Expand Down
90 changes: 90 additions & 0 deletions Mail/Views/Thread/WebView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
Infomaniak Mail - iOS App
Copyright (C) 2022 Infomaniak Network SA

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import SwiftUI
import WebKit

struct WebView: UIViewRepresentable {
@ObservedObject var model: WebViewModel

let messageUid: String

private var webView: WKWebView {
return model.webView
}

class Coordinator: NSObject, WKNavigationDelegate {
var parent: WebView

init(_ parent: WebView) {
self.parent = parent
}

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
Task { @MainActor in
try await webView.evaluateJavaScript("listenToSizeChanges()")

// Fix CSS properties and adapt the mail to the screen size
let readyState = try await webView.evaluateJavaScript("document.readyState") as? String
guard readyState == "complete" else { return }

_ = try await webView.evaluateJavaScript("removeAllProperties()")
_ = try await webView.evaluateJavaScript("normalizeMessageWidth(\(webView.frame.width), '\(parent.messageUid)')")
}
}

func webView(
_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
) {
if navigationAction.navigationType == .linkActivated {
if let url = navigationAction.request.url {
decisionHandler(.cancel)
UIApplication.shared.open(url)
}
} else {
decisionHandler(.allow)
}
}
}

func makeCoordinator() -> Coordinator {
Coordinator(self)
}

func makeUIView(context: Context) -> WKWebView {
webView.navigationDelegate = context.coordinator
webView.scrollView.bounces = false
webView.scrollView.bouncesZoom = false
webView.scrollView.showsVerticalScrollIndicator = false
webView.scrollView.showsHorizontalScrollIndicator = true
webView.scrollView.alwaysBounceVertical = false
webView.scrollView.alwaysBounceHorizontal = false
#if DEBUG
if #available(iOS 16.4, *) {
webView.isInspectable = true
}
#endif
return webView
}

func updateUIView(_ uiView: WKWebView, context: Context) {
// needed for UIViewRepresentable
}
}
Loading