add button scroll down
This commit is contained in:
		
							parent
							
								
									b9ea0807e5
								
							
						
					
					
						commit
						b267e5a999
					
				@ -8,6 +8,7 @@ struct PrivateChatView: View {
 | 
				
			|||||||
    @StateObject private var viewModel: PrivateChatViewModel
 | 
					    @StateObject private var viewModel: PrivateChatViewModel
 | 
				
			||||||
    @State private var hasPositionedToBottom: Bool = false
 | 
					    @State private var hasPositionedToBottom: Bool = false
 | 
				
			||||||
    @State private var scrollToBottomTrigger: UUID = .init()
 | 
					    @State private var scrollToBottomTrigger: UUID = .init()
 | 
				
			||||||
 | 
					    @State private var isBottomAnchorVisible: Bool = true
 | 
				
			||||||
    @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
 | 
				
			||||||
@ -21,26 +22,34 @@ struct PrivateChatView: View {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var body: some View {
 | 
					    var body: some View {
 | 
				
			||||||
        ScrollViewReader { proxy in
 | 
					        ZStack(alignment: .bottomTrailing) {
 | 
				
			||||||
            content
 | 
					            ScrollViewReader { proxy in
 | 
				
			||||||
                .onChange(of: viewModel.messages.count) { _ in
 | 
					                content
 | 
				
			||||||
                    guard !viewModel.isLoadingMore else { return }
 | 
					                    .onChange(of: viewModel.messages.count) { _ in
 | 
				
			||||||
                    let targetId = viewModel.messages.last?.id ?? bottomAnchorId
 | 
					                        guard !viewModel.isLoadingMore else { return }
 | 
				
			||||||
                    DispatchQueue.main.async {
 | 
					                        let targetId = viewModel.messages.last?.id ?? bottomAnchorId
 | 
				
			||||||
                        withAnimation(.easeInOut(duration: 0.2)) {
 | 
					                        DispatchQueue.main.async {
 | 
				
			||||||
                            proxy.scrollTo(targetId, anchor: .bottom)
 | 
					                            withAnimation(.easeInOut(duration: 0.2)) {
 | 
				
			||||||
                        }
 | 
					                                proxy.scrollTo(targetId, anchor: .bottom)
 | 
				
			||||||
                        hasPositionedToBottom = true
 | 
					                            }
 | 
				
			||||||
                    }
 | 
					                            hasPositionedToBottom = true
 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                .onChange(of: scrollToBottomTrigger) { _ in
 | 
					 | 
				
			||||||
                    let targetId = viewModel.messages.last?.id ?? bottomAnchorId
 | 
					 | 
				
			||||||
                    DispatchQueue.main.async {
 | 
					 | 
				
			||||||
                        withAnimation(.easeInOut(duration: 0.2)) {
 | 
					 | 
				
			||||||
                            proxy.scrollTo(targetId, anchor: .bottom)
 | 
					 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                    .onChange(of: scrollToBottomTrigger) { _ in
 | 
				
			||||||
 | 
					                        let targetId = viewModel.messages.last?.id ?? bottomAnchorId
 | 
				
			||||||
 | 
					                        DispatchQueue.main.async {
 | 
				
			||||||
 | 
					                            withAnimation(.easeInOut(duration: 0.2)) {
 | 
				
			||||||
 | 
					                                proxy.scrollTo(targetId, anchor: .bottom)
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if !isBottomAnchorVisible {
 | 
				
			||||||
 | 
					                scrollToBottomButton
 | 
				
			||||||
 | 
					                    .padding(.trailing, 20)
 | 
				
			||||||
 | 
					                    .padding(.bottom, 80)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        .navigationTitle(title)
 | 
					        .navigationTitle(title)
 | 
				
			||||||
        .navigationBarTitleDisplayMode(.inline)
 | 
					        .navigationBarTitleDisplayMode(.inline)
 | 
				
			||||||
@ -99,6 +108,8 @@ struct PrivateChatView: View {
 | 
				
			|||||||
                    Color.clear
 | 
					                    Color.clear
 | 
				
			||||||
                        .frame(height: 1)
 | 
					                        .frame(height: 1)
 | 
				
			||||||
                        .id(bottomAnchorId)
 | 
					                        .id(bottomAnchorId)
 | 
				
			||||||
 | 
					                        .onAppear { isBottomAnchorVisible = true }
 | 
				
			||||||
 | 
					                        .onDisappear { isBottomAnchorVisible = false }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                .padding(.vertical, 12)
 | 
					                .padding(.vertical, 12)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -233,9 +244,9 @@ struct PrivateChatView: View {
 | 
				
			|||||||
                Button(action: {  }) { // переключатель на стикеры
 | 
					                Button(action: {  }) { // переключатель на стикеры
 | 
				
			||||||
                    Image(systemName: "paperclip")
 | 
					                    Image(systemName: "paperclip")
 | 
				
			||||||
                        .font(.system(size: 18, weight: .semibold))
 | 
					                        .font(.system(size: 18, weight: .semibold))
 | 
				
			||||||
                        .frame(width: 40, height: 40)
 | 
					 | 
				
			||||||
                        .foregroundColor(.secondary)
 | 
					                        .foregroundColor(.secondary)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                .buttonStyle(ComposerIconButtonStyle())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                ZStack(alignment: .bottomTrailing) {
 | 
					                ZStack(alignment: .bottomTrailing) {
 | 
				
			||||||
                    TextField(inputTab.placeholder, text: $draftText, axis: .vertical)
 | 
					                    TextField(inputTab.placeholder, text: $draftText, axis: .vertical)
 | 
				
			||||||
@ -269,10 +280,9 @@ struct PrivateChatView: View {
 | 
				
			|||||||
                    Button(action: { isVideoPreferred.toggle() }) {
 | 
					                    Button(action: { isVideoPreferred.toggle() }) {
 | 
				
			||||||
                        Image(systemName: isVideoPreferred ? "video.fill" : "mic.fill")
 | 
					                        Image(systemName: isVideoPreferred ? "video.fill" : "mic.fill")
 | 
				
			||||||
                            .font(.system(size: 18, weight: .semibold))
 | 
					                            .font(.system(size: 18, weight: .semibold))
 | 
				
			||||||
                            .frame(width: 40, height: 40)
 | 
					 | 
				
			||||||
                            .foregroundColor(.secondary)
 | 
					                            .foregroundColor(.secondary)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    .buttonStyle(.plain)
 | 
					                    .buttonStyle(ComposerIconButtonStyle())
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    sendButton
 | 
					                    sendButton
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -284,6 +294,19 @@ struct PrivateChatView: View {
 | 
				
			|||||||
        .background(.ultraThinMaterial)
 | 
					        .background(.ultraThinMaterial)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private var scrollToBottomButton: some View {
 | 
				
			||||||
 | 
					        Button(action: scrollToBottom) {
 | 
				
			||||||
 | 
					            Image(systemName: "arrow.down")
 | 
				
			||||||
 | 
					                .font(.system(size: 18, weight: .semibold))
 | 
				
			||||||
 | 
					                .foregroundColor(.white)
 | 
				
			||||||
 | 
					                .frame(width: 44, height: 44)
 | 
				
			||||||
 | 
					                .background(Color.accentColor)
 | 
				
			||||||
 | 
					                .clipShape(Circle())
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        .buttonStyle(.plain)
 | 
				
			||||||
 | 
					        .shadow(color: Color.black.opacity(0.2), radius: 4, x: 0, y: 2)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private var isSendDisabled: Bool {
 | 
					    private var isSendDisabled: Bool {
 | 
				
			||||||
        draftText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || currentUserId == nil
 | 
					        draftText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || currentUserId == nil
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -346,6 +369,26 @@ struct PrivateChatView: View {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private func scrollToBottom() {
 | 
				
			||||||
 | 
					        scrollToBottomTrigger = .init()
 | 
				
			||||||
 | 
					        hasPositionedToBottom = true
 | 
				
			||||||
 | 
					        isBottomAnchorVisible = true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private struct ComposerIconButtonStyle: ButtonStyle {
 | 
				
			||||||
 | 
					        func makeBody(configuration: Configuration) -> some View {
 | 
				
			||||||
 | 
					            configuration.label
 | 
				
			||||||
 | 
					                .frame(width: 36, height: 36)
 | 
				
			||||||
 | 
					                .background(Color(.secondarySystemBackground))
 | 
				
			||||||
 | 
					                .clipShape(Circle())
 | 
				
			||||||
 | 
					                .overlay(
 | 
				
			||||||
 | 
					                    Circle().stroke(Color.secondary.opacity(0.15))
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                .scaleEffect(configuration.isPressed ? 0.94 : 1)
 | 
				
			||||||
 | 
					                .opacity(configuration.isPressed ? 0.75 : 1)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private enum ComposerTab: String {
 | 
					    private enum ComposerTab: String {
 | 
				
			||||||
        case chat
 | 
					        case chat
 | 
				
			||||||
        case stickers
 | 
					        case stickers
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user