ios_app_v2/yobble/Components/TopBarView.swift
2025-10-21 03:12:35 +03:00

227 lines
7.8 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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())
}
}