scroll to top while tap tap
This commit is contained in:
		
							parent
							
								
									198b51bd91
								
							
						
					
					
						commit
						1c9f249289
					
				@ -32,6 +32,7 @@ struct ChatsTab: View {
 | 
				
			|||||||
    @State private var isPendingChatActive: Bool = false
 | 
					    @State private var isPendingChatActive: Bool = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private let searchRevealDistance: CGFloat = 90
 | 
					    private let searchRevealDistance: CGFloat = 90
 | 
				
			||||||
 | 
					    private let scrollToTopAnchorId = "ChatsListTopAnchor"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private var currentUserId: String? {
 | 
					    private var currentUserId: String? {
 | 
				
			||||||
        let userId = loginViewModel.userId
 | 
					        let userId = loginViewModel.userId
 | 
				
			||||||
@ -109,8 +110,9 @@ struct ChatsTab: View {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private var chatList: some View {
 | 
					    private var chatList: some View {
 | 
				
			||||||
        ZStack {
 | 
					        ScrollViewReader { proxy in
 | 
				
			||||||
            List {
 | 
					            ZStack {
 | 
				
			||||||
 | 
					                List {
 | 
				
			||||||
//            VStack(spacing: 0) {
 | 
					//            VStack(spacing: 0) {
 | 
				
			||||||
//                searchBar
 | 
					//                searchBar
 | 
				
			||||||
//                    .padding(.horizontal, 16)
 | 
					//                    .padding(.horizontal, 16)
 | 
				
			||||||
@ -118,63 +120,70 @@ struct ChatsTab: View {
 | 
				
			|||||||
//            }
 | 
					//            }
 | 
				
			||||||
//            .background(Color(UIColor.systemBackground))
 | 
					//            .background(Color(UIColor.systemBackground))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if let message = viewModel.errorMessage {
 | 
					                    if let message = viewModel.errorMessage {
 | 
				
			||||||
                    Section {
 | 
					                        Section {
 | 
				
			||||||
                        HStack(alignment: .top, spacing: 8) {
 | 
					                            HStack(alignment: .top, spacing: 8) {
 | 
				
			||||||
                            Image(systemName: "exclamationmark.triangle.fill")
 | 
					                                Image(systemName: "exclamationmark.triangle.fill")
 | 
				
			||||||
                                .foregroundColor(.orange)
 | 
					                                    .foregroundColor(.orange)
 | 
				
			||||||
                            Text(message)
 | 
					                                Text(message)
 | 
				
			||||||
                                .font(.subheadline)
 | 
					 | 
				
			||||||
                                .foregroundColor(.orange)
 | 
					 | 
				
			||||||
                            Spacer(minLength: 0)
 | 
					 | 
				
			||||||
                            Button(action: triggerChatsReload) {
 | 
					 | 
				
			||||||
                                Text(NSLocalizedString("Обновить", comment: ""))
 | 
					 | 
				
			||||||
                                    .font(.subheadline)
 | 
					                                    .font(.subheadline)
 | 
				
			||||||
 | 
					                                    .foregroundColor(.orange)
 | 
				
			||||||
 | 
					                                Spacer(minLength: 0)
 | 
				
			||||||
 | 
					                                Button(action: triggerChatsReload) {
 | 
				
			||||||
 | 
					                                    Text(NSLocalizedString("Обновить", comment: ""))
 | 
				
			||||||
 | 
					                                        .font(.subheadline)
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
 | 
					                            .padding(.vertical, 4)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        .padding(.vertical, 4)
 | 
					                        .listRowInsets(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16))
 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    .listRowInsets(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16))
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if isSearching {
 | 
					 | 
				
			||||||
                    Section(header: localSearchHeader) {
 | 
					 | 
				
			||||||
                        if localSearchResults.isEmpty {
 | 
					 | 
				
			||||||
                            emptySearchResultView
 | 
					 | 
				
			||||||
                                .listRowInsets(EdgeInsets(top: 24, leading: 16, bottom: 24, trailing: 16))
 | 
					 | 
				
			||||||
                                .listRowSeparator(.hidden)
 | 
					 | 
				
			||||||
                        } else {
 | 
					 | 
				
			||||||
                            ForEach(localSearchResults) { chat in
 | 
					 | 
				
			||||||
                                chatRowItem(for: chat)
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Section(header: globalSearchHeader) {
 | 
					                    if isSearching {
 | 
				
			||||||
                        globalSearchContent
 | 
					                        Section(header: localSearchHeader) {
 | 
				
			||||||
                    }
 | 
					                            if localSearchResults.isEmpty {
 | 
				
			||||||
                } else {
 | 
					                                emptySearchResultView
 | 
				
			||||||
 | 
					                                    .listRowInsets(EdgeInsets(top: 24, leading: 16, bottom: 24, trailing: 16))
 | 
				
			||||||
 | 
					                                    .listRowSeparator(.hidden)
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                let firstLocalChatId = localSearchResults.first?.chatId
 | 
				
			||||||
 | 
					                                ForEach(localSearchResults) { chat in
 | 
				
			||||||
 | 
					                                    chatRowItem(for: chat)
 | 
				
			||||||
 | 
					                                        .id(chat.chatId == firstLocalChatId ? scrollToTopAnchorId : chat.chatId)
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        Section(header: globalSearchHeader) {
 | 
				
			||||||
 | 
					                            globalSearchContent
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
//                    if let message = viewModel.errorMessage, viewModel.chats.isEmpty {
 | 
					//                    if let message = viewModel.errorMessage, viewModel.chats.isEmpty {
 | 
				
			||||||
//                        errorState(message: message)
 | 
					//                        errorState(message: message)
 | 
				
			||||||
//                    } else
 | 
					//                    } else
 | 
				
			||||||
                    if viewModel.chats.isEmpty {
 | 
					                        if viewModel.chats.isEmpty {
 | 
				
			||||||
                        emptyState
 | 
					                            emptyState
 | 
				
			||||||
                    } else {
 | 
					                        } else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        ForEach(viewModel.chats) { chat in
 | 
					                            let firstChatId = viewModel.chats.first?.chatId
 | 
				
			||||||
                            chatRowItem(for: chat)
 | 
					                            ForEach(viewModel.chats) { chat in
 | 
				
			||||||
                        }
 | 
					                                chatRowItem(for: chat)
 | 
				
			||||||
 | 
					                                    .id(chat.chatId == firstChatId ? scrollToTopAnchorId : chat.chatId)
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        if viewModel.isLoadingMore {
 | 
					                            if viewModel.isLoadingMore {
 | 
				
			||||||
                            loadingMoreRow
 | 
					                                loadingMoreRow
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					                .listStyle(.plain)
 | 
				
			||||||
            .listStyle(.plain)
 | 
					                .modifier(ScrollDismissesKeyboardModifier())
 | 
				
			||||||
            .modifier(ScrollDismissesKeyboardModifier())
 | 
					                .simultaneousGesture(searchBarGesture)
 | 
				
			||||||
            .simultaneousGesture(searchBarGesture)
 | 
					                .simultaneousGesture(tapToDismissKeyboardGesture)
 | 
				
			||||||
            .simultaneousGesture(tapToDismissKeyboardGesture)
 | 
					                .onReceive(NotificationCenter.default.publisher(for: .chatsShouldScrollToTop)) { _ in
 | 
				
			||||||
 | 
					                    scrollChatsToTop(using: proxy)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
//            .safeAreaInset(edge: .top) {
 | 
					//            .safeAreaInset(edge: .top) {
 | 
				
			||||||
//                VStack(spacing: 0) {
 | 
					//                VStack(spacing: 0) {
 | 
				
			||||||
//                    searchBar
 | 
					//                    searchBar
 | 
				
			||||||
@ -186,7 +195,8 @@ struct ChatsTab: View {
 | 
				
			|||||||
//                .background(Color(UIColor.systemBackground))
 | 
					//                .background(Color(UIColor.systemBackground))
 | 
				
			||||||
//            }
 | 
					//            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            pendingChatNavigationLink
 | 
					                pendingChatNavigationLink
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -217,6 +227,14 @@ struct ChatsTab: View {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private func scrollChatsToTop(using proxy: ScrollViewProxy) {
 | 
				
			||||||
 | 
					        DispatchQueue.main.async {
 | 
				
			||||||
 | 
					            withAnimation(.spring(response: 0.35, dampingFraction: 0.75)) {
 | 
				
			||||||
 | 
					                proxy.scrollTo(scrollToTopAnchorId, anchor: .top)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private var searchBarGesture: some Gesture {
 | 
					    private var searchBarGesture: some Gesture {
 | 
				
			||||||
        DragGesture(minimumDistance: 10, coordinateSpace: .local)
 | 
					        DragGesture(minimumDistance: 10, coordinateSpace: .local)
 | 
				
			||||||
            .onChanged { value in
 | 
					            .onChanged { value in
 | 
				
			||||||
@ -319,8 +337,10 @@ struct ChatsTab: View {
 | 
				
			|||||||
        if globalSearchResults.isEmpty {
 | 
					        if globalSearchResults.isEmpty {
 | 
				
			||||||
            globalSearchEmptyRow
 | 
					            globalSearchEmptyRow
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
 | 
					            let firstGlobalUserId = globalSearchResults.first?.id
 | 
				
			||||||
            ForEach(globalSearchResults) { user in
 | 
					            ForEach(globalSearchResults) { user in
 | 
				
			||||||
                globalSearchRow(for: user)
 | 
					                globalSearchRow(for: user)
 | 
				
			||||||
 | 
					                    .id(user.id == firstGlobalUserId ? AnyHashable(scrollToTopAnchorId) : AnyHashable(user.id))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -1181,4 +1201,5 @@ extension Notification.Name {
 | 
				
			|||||||
    static let debugRefreshChats = Notification.Name("debugRefreshChats")
 | 
					    static let debugRefreshChats = Notification.Name("debugRefreshChats")
 | 
				
			||||||
    static let chatsShouldRefresh = Notification.Name("chatsShouldRefresh")
 | 
					    static let chatsShouldRefresh = Notification.Name("chatsShouldRefresh")
 | 
				
			||||||
    static let chatsReloadCompleted = Notification.Name("chatsReloadCompleted")
 | 
					    static let chatsReloadCompleted = Notification.Name("chatsReloadCompleted")
 | 
				
			||||||
 | 
					    static let chatsShouldScrollToTop = Notification.Name("chatsShouldScrollToTop")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -14,7 +14,7 @@ struct CustomTabBar: View {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                TabBarButton(systemName: "bubble.left.and.bubble.right.fill", text: NSLocalizedString("Чаты", comment: ""), isSelected: selectedTab == 2) {
 | 
					                TabBarButton(systemName: "bubble.left.and.bubble.right.fill", text: NSLocalizedString("Чаты", comment: ""), isSelected: selectedTab == 2) {
 | 
				
			||||||
                    selectedTab = 2
 | 
					                    handleChatsTabTap()
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                TabBarButton(systemName: "gearshape.fill", text: NSLocalizedString("Настройки", comment: ""), isSelected: selectedTab == 5) {
 | 
					                TabBarButton(systemName: "gearshape.fill", text: NSLocalizedString("Настройки", comment: ""), isSelected: selectedTab == 5) {
 | 
				
			||||||
@ -34,7 +34,7 @@ struct CustomTabBar: View {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                TabBarButton(systemName: "bubble.left.and.bubble.right.fill", text: NSLocalizedString("Чаты", comment: ""), isSelected: selectedTab == 2) {
 | 
					                TabBarButton(systemName: "bubble.left.and.bubble.right.fill", text: NSLocalizedString("Чаты", comment: ""), isSelected: selectedTab == 2) {
 | 
				
			||||||
                    selectedTab = 2
 | 
					                    handleChatsTabTap()
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                TabBarButton(systemName: "person.crop.square", text: NSLocalizedString("Лицо", comment: ""), isSelected: selectedTab == 3) {
 | 
					                TabBarButton(systemName: "person.crop.square", text: NSLocalizedString("Лицо", comment: ""), isSelected: selectedTab == 3) {
 | 
				
			||||||
@ -93,3 +93,13 @@ struct CreateButton: View {
 | 
				
			|||||||
        .offset(y: -3)
 | 
					        .offset(y: -3)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private extension CustomTabBar {
 | 
				
			||||||
 | 
					    func handleChatsTabTap() {
 | 
				
			||||||
 | 
					        if selectedTab == 2 {
 | 
				
			||||||
 | 
					            NotificationCenter.default.post(name: .chatsShouldScrollToTop, object: nil)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            selectedTab = 2
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user