Compare commits
No commits in common. "b47922694d7915f1442a26b4061b2c7ee09e6c42" and "2000ddadc2a6684a19fd733017e63e19c6812483" have entirely different histories.
b47922694d
...
2000ddadc2
@ -420,7 +420,7 @@
|
|||||||
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
|
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15;
|
IPHONEOS_DEPLOYMENT_TARGET = 16;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11;
|
MACOSX_DEPLOYMENT_TARGET = 11;
|
||||||
@ -460,7 +460,7 @@
|
|||||||
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
|
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15;
|
IPHONEOS_DEPLOYMENT_TARGET = 16;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11;
|
MACOSX_DEPLOYMENT_TARGET = 11;
|
||||||
|
|||||||
@ -1,7 +1,4 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
#if canImport(UIKit)
|
|
||||||
import UIKit
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct PrivateChatView: View {
|
struct PrivateChatView: View {
|
||||||
let chat: PrivateChatListItem
|
let chat: PrivateChatListItem
|
||||||
@ -15,7 +12,6 @@ struct PrivateChatView: View {
|
|||||||
@State private var draftText: String = ""
|
@State private var draftText: String = ""
|
||||||
@State private var inputTab: ComposerTab = .chat
|
@State private var inputTab: ComposerTab = .chat
|
||||||
@State private var isVideoPreferred: Bool = false
|
@State private var isVideoPreferred: Bool = false
|
||||||
@State private var legacyComposerHeight: CGFloat = 40
|
|
||||||
@FocusState private var isComposerFocused: Bool
|
@FocusState private var isComposerFocused: Bool
|
||||||
@EnvironmentObject private var messageCenter: IncomingMessageCenter
|
@EnvironmentObject private var messageCenter: IncomingMessageCenter
|
||||||
|
|
||||||
@ -246,31 +242,12 @@ struct PrivateChatView: View {
|
|||||||
.frame(width: 36, height: 36)
|
.frame(width: 36, height: 36)
|
||||||
|
|
||||||
ZStack(alignment: .bottomTrailing) {
|
ZStack(alignment: .bottomTrailing) {
|
||||||
Group {
|
|
||||||
if #available(iOS 160.0, *) {
|
|
||||||
TextField(inputTab.placeholder, text: $draftText, axis: .vertical)
|
TextField(inputTab.placeholder, text: $draftText, axis: .vertical)
|
||||||
.lineLimit(1...4)
|
.lineLimit(1...4)
|
||||||
.focused($isComposerFocused)
|
.focused($isComposerFocused)
|
||||||
.submitLabel(.send)
|
.submitLabel(.send)
|
||||||
.disabled(currentUserId == nil)
|
.disabled(currentUserId == nil)
|
||||||
.onSubmit { sendCurrentMessage() }
|
.onSubmit { sendCurrentMessage() }
|
||||||
} else {
|
|
||||||
LegacyMultilineTextView(
|
|
||||||
text: $draftText,
|
|
||||||
placeholder: inputTab.placeholder,
|
|
||||||
isFocused: Binding(
|
|
||||||
get: { isComposerFocused },
|
|
||||||
set: { isComposerFocused = $0 }
|
|
||||||
),
|
|
||||||
isEnabled: currentUserId != nil,
|
|
||||||
minHeight: 10,
|
|
||||||
maxLines: 4,
|
|
||||||
calculatedHeight: $legacyComposerHeight,
|
|
||||||
onSubmit: sendCurrentMessage
|
|
||||||
)
|
|
||||||
.frame(height: legacyComposerHeight)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.top, 10)
|
.padding(.top, 10)
|
||||||
.padding(.leading, 12)
|
.padding(.leading, 12)
|
||||||
.padding(.trailing, 44)
|
.padding(.trailing, 44)
|
||||||
@ -465,155 +442,6 @@ struct PrivateChatView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if canImport(UIKit)
|
|
||||||
private struct LegacyMultilineTextView: UIViewRepresentable {
|
|
||||||
@Binding var text: String
|
|
||||||
var placeholder: String
|
|
||||||
@Binding var isFocused: Bool
|
|
||||||
var isEnabled: Bool
|
|
||||||
var minHeight: CGFloat
|
|
||||||
var maxLines: Int
|
|
||||||
@Binding var calculatedHeight: CGFloat
|
|
||||||
var onSubmit: (() -> Void)?
|
|
||||||
|
|
||||||
func makeUIView(context: Context) -> UITextView {
|
|
||||||
let textView = UITextView()
|
|
||||||
textView.delegate = context.coordinator
|
|
||||||
textView.backgroundColor = .clear
|
|
||||||
textView.textContainerInset = .zero
|
|
||||||
textView.textContainer.lineFragmentPadding = 0
|
|
||||||
textView.isScrollEnabled = false
|
|
||||||
textView.font = UIFont.preferredFont(forTextStyle: .body)
|
|
||||||
textView.text = text
|
|
||||||
textView.returnKeyType = .send
|
|
||||||
textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
||||||
|
|
||||||
let placeholderLabel = context.coordinator.placeholderLabel
|
|
||||||
placeholderLabel.text = placeholder
|
|
||||||
placeholderLabel.font = textView.font
|
|
||||||
placeholderLabel.textColor = UIColor.secondaryLabel
|
|
||||||
placeholderLabel.numberOfLines = 1
|
|
||||||
placeholderLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
textView.addSubview(placeholderLabel)
|
|
||||||
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
placeholderLabel.topAnchor.constraint(equalTo: textView.topAnchor),
|
|
||||||
placeholderLabel.leadingAnchor.constraint(equalTo: textView.leadingAnchor),
|
|
||||||
placeholderLabel.trailingAnchor.constraint(lessThanOrEqualTo: textView.trailingAnchor)
|
|
||||||
])
|
|
||||||
|
|
||||||
context.coordinator.updatePlaceholderVisibility(for: textView)
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
Self.recalculateHeight(for: textView, result: calculatedHeightBinding, minHeight: minHeight, maxLines: maxLines)
|
|
||||||
}
|
|
||||||
|
|
||||||
return textView
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateUIView(_ uiView: UITextView, context: Context) {
|
|
||||||
context.coordinator.parent = self
|
|
||||||
|
|
||||||
if uiView.text != text {
|
|
||||||
uiView.text = text
|
|
||||||
}
|
|
||||||
|
|
||||||
uiView.isEditable = isEnabled
|
|
||||||
uiView.isSelectable = isEnabled
|
|
||||||
uiView.textColor = isEnabled ? UIColor.label : UIColor.secondaryLabel
|
|
||||||
|
|
||||||
let placeholderLabel = context.coordinator.placeholderLabel
|
|
||||||
if placeholderLabel.text != placeholder {
|
|
||||||
placeholderLabel.text = placeholder
|
|
||||||
}
|
|
||||||
placeholderLabel.font = uiView.font
|
|
||||||
context.coordinator.updatePlaceholderVisibility(for: uiView)
|
|
||||||
|
|
||||||
if isFocused && !uiView.isFirstResponder {
|
|
||||||
uiView.becomeFirstResponder()
|
|
||||||
} else if !isFocused && uiView.isFirstResponder {
|
|
||||||
uiView.resignFirstResponder()
|
|
||||||
}
|
|
||||||
|
|
||||||
Self.recalculateHeight(for: uiView, result: calculatedHeightBinding, minHeight: minHeight, maxLines: maxLines)
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeCoordinator() -> Coordinator {
|
|
||||||
Coordinator(parent: self)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var calculatedHeightBinding: Binding<CGFloat> {
|
|
||||||
Binding(get: { calculatedHeight }, set: { calculatedHeight = $0 })
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func recalculateHeight(for textView: UITextView, result: Binding<CGFloat>, minHeight: CGFloat, maxLines: Int) {
|
|
||||||
let width = textView.bounds.width
|
|
||||||
guard width > 0 else {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
recalculateHeight(for: textView, result: result, minHeight: minHeight, maxLines: maxLines)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let fittingSize = CGSize(width: width, height: .greatestFiniteMagnitude)
|
|
||||||
let targetSize = textView.sizeThatFits(fittingSize)
|
|
||||||
let lineHeight = textView.font?.lineHeight ?? UIFont.preferredFont(forTextStyle: .body).lineHeight
|
|
||||||
let maxHeight = minHeight + lineHeight * CGFloat(max(maxLines - 1, 0))
|
|
||||||
let clampedHeight = min(max(targetSize.height, minHeight), maxHeight)
|
|
||||||
|
|
||||||
if abs(result.wrappedValue - clampedHeight) > 0.5 {
|
|
||||||
result.wrappedValue = clampedHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
textView.isScrollEnabled = targetSize.height > maxHeight + 0.5
|
|
||||||
}
|
|
||||||
|
|
||||||
final class Coordinator: NSObject, UITextViewDelegate {
|
|
||||||
var parent: LegacyMultilineTextView
|
|
||||||
let placeholderLabel = UILabel()
|
|
||||||
|
|
||||||
init(parent: LegacyMultilineTextView) {
|
|
||||||
self.parent = parent
|
|
||||||
}
|
|
||||||
|
|
||||||
func textViewDidBeginEditing(_ textView: UITextView) {
|
|
||||||
if !parent.isFocused {
|
|
||||||
parent.isFocused = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func textViewDidEndEditing(_ textView: UITextView) {
|
|
||||||
if parent.isFocused {
|
|
||||||
parent.isFocused = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func textViewDidChange(_ textView: UITextView) {
|
|
||||||
if parent.text != textView.text {
|
|
||||||
parent.text = textView.text
|
|
||||||
}
|
|
||||||
updatePlaceholderVisibility(for: textView)
|
|
||||||
LegacyMultilineTextView.recalculateHeight(for: textView, result: parent.calculatedHeightBinding, minHeight: parent.minHeight, maxLines: parent.maxLines)
|
|
||||||
}
|
|
||||||
|
|
||||||
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
|
||||||
if text == "\n" {
|
|
||||||
if let onSubmit = parent.onSubmit {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
onSubmit()
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func updatePlaceholderVisibility(for textView: UITextView) {
|
|
||||||
placeholderLabel.isHidden = !textView.text.isEmpty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// MARK: - Preview
|
// MARK: - Preview
|
||||||
|
|
||||||
// Previews intentionally omitted - MessageItem has custom decoding-only initializer.
|
// Previews intentionally omitted - MessageItem has custom decoding-only initializer.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user