import SwiftUI struct TopBarView: View { var title: String let isMessengerModeEnabled: Bool // Состояния для ProfileTab @Binding var selectedAccount: String // @Binding var sheetType: ProfileTab.SheetType? var accounts: [String] // var viewModel: LoginViewModel @ObservedObject var viewModel: LoginViewModel @Binding var isSettingsPresented: Bool @Binding var isQrPresented: Bool // Привязка для управления боковым меню @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" } var isContactsTab: Bool { return title == "Contacts" } private var statusMessage: String? { if viewModel.chatLoadingState == .loading { return NSLocalizedString("Загрузка чатов", comment: "") } if viewModel.socketState == .connecting { return NSLocalizedString("Подключение", comment: "") } return nil } var body: some View { VStack(spacing: 0) { HStack { if !isMessengerModeEnabled{ // Кнопка "Гамбургер" для открытия меню Button(action: { withAnimation { isSideMenuPresented.toggle() } }) { Image(systemName: "line.horizontal.3") .imageScale(.large) .foregroundColor(.primary) } } // Spacer() if let statusMessage { connectionStatusView(message: statusMessage) 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 isProfileTab { NavigationLink(isActive: $isSettingsPresented) { SettingsView(viewModel: viewModel) } label: { Image(systemName: "wrench") .imageScale(.large) .foregroundColor(.primary) } } else if isContactsTab { NavigationLink(isActive: $isQrPresented) { QrView() } label: { Image(systemName: "qrcode.viewfinder") .imageScale(.large) .foregroundColor(.primary) } } // else if isChatsTab { // Button(action: { // NotificationCenter.default.post(name: .debugRefreshChats, object: nil) // }) { // Text(NSLocalizedString("DEBUG UPDATE", comment: "")) // .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 { func connectionStatusView(message: String) -> some View { HStack(spacing: 8) { ProgressView() .progressViewStyle(.circular) Text(message) .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 = "" @State private var isSettingsPresented = false @State private var isQrPresented = false var body: some View { TopBarView( title: "Chats", isMessengerModeEnabled: false, selectedAccount: $selectedAccount, accounts: [selectedAccount], viewModel: viewModel, isSettingsPresented: $isSettingsPresented, isQrPresented: $isSettingsPresented, isSideMenuPresented: $isSideMenuPresented, chatSearchRevealProgress: $revealProgress, chatSearchText: $searchText, ) } } static var previews: some View { Wrapper() .environmentObject(ThemeManager()) } }