Files
element-x-ios/ElementX/Sources/Other/Pills/MessageText.swift
T
Mauro Romito f53e76b8a8 fix
2023-09-18 13:55:07 +02:00

134 lines
5.4 KiB
Swift

//
// Copyright 2023 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import SwiftUI
import UIKit
final class MessageTextView: UITextView {
// This prevents the magnifying glass from showing up
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
gestureRecognizer as? UILongPressGestureRecognizer == nil
}
}
struct MessageText: UIViewRepresentable {
@Environment(\.openURL) private var openURLAction: OpenURLAction
let attributedString: AttributedString
func makeUIView(context: Context) -> MessageTextView {
let textView = MessageTextView()
textView.isEditable = false
textView.isScrollEnabled = false
textView.adjustsFontForContentSizeCategory = true
// Required to allow tapping links
// We disable selection at delegate level
textView.isSelectable = true
textView.isUserInteractionEnabled = true
// Otherwise links can be dragged and dropped when long pressed
textView.textDragInteraction?.isEnabled = false
textView.contentInset = .zero
textView.contentInsetAdjustmentBehavior = .never
textView.textContainerInset = .zero
textView.textContainer.lineFragmentPadding = 0
textView.textLayoutManager?.usesFontLeading = false
textView.backgroundColor = .clear
textView.attributedText = NSAttributedString(attributedString)
textView.delegate = context.coordinator
return textView
}
func updateUIView(_ uiView: MessageTextView, context: Context) {
uiView.attributedText = NSAttributedString(attributedString)
context.coordinator.openURLAction = openURLAction
}
func sizeThatFits(_ proposal: ProposedViewSize, uiView: MessageTextView, context: Context) -> CGSize? {
uiView.sizeThatFits(CGSize(width: proposal.width ?? UIView.layoutFittingExpandedSize.width, height: UIView.layoutFittingCompressedSize.height))
}
func makeCoordinator() -> Coordinator {
Coordinator(openURLAction: openURLAction)
}
final class Coordinator: NSObject, UITextViewDelegate {
var openURLAction: OpenURLAction
init(openURLAction: OpenURLAction) {
self.openURLAction = openURLAction
}
func textViewDidChangeSelection(_ textView: UITextView) {
textView.selectedTextRange = nil
}
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
if interaction == .invokeDefaultAction {
openURLAction.callAsFunction(URL)
}
return false
}
}
}
// MARK: - Previews
struct MessageText_Previews: PreviewProvider {
private static let defaultFontContainer: AttributeContainer = {
var container = AttributeContainer()
container.font = UIFont.preferredFont(forTextStyle: .body)
return container
}()
private static let attributedString = AttributedString("Hello World! Hello world! Hello world! Hello world! Hello World! Hellooooooooooooooooooooooo Woooooooooooooooooooooorld", attributes: defaultFontContainer)
private static let attributedStringWithAttachment = "Hello " + AttributedString(NSAttributedString(attachment: PillTextAttachment(data: Data(), ofType: InfoPlistReader.main.pillsUTType))) + " World!"
private static let htmlStringWithQuote =
"""
<blockquote>A blockquote that is long and goes onto multiple lines as the first item in the message</blockquote>
<p>Then another line of text here to reply to the blockquote, which is also a multiline component.</p>
"""
private static let htmlStringWithList = "<p>This is a list</p>\n<ul>\n<li>One</li>\n<li>Two</li>\n<li>And number 3</li>\n</ul>\n"
private static let attributedStringBuilder = AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL)
static var previews: some View {
MessageText(attributedString: attributedString)
.border(Color.purple)
.previewDisplayName("Custom Text")
// For comparison
Text(attributedString)
.border(Color.purple)
.previewDisplayName("SwiftUI Default Text")
MessageText(attributedString: attributedStringWithAttachment)
.border(Color.purple)
.previewDisplayName("Custom Attachment")
if let attributedString = attributedStringBuilder.fromHTML(htmlStringWithQuote) {
MessageText(attributedString: attributedString)
.border(Color.purple)
.previewDisplayName("With block quote")
}
if let attributedString = attributedStringBuilder.fromHTML(htmlStringWithList) {
MessageText(attributedString: attributedString)
.border(Color.purple)
.previewDisplayName("With list")
}
}
}