260 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Swift
		
	
	
	
	
	
			
		
		
	
	
			260 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Swift
		
	
	
	
	
	
import SwiftUI
 | 
						||
 | 
						||
struct MainView: View {
 | 
						||
    @ObservedObject var viewModel: LoginViewModel
 | 
						||
    @EnvironmentObject private var messageCenter: IncomingMessageCenter
 | 
						||
    @State private var selectedTab: Int = 0
 | 
						||
    @AppStorage("messengerModeEnabled") private var isMessengerModeEnabled: Bool = false
 | 
						||
//    @StateObject private var newHomeTabViewModel = NewHomeTabViewModel()
 | 
						||
    
 | 
						||
    // Состояния для TopBarView
 | 
						||
    @State private var selectedAccount = "@user1"
 | 
						||
    @State private var accounts = ["@user1", "@user2", "@user3"]
 | 
						||
//    @State private var sheetType: ProfileTab.SheetType? = nil
 | 
						||
    
 | 
						||
    // Состояния для бокового меню
 | 
						||
    @State private var isSideMenuPresented = false
 | 
						||
    @State private var menuOffset: CGFloat = 0
 | 
						||
    @State private var chatSearchRevealProgress: CGFloat = 0
 | 
						||
    @State private var chatSearchText: String = ""
 | 
						||
    @State private var isSettingsPresented = false
 | 
						||
    @State private var isQrPresented = false
 | 
						||
    @State private var deepLinkChatItem: PrivateChatListItem?
 | 
						||
    @State private var isDeepLinkChatActive = false
 | 
						||
 | 
						||
    private var tabTitle: String {
 | 
						||
        switch selectedTab {
 | 
						||
        case 0: return NSLocalizedString("Home", comment: "")
 | 
						||
        case 1: return NSLocalizedString("Concept", comment: "")
 | 
						||
        case 2: return NSLocalizedString("Чаты", comment: "")
 | 
						||
        case 3: return NSLocalizedString("Profile", comment: "")
 | 
						||
        case 4: return NSLocalizedString("Контакты", comment: "")
 | 
						||
        case 5: return NSLocalizedString("Настройки", comment: "")
 | 
						||
        default: return NSLocalizedString("Home", comment: "")
 | 
						||
        }
 | 
						||
    }
 | 
						||
    
 | 
						||
    private var menuWidth: CGFloat {
 | 
						||
        UIScreen.main.bounds.width * 0.8
 | 
						||
    }
 | 
						||
 | 
						||
    var body: some View {
 | 
						||
        NavigationView {
 | 
						||
            ZStack(alignment: .top) {
 | 
						||
                ZStack(alignment: .leading) { // Выравниваем ZStack по левому краю
 | 
						||
                    // Основной контент
 | 
						||
                    VStack(spacing: 0) {
 | 
						||
                        TopBarView(
 | 
						||
                            title: tabTitle,
 | 
						||
                            isMessengerModeEnabled: isMessengerModeEnabled,
 | 
						||
                            selectedAccount: $selectedAccount,
 | 
						||
                            accounts: accounts,
 | 
						||
                            viewModel: viewModel,
 | 
						||
                            isSettingsPresented: $isSettingsPresented,
 | 
						||
                            isQrPresented: $isQrPresented,
 | 
						||
                            isSideMenuPresented: $isSideMenuPresented,
 | 
						||
                            chatSearchRevealProgress: $chatSearchRevealProgress,
 | 
						||
                            chatSearchText: $chatSearchText
 | 
						||
                        )
 | 
						||
                        
 | 
						||
                        ZStack {
 | 
						||
                            if isMessengerModeEnabled {
 | 
						||
                                ChatsTab(
 | 
						||
                                    loginViewModel: viewModel,
 | 
						||
                                    searchRevealProgress: $chatSearchRevealProgress,
 | 
						||
                                    searchText: $chatSearchText
 | 
						||
                                )
 | 
						||
                                .opacity(selectedTab == 2 ? 1 : 0)
 | 
						||
                                .allowsHitTesting(selectedTab == 2)
 | 
						||
 | 
						||
                                ContactsTab()
 | 
						||
                                    .opacity(selectedTab == 4 ? 1 : 0)
 | 
						||
 | 
						||
                                SettingsView(viewModel: viewModel)
 | 
						||
                                    .opacity(selectedTab == 5 ? 1 : 0)
 | 
						||
                            } else {
 | 
						||
                                NewHomeTab()
 | 
						||
                                    .opacity(selectedTab == 0 ? 1 : 0)
 | 
						||
 | 
						||
                                ConceptTab()
 | 
						||
                                    .opacity(selectedTab == 1 ? 1 : 0)
 | 
						||
 | 
						||
                                ChatsTab(
 | 
						||
                                    loginViewModel: viewModel,
 | 
						||
                                    searchRevealProgress: $chatSearchRevealProgress,
 | 
						||
                                    searchText: $chatSearchText
 | 
						||
                                )
 | 
						||
                                .opacity(selectedTab == 2 ? 1 : 0)
 | 
						||
                                .allowsHitTesting(selectedTab == 2)
 | 
						||
 | 
						||
                                ProfileTab()
 | 
						||
                                    .opacity(selectedTab == 3 ? 1 : 0)
 | 
						||
                            }
 | 
						||
                        }
 | 
						||
                        .frame(maxWidth: .infinity, maxHeight: .infinity)
 | 
						||
                        
 | 
						||
                        CustomTabBar(selectedTab: $selectedTab, isMessengerModeEnabled: isMessengerModeEnabled) {
 | 
						||
                            print("Create button tapped")
 | 
						||
                        }
 | 
						||
                    }
 | 
						||
                    .frame(maxWidth: .infinity, maxHeight: .infinity) // Убедимся, что основной контент занимает все пространство
 | 
						||
                    .ignoresSafeArea(edges: .bottom)
 | 
						||
                    .navigationBarHidden(true)
 | 
						||
//                    .sheet(item: $sheetType) { type in
 | 
						||
//                        // ... sheet presentation logic
 | 
						||
//                    }
 | 
						||
                    
 | 
						||
                    // Затемнение и закрытие по тапу
 | 
						||
                    Color.black
 | 
						||
                        .opacity(Double(menuOffset / menuWidth) * 0.4)
 | 
						||
                        .ignoresSafeArea()
 | 
						||
                        .onTapGesture {
 | 
						||
                            withAnimation(.easeInOut) {
 | 
						||
                                isSideMenuPresented = false
 | 
						||
                            }
 | 
						||
                        }
 | 
						||
                        .allowsHitTesting(menuOffset > 0)
 | 
						||
 | 
						||
                    // Боковое меню
 | 
						||
                    if !isMessengerModeEnabled {
 | 
						||
                        SideMenuView(viewModel: viewModel, isPresented: $isSideMenuPresented)
 | 
						||
                            .frame(width: menuWidth)
 | 
						||
                            .offset(x: -menuWidth + menuOffset) // Новая логика смещения
 | 
						||
                            .ignoresSafeArea(edges: .vertical)
 | 
						||
                    }
 | 
						||
                }
 | 
						||
 | 
						||
                deepLinkNavigationLink
 | 
						||
            }
 | 
						||
            .gesture(
 | 
						||
                DragGesture()
 | 
						||
                    .onChanged { gesture in
 | 
						||
                        if !isMessengerModeEnabled {
 | 
						||
                            if !isSideMenuPresented && gesture.startLocation.x > 60 { return }
 | 
						||
                            
 | 
						||
                            let translation = gesture.translation.width
 | 
						||
                            
 | 
						||
                            // Определяем базовое смещение в зависимости от того, открыто меню или нет
 | 
						||
                            let baseOffset = isSideMenuPresented ? menuWidth : 0
 | 
						||
                            
 | 
						||
                            // Новое смещение — это база плюс текущий свайп
 | 
						||
                            let newOffset = baseOffset + translation
 | 
						||
                            
 | 
						||
                            // Жестко ограничиваем итоговое смещение между 0 и шириной меню
 | 
						||
                            self.menuOffset = max(0, min(menuWidth, newOffset))
 | 
						||
                        }
 | 
						||
                    }
 | 
						||
                    .onEnded { gesture in
 | 
						||
                        if !isMessengerModeEnabled {
 | 
						||
                            if !isSideMenuPresented && gesture.startLocation.x > 60 { return }
 | 
						||
                            
 | 
						||
                            let threshold = menuWidth * 0.4
 | 
						||
                            
 | 
						||
                            withAnimation(.easeInOut) {
 | 
						||
                                if self.menuOffset > threshold {
 | 
						||
                                    isSideMenuPresented = true
 | 
						||
                                } else {
 | 
						||
                                    isSideMenuPresented = false
 | 
						||
                                }
 | 
						||
                                // Устанавливаем финальное смещение после анимации
 | 
						||
                                self.menuOffset = isSideMenuPresented ? menuWidth : 0
 | 
						||
                            }
 | 
						||
                        }
 | 
						||
                    }
 | 
						||
            )
 | 
						||
        }
 | 
						||
        .navigationViewStyle(StackNavigationViewStyle())
 | 
						||
        .onChange(of: isSideMenuPresented) { presented in
 | 
						||
            // Плавная анимация при нажатии на кнопку, а не только при жесте
 | 
						||
            withAnimation(.easeInOut) {
 | 
						||
                menuOffset = presented ? menuWidth : 0
 | 
						||
            }
 | 
						||
        }
 | 
						||
        .onAppear {
 | 
						||
            enforceTabSelectionForMessengerMode()
 | 
						||
        }
 | 
						||
        .onChange(of: isMessengerModeEnabled) { _ in
 | 
						||
            enforceTabSelectionForMessengerMode()
 | 
						||
        }
 | 
						||
        .onChange(of: messageCenter.pendingNavigation?.id) { _ in
 | 
						||
            guard !AppConfig.PRESENT_CHAT_AS_SHEET,
 | 
						||
                  let target = messageCenter.pendingNavigation else { return }
 | 
						||
            withAnimation(.easeInOut) {
 | 
						||
                isSideMenuPresented = false
 | 
						||
                menuOffset = 0
 | 
						||
            }
 | 
						||
            if !chatSearchText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
 | 
						||
                chatSearchText = ""
 | 
						||
            }
 | 
						||
            if chatSearchRevealProgress > 0 {
 | 
						||
                withAnimation(.spring(response: 0.35, dampingFraction: 0.75)) {
 | 
						||
                    chatSearchRevealProgress = 0
 | 
						||
                }
 | 
						||
            }
 | 
						||
            deepLinkChatItem = target.chat
 | 
						||
            isDeepLinkChatActive = true
 | 
						||
            NotificationCenter.default.post(name: .chatsShouldRefresh, object: nil)
 | 
						||
            DispatchQueue.main.async {
 | 
						||
                messageCenter.pendingNavigation = nil
 | 
						||
            }
 | 
						||
        }
 | 
						||
        .onChange(of: selectedTab) { newValue in
 | 
						||
            if newValue != 3 {
 | 
						||
                isSettingsPresented = false
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
private extension MainView {
 | 
						||
    func enforceTabSelectionForMessengerMode() {
 | 
						||
        if isMessengerModeEnabled {
 | 
						||
            if selectedTab < 2 {
 | 
						||
                selectedTab = 2
 | 
						||
            }
 | 
						||
        } else if selectedTab > 3 {
 | 
						||
            selectedTab = 0
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    var deepLinkNavigationLink: some View {
 | 
						||
        NavigationLink(
 | 
						||
            destination: deepLinkChatDestination,
 | 
						||
            isActive: Binding(
 | 
						||
                get: { isDeepLinkChatActive && deepLinkChatItem != nil },
 | 
						||
                set: { newValue in
 | 
						||
                    if !newValue {
 | 
						||
                        isDeepLinkChatActive = false
 | 
						||
                        deepLinkChatItem = nil
 | 
						||
                    }
 | 
						||
                }
 | 
						||
            )
 | 
						||
        ) {
 | 
						||
            EmptyView()
 | 
						||
        }
 | 
						||
        .hidden()
 | 
						||
    }
 | 
						||
 | 
						||
    @ViewBuilder
 | 
						||
    var deepLinkChatDestination: some View {
 | 
						||
        if let chatItem = deepLinkChatItem {
 | 
						||
            PrivateChatView(
 | 
						||
                chat: chatItem,
 | 
						||
                currentUserId: messageCenter.currentUserId
 | 
						||
            )
 | 
						||
            .id(chatItem.chatId)
 | 
						||
        } else {
 | 
						||
            EmptyView()
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
struct MainView_Previews: PreviewProvider {
 | 
						||
    static var previews: some View {
 | 
						||
        let mockViewModel = LoginViewModel()
 | 
						||
        MainView(viewModel: mockViewModel)
 | 
						||
            .environmentObject(IncomingMessageCenter())
 | 
						||
            .environmentObject(ThemeManager())
 | 
						||
    }
 | 
						||
}
 |