Skip to content

Commit

Permalink
Merge pull request #814 from Infomaniak/composemessageview-v2
Browse files Browse the repository at this point in the history
feat: Update ComposeMessageView
  • Loading branch information
PhilippeWeidmann committed Jun 20, 2023
2 parents 83bf55c + 51b6156 commit d04f7a2
Show file tree
Hide file tree
Showing 28 changed files with 888 additions and 557 deletions.
38 changes: 31 additions & 7 deletions Mail/Components/RecipientCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,54 @@
import MailCore
import SwiftUI

struct RecipientCellModifier: ViewModifier {
func body(content: Content) -> some View {
content
.lineLimit(1)
.frame(maxWidth: .infinity, alignment: .leading)
.accessibilityElement(children: .combine)
.accessibilityAddTraits(.isButton)
}
}

extension View {
func recipientCellModifier() -> some View {
modifier(RecipientCellModifier())
}
}

struct RecipientCell: View {
let recipient: Recipient
var highlight: String?

var body: some View {
HStack(spacing: 8) {
AvatarView(avatarDisplayable: recipient, size: 40)
.accessibilityHidden(true)

if recipient.name.isEmpty {
Text(recipient.email)
Text(highlightedAttributedString(from: recipient.email))
.textStyle(.bodyMedium)
} else {
VStack(alignment: .leading) {
Text(recipient.name)
Text(highlightedAttributedString(from: recipient.name))
.textStyle(.bodyMedium)
Text(recipient.email)
Text(highlightedAttributedString(from: recipient.email))
.textStyle(.bodySecondary)
}
}
}
.lineLimit(1)
.frame(maxWidth: .infinity, alignment: .leading)
.accessibilityElement(children: .combine)
.accessibilityAddTraits(.isButton)
.recipientCellModifier()
}

private func highlightedAttributedString(from data: String) -> AttributedString {
var attributedString = AttributedString(data)
guard let highlight else { return attributedString }

if let range = attributedString.range(of: highlight, options: .caseInsensitive) {
attributedString[range].foregroundColor = .accentColor
}
return attributedString
}
}

Expand Down
47 changes: 47 additions & 0 deletions Mail/Components/UnknownRecipientView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
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 MailCore
import MailResources
import SwiftUI

struct UnknownRecipientView: View {
let size: CGFloat

private var iconSize: CGFloat {
return size - 2 * UIConstants.unknownRecipientHorizontalPadding
}

var body: some View {
Circle()
.fill(Color.accentColor)
.frame(width: size, height: size)
.overlay {
MailResourcesAsset.userBold.swiftUIImage
.resizable()
.foregroundColor(MailResourcesAsset.backgroundColor.swiftUIColor)
.frame(width: iconSize, height: iconSize)
}
}
}

struct UnknownRecipientView_Previews: PreviewProvider {
static var previews: some View {
UnknownRecipientView(size: 40)
}
}
12 changes: 6 additions & 6 deletions Mail/Helpers/RichTextEditor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import WebKit
struct RichTextEditor: UIViewRepresentable {
typealias UIViewType = MailEditorView

@Binding var model: RichTextEditorModel
@ObservedObject var model: RichTextEditorModel
@Binding var body: String
@Binding var isShowingCamera: Bool
@Binding var isShowingFileSelection: Bool
Expand All @@ -37,12 +37,12 @@ struct RichTextEditor: UIViewRepresentable {
let blockRemoteContent: Bool
var alert: ObservedObject<NewMessageAlert>.Wrapper

init(model: Binding<RichTextEditorModel>, body: Binding<String>,
init(model: RichTextEditorModel, body: Binding<String>,
alert: ObservedObject<NewMessageAlert>.Wrapper,
isShowingCamera: Binding<Bool>, isShowingFileSelection: Binding<Bool>, isShowingPhotoLibrary: Binding<Bool>,
becomeFirstResponder: Binding<Bool>,
blockRemoteContent: Bool) {
_model = model
_model = ObservedObject(wrappedValue: model)
_body = body
self.alert = alert
_isShowingCamera = isShowingCamera
Expand Down Expand Up @@ -146,9 +146,9 @@ extension SQTextEditorView {
}
}

struct RichTextEditorModel {
var cursorPosition: CGFloat = 0
var height: CGFloat = 0
class RichTextEditorModel: ObservableObject {
@Published var cursorPosition: CGFloat = 0
@Published var height: CGFloat = 0
}

class MailEditorView: SQTextEditorView {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ struct AttachmentsHeaderView: View {
}
}
.padding(.vertical, 1)
.padding(.horizontal, 16)
}
.padding(.horizontal, 16)
}
}
.customAlert(item: $attachmentsManager.globalError) { error in
Expand Down
63 changes: 63 additions & 0 deletions Mail/Views/New Message/AutocompletionCell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
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 MailCore
import MailResources
import SwiftUI

struct AutocompletionCell: View {
let addRecipient: @MainActor (Recipient) -> Void
let recipient: Recipient
var highlight: String?
let alreadyAppend: Bool
let unknownRecipient: Bool

var body: some View {
HStack(spacing: 12) {
Button {
addRecipient(recipient)
} label: {
if unknownRecipient {
UnknownRecipientCell(recipient: recipient)
} else {
RecipientCell(recipient: recipient, highlight: highlight)
}
}
.allowsHitTesting(!alreadyAppend || unknownRecipient)
.opacity(alreadyAppend && !unknownRecipient ? 0.5 : 1)

if alreadyAppend && !unknownRecipient {
MailResourcesAsset.checked.swiftUIImage
.resizable()
.frame(width: 24, height: 24)
.foregroundColor(MailResourcesAsset.textTertiaryColor.swiftUIColor)
}
}
}
}

struct AutocompletionCell_Previews: PreviewProvider {
static var previews: some View {
AutocompletionCell(
addRecipient: { _ in /* Preview */ },
recipient: PreviewHelper.sampleRecipient1,
alreadyAppend: false,
unknownRecipient: false
)
}
}
82 changes: 58 additions & 24 deletions Mail/Views/New Message/AutocompletionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,50 +16,84 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import Combine
import MailCore
import MailResources
import RealmSwift
import SwiftUI

struct AutocompletionView: View {
@State private var shouldAddUserProposal = false

@ObservedObject var textDebounce: TextDebounce

@Binding var autocompletion: [Recipient]
@Binding var unknownRecipientAutocompletion: String
@Binding var addedRecipients: RealmSwift.List<Recipient>

let onSelect: (Recipient) -> Void
let addRecipient: @MainActor (Recipient) -> Void

var body: some View {
LazyVStack {
LazyVStack(spacing: UIConstants.autocompletionVerticalPadding) {
ForEach(autocompletion) { recipient in
VStack(alignment: .leading, spacing: 8) {
Button {
onSelect(recipient)
} label: {
RecipientCell(recipient: recipient)
}
.padding(.horizontal, 8)
let isLastRecipient = autocompletion.last?.isSameRecipient(as: recipient) == true
let isUserProposal = shouldAddUserProposal && isLastRecipient

IKDivider()
VStack(alignment: .leading, spacing: UIConstants.autocompletionVerticalPadding) {
AutocompletionCell(
addRecipient: addRecipient,
recipient: recipient,
highlight: textDebounce.text,
alreadyAppend: addedRecipients.contains { $0.isSameRecipient(as: recipient) },
unknownRecipient: isUserProposal
)

if !isLastRecipient {
IKDivider()
}
}
}
}
.onAppear {
updateAutocompletion(textDebounce.text)
}
.onReceive(textDebounce.$text.debounce(for: .milliseconds(150), scheduler: DispatchQueue.main)) { currentValue in
updateAutocompletion("\(currentValue)")
}
}

if !unknownRecipientAutocompletion.isEmpty {
Button {
onSelect(Recipient(email: unknownRecipientAutocompletion, name: ""))
} label: {
AddRecipientCell(recipientEmail: unknownRecipientAutocompletion)
}
.padding(.horizontal, 8)
private func updateAutocompletion(_ search: String) {
guard let contactManager = AccountManager.instance.currentContactManager else {
withAnimation {
autocompletion = []
}
return
}

let trimmedSearch = search.trimmingCharacters(in: .whitespacesAndNewlines)

let autocompleteContacts = contactManager.contacts(matching: trimmedSearch)
var autocompleteRecipients = autocompleteContacts.map { Recipient(email: $0.email, name: $0.name) }

let realResults = autocompleteRecipients.filter { !addedRecipients.map(\.email).contains($0.email) }

withAnimation {
shouldAddUserProposal = !(realResults.count == 1 && realResults.first?.email == textDebounce.text)
if shouldAddUserProposal {
autocompleteRecipients
.append(Recipient(email: textDebounce.text, name: ""))
}

autocompletion = autocompleteRecipients
}
.padding(.top, 8)
.padding(.horizontal, 8)
}
}

struct AutocompletionView_Previews: PreviewProvider {
static var previews: some View {
AutocompletionView(autocompletion: .constant([
PreviewHelper.sampleRecipient1, PreviewHelper.sampleRecipient2, PreviewHelper.sampleRecipient3
]),
unknownRecipientAutocompletion: .constant("")) { _ in /* Preview */ }
AutocompletionView(
textDebounce: TextDebounce(),
autocompletion: .constant([]),
addedRecipients: .constant([PreviewHelper.sampleRecipient1].toRealmList())
) { _ in /* Preview */ }
}
}
Loading

0 comments on commit d04f7a2

Please sign in to comment.