import SwiftUI struct TopBarView: View { var title: String // Состояния для ProfileTab @Binding var selectedAccount: String // @Binding var sheetType: ProfileTab.SheetType? var accounts: [String] // var viewModel: LoginViewModel @ObservedObject var viewModel: LoginViewModel // Привязка для управления боковым меню @Binding var isSideMenuPresented: Bool @Binding var chatSearchRevealProgress: CGFloat @Binding var chatSearchText: String var isHomeTab: Bool { return title == "Home" } var isChatsTab: Bool { return title == "Chats" } var isProfileTab: Bool { return title == "Profile" } private var shouldShowConnectionStatus: Bool { viewModel.socketState != .connected } var body: some View { VStack(spacing: 0) { HStack { // Кнопка "Гамбургер" для открытия меню Button(action: { withAnimation { isSideMenuPresented.toggle() } }) { Image(systemName: "line.horizontal.3") .imageScale(.large) .foregroundColor(.primary) } // Spacer() if shouldShowConnectionStatus { connectionStatusView Spacer() } else if isHomeTab{ Text("Yobble") .font(.largeTitle) .fontWeight(.bold) Spacer() } else if isProfileTab { Spacer() Button(action: { }) { HStack(spacing: 4) { Text("@\(viewModel.username)") .font(.headline) .foregroundColor(.primary) Image(systemName: "chevron.down") .font(.subheadline) .foregroundColor(.gray) } } Spacer() } else { Text(title) .font(.largeTitle) .fontWeight(.bold) Spacer() } if isHomeTab{ HStack(spacing: 20) { // Кнопка поиска Button(action: { // пока ничего не делаем }) { Image(systemName: "magnifyingglass") .imageScale(.large) .foregroundColor(.primary) } // Кнопка уведомлений Button(action: { // пока ничего не делаем }) { Image(systemName: "bell") .imageScale(.large) .foregroundColor(.primary) } } } else if isChatsTab { Button(action: { NotificationCenter.default.post(name: .debugRefreshChats, object: nil) }) { Text(NSLocalizedString("DEBUG UPDATE", comment: "")) .foregroundColor(.primary) } } else if isProfileTab { NavigationLink(destination: SettingsView(viewModel: viewModel)) { Image(systemName: "wrench") .imageScale(.large) .foregroundColor(.primary) } } } .padding() .frame(height: 50) // Стандартная высота для нав. бара if isChatsTab { revealableSearchBar } Divider() } .background(Color(UIColor.systemBackground)) .onChange(of: isChatsTab) { isChats in let hasSearchText = !chatSearchText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty if !isChats { guard !hasSearchText else { return } withAnimation(.spring(response: 0.35, dampingFraction: 0.75)) { chatSearchRevealProgress = 0 } } else if hasSearchText { withAnimation(.spring(response: 0.35, dampingFraction: 0.75)) { chatSearchRevealProgress = 1 } } } } } private extension TopBarView { var connectionStatusView: some View { HStack(spacing: 8) { ProgressView() .progressViewStyle(.circular) Text(NSLocalizedString("Подключение", comment: "")) .font(.headline) .foregroundColor(.primary) } } private var normalizedRevealProgress: CGFloat { guard isChatsTab else { return 0 } return max(0, min(chatSearchRevealProgress, 1)) } private var revealableSearchBar: some View { let progress = normalizedRevealProgress return VStack(spacing: 0) { searchBar .padding(.horizontal) .padding(.bottom, searchBarBottomSpacing) .opacity(progress) } .frame(height: searchBarRevealHeight * progress, alignment: .top) .clipped() .allowsHitTesting(progress > 0.9) .accessibilityHidden(progress < 0.9) } private var searchBarRevealHeight: CGFloat { searchBarBaseHeight + searchBarBottomSpacing } // 36 min height + 6 * 2 vertical padding inside searchBar private var searchBarBaseHeight: CGFloat { 48 } private var searchBarBottomSpacing: CGFloat { 0 } var searchBar: some View { HStack(spacing: 8) { Image(systemName: "magnifyingglass") .foregroundColor(.secondary) TextField(NSLocalizedString("Поиск", comment: ""), text: $chatSearchText) .textFieldStyle(.plain) .textInputAutocapitalization(.never) .autocorrectionDisabled() if !chatSearchText.isEmpty { Button(action: { chatSearchText = "" }) { Image(systemName: "xmark.circle.fill") .foregroundColor(.secondary) } .buttonStyle(.plain) } } .padding(.horizontal, 12) .padding(.vertical, 6) .frame(minHeight: 36) .background( RoundedRectangle(cornerRadius: 12, style: .continuous) .fill(Color(UIColor.secondarySystemBackground)) ) } } struct TopBarView_Previews: PreviewProvider { struct Wrapper: View { @State private var selectedAccount = "@user" @State private var isSideMenuPresented = false @State private var revealProgress: CGFloat = 1 @StateObject private var viewModel = LoginViewModel() @State private var searchText: String = "" var body: some View { TopBarView( title: "Chats", selectedAccount: $selectedAccount, accounts: [selectedAccount], viewModel: viewModel, isSideMenuPresented: $isSideMenuPresented, chatSearchRevealProgress: $revealProgress, chatSearchText: $searchText ) } } static var previews: some View { Wrapper() .environmentObject(ThemeManager()) } }