add legacy ios 15
This commit is contained in:
		
							parent
							
								
									adba8fc568
								
							
						
					
					
						commit
						b47922694d
					
				@ -1,4 +1,7 @@
 | 
				
			|||||||
import SwiftUI
 | 
					import SwiftUI
 | 
				
			||||||
 | 
					#if canImport(UIKit)
 | 
				
			||||||
 | 
					import UIKit
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct PrivateChatView: View {
 | 
					struct PrivateChatView: View {
 | 
				
			||||||
    let chat: PrivateChatListItem
 | 
					    let chat: PrivateChatListItem
 | 
				
			||||||
@ -12,6 +15,7 @@ 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -242,30 +246,36 @@ struct PrivateChatView: View {
 | 
				
			|||||||
                .frame(width: 36, height: 36)
 | 
					                .frame(width: 36, height: 36)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                ZStack(alignment: .bottomTrailing) {
 | 
					                ZStack(alignment: .bottomTrailing) {
 | 
				
			||||||
                    if #available(iOS 16.0, *) {
 | 
					                    Group {
 | 
				
			||||||
                        TextField(inputTab.placeholder, text: $draftText, axis: .vertical)
 | 
					                        if #available(iOS 160.0, *) {
 | 
				
			||||||
                            .lineLimit(1...4)
 | 
					                            TextField(inputTab.placeholder, text: $draftText, axis: .vertical)
 | 
				
			||||||
                            .focused($isComposerFocused)
 | 
					                                .lineLimit(1...4)
 | 
				
			||||||
                            .submitLabel(.send)
 | 
					                                .focused($isComposerFocused)
 | 
				
			||||||
                            .disabled(currentUserId == nil)
 | 
					                                .submitLabel(.send)
 | 
				
			||||||
                            .onSubmit { sendCurrentMessage() }
 | 
					                                .disabled(currentUserId == nil)
 | 
				
			||||||
                            .padding(.top, 10)
 | 
					                                .onSubmit { sendCurrentMessage() }
 | 
				
			||||||
                            .padding(.leading, 12)
 | 
					                        } else {
 | 
				
			||||||
                            .padding(.trailing, 44)
 | 
					                            LegacyMultilineTextView(
 | 
				
			||||||
                            .padding(.bottom, 10)
 | 
					                                text: $draftText,
 | 
				
			||||||
                            .frame(maxWidth: .infinity, minHeight: 40, alignment: .bottomLeading)
 | 
					                                placeholder: inputTab.placeholder,
 | 
				
			||||||
                    } else {
 | 
					                                isFocused: Binding(
 | 
				
			||||||
                        TextField(inputTab.placeholder, text: $draftText)
 | 
					                                    get: { isComposerFocused },
 | 
				
			||||||
                            .focused($isComposerFocused)
 | 
					                                    set: { isComposerFocused = $0 }
 | 
				
			||||||
                            .submitLabel(.send)
 | 
					                                ),
 | 
				
			||||||
                            .disabled(currentUserId == nil)
 | 
					                                isEnabled: currentUserId != nil,
 | 
				
			||||||
                            .onSubmit { sendCurrentMessage() }
 | 
					                                minHeight: 10,
 | 
				
			||||||
                            .padding(.top, 10)
 | 
					                                maxLines: 4,
 | 
				
			||||||
                            .padding(.leading, 12)
 | 
					                                calculatedHeight: $legacyComposerHeight,
 | 
				
			||||||
                            .padding(.trailing, 44)
 | 
					                                onSubmit: sendCurrentMessage
 | 
				
			||||||
                            .padding(.bottom, 10)
 | 
					                            )
 | 
				
			||||||
                            .frame(maxWidth: .infinity, minHeight: 40, alignment: .bottomLeading)
 | 
					                            .frame(height: legacyComposerHeight)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					                    .padding(.top, 10)
 | 
				
			||||||
 | 
					                    .padding(.leading, 12)
 | 
				
			||||||
 | 
					                    .padding(.trailing, 44)
 | 
				
			||||||
 | 
					                    .padding(.bottom, 10)
 | 
				
			||||||
 | 
					                    .frame(maxWidth: .infinity, minHeight: 40, alignment: .bottomLeading)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Button(action: {  }) { // переключатель на стикеры
 | 
					                    Button(action: {  }) { // переключатель на стикеры
 | 
				
			||||||
                        Image(systemName: "face.smiling")
 | 
					                        Image(systemName: "face.smiling")
 | 
				
			||||||
@ -455,6 +465,155 @@ 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