mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 16:24:11 +00:00
134 lines
5.4 KiB
Swift
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")
|
|
}
|
|
}
|
|
}
|