using System.Diagnostics; using CORP_MAUI.Pages; namespace CORP_MAUI; public partial class AppShell : Shell { // Порог ширины для переключения на вертикальное меню (в пикселях) private const double WideScreenThreshold = 768; private bool _isDesktopMode; private int _lastNonMenuTabIndex = 2; // По умолчанию - Лента public AppShell() { try { Trace.WriteLine("=== APP SHELL CONSTRUCTOR START ==="); Trace.WriteLine("[AppShell] >>> Вызов InitializeComponent"); InitializeComponent(); Trace.WriteLine("[AppShell] >>> InitializeComponent завершен"); Trace.WriteLine("[AppShell] >>> Регистрация маршрутов"); // Регистрация маршрутов для навигации Routing.RegisterRoute(nameof(ChatPage), typeof(ChatPage)); Routing.RegisterRoute(nameof(DepartmentDetailsPage), typeof(DepartmentDetailsPage)); Routing.RegisterRoute(nameof(EmployeeProfilePage), typeof(EmployeeProfilePage)); Routing.RegisterRoute(nameof(MenuPage), typeof(MenuPage)); Routing.RegisterRoute(nameof(DepartmentsPage), typeof(DepartmentsPage)); Routing.RegisterRoute(nameof(ProfilePage), typeof(ProfilePage)); Routing.RegisterRoute(nameof(DocumentsPage), typeof(DocumentsPage)); // Подписываемся на навигацию для сохранения предыдущей вкладки Navigating += OnShellNavigating; Trace.WriteLine("[AppShell] ✅ AppShell инициализирован"); Trace.WriteLine("=== APP SHELL CONSTRUCTOR END ==="); // Устанавливаем начальный режим меню UpdateLayoutMode(); // При запуске переключаемся на ленту (индекс 2), а не на меню (индекс 0) if (MobileTabBar?.Items?.Count > 2) { CurrentItem = MobileTabBar.Items[2]; } // Инициализация без ожидания (так await нельзя из синхронного конструктора) _ = InitializeAsync(); } catch (Exception ex) { Trace.WriteLine("=== APP SHELL CONSTRUCTOR ERROR ==="); Trace.WriteLine($"[AppShell] ❌ Тип ошибки: {ex.GetType().FullName}"); Trace.WriteLine($"[AppShell] ❌ Сообщение: {ex.Message}"); Trace.WriteLine($"[AppShell] ❌ StackTrace: {ex.StackTrace}"); if (ex.InnerException != null) { Trace.WriteLine($"[AppShell] ❌ InnerException: {ex.InnerException.Message}"); Trace.WriteLine($"[AppShell] ❌ InnerException StackTrace: {ex.InnerException.StackTrace}"); } // Показываем ошибку в диалоге Task.Run(async () => { await Task.Delay(100); await MainThread.InvokeOnMainThreadAsync(async () => { Page? mainPage = Application.Current?.Windows.FirstOrDefault()?.Page; if (mainPage != null) { await mainPage.DisplayAlert("Ошибка AppShell", $"Ошибка инициализации AppShell:\n{ex.Message}\n\nStackTrace:\n{ex.StackTrace}", "OK"); } }); }); throw; } } private async Task InitializeAsync() { try { Trace.WriteLine("[AppShell] >>> Начало InitializeAsync"); // Инициализация сервисов (если они не инициализированы) ServiceProvider.Initialize(); Trace.WriteLine($"[AppShell] IsAuthenticated: {ServiceProvider.ApiService.IsAuthenticated}"); // Проверяем, авторизован ли пользователь (токены загружаются автоматически в ApiService) if (ServiceProvider.ApiService.IsAuthenticated) { // Получаем токен для WebSocket string? accessToken = await SecureStorage.GetAsync("api.jwt_access"); Trace.WriteLine($"[AppShell] Access token exists: {!string.IsNullOrEmpty(accessToken)}"); Trace.WriteLine($"[AppShell] WebSocket connected: {ServiceProvider.WebSocketService.IsConnected}"); if (!string.IsNullOrEmpty(accessToken) && !ServiceProvider.WebSocketService.IsConnected) { Trace.WriteLine("[AppShell] >>> Подключение WebSocket из AppShell"); // Подключаем WebSocket только если его еще не подключен await ServiceProvider.WebSocketService.ConnectAsync(accessToken); Trace.WriteLine("[AppShell] ✅ WebSocket подключен из AppShell"); } else if (ServiceProvider.WebSocketService.IsConnected) { Trace.WriteLine("[AppShell] WebSocket уже подключен, пропускаем"); } } else { Trace.WriteLine("[AppShell] ⚠️ Пользователь не авторизован"); } Trace.WriteLine("[AppShell] ✅ InitializeAsync завершен"); } catch (Exception ex) { Trace.WriteLine($"[AppShell] ❌ Ошибка InitializeAsync: {ex.Message}"); Trace.WriteLine($"[AppShell] StackTrace: {ex.StackTrace}"); await DisplayAlert("Ошибка", $"Ошибка инициализации: {ex.Message}", "OK"); } } /// /// Сохраняет индекс текущей вкладки перед переходом на меню /// private void OnShellNavigating(object? sender, ShellNavigatingEventArgs e) { // Если переходим на MenuTabPage, сохраняем текущую вкладку if (e.Target?.Location?.OriginalString?.Contains("MenuTab") == true) { // Сохраняем индекс текущей вкладки (не меню) if (MobileTabBar != null) { for (int i = 1; i < MobileTabBar.Items.Count; i++) { if (MobileTabBar.Items[i] == MobileTabBar.CurrentItem) { _lastNonMenuTabIndex = i; MenuPage.SetPreviousTabIndex(i); Trace.WriteLine($"[AppShell] Сохранена предыдущая вкладка: {i}"); break; } } } } } protected override void OnSizeAllocated(double width, double height) { base.OnSizeAllocated(width, height); UpdateLayoutMode(width); } /// /// Обновляет режим макета в зависимости от ширины экрана /// На широких экранах: Flyout (вертикальное меню слева) /// На узких экранах: TabBar (нижняя панель) /// private void UpdateLayoutMode(double? width = null) { try { // Получаем текущую ширину окна double currentWidth = width ?? 0; // Если ширина не передана, пытаемся получить из разных источников if (currentWidth <= 0) { // Пробуем получить из Window var window = Application.Current?.Windows.FirstOrDefault(); if (window != null && window.Width > 0) { currentWidth = window.Width; } } if (currentWidth <= 0) { // Пробуем получить из DeviceDisplay var displayInfo = DeviceDisplay.MainDisplayInfo; if (displayInfo.Width > 0 && displayInfo.Density > 0) { currentWidth = displayInfo.Width / displayInfo.Density; } } // На Windows/Desktop по умолчанию считаем широким экраном если не удалось определить if (currentWidth <= 0) { #if WINDOWS || MACCATALYST currentWidth = 1200; // Предполагаем десктоп #else currentWidth = 400; // Предполагаем мобильное устройство #endif } bool shouldBeDesktop = currentWidth >= WideScreenThreshold; Trace.WriteLine($"[AppShell] UpdateLayoutMode: width={currentWidth}, threshold={WideScreenThreshold}, shouldBeDesktop={shouldBeDesktop}, currentMode={_isDesktopMode}"); // При первом запуске (_isDesktopMode = false) или при изменении режима bool isFirstRun = !_isDesktopMode && shouldBeDesktop; // Проверяем, нужно ли менять режим if (shouldBeDesktop == _isDesktopMode && !isFirstRun) { return; // Режим не изменился } _isDesktopMode = shouldBeDesktop; if (_isDesktopMode) { // Десктопный режим - показываем Flyout, скрываем TabBar SwitchToDesktopMode(); } else { // Мобильный режим - показываем TabBar, скрываем Flyout SwitchToMobileMode(); } } catch (Exception ex) { Trace.WriteLine($"[AppShell] ⚠️ Ошибка UpdateLayoutMode: {ex.Message}"); } } private void SwitchToDesktopMode() { try { Trace.WriteLine("[AppShell] >>> Переключение на десктопный режим (DesktopLayout)"); // Скрываем TabBar MobileTabBar.IsVisible = false; // Скрываем FlyoutItems (не используем Flyout) DesktopFlyoutNews.IsVisible = false; DesktopFlyoutEmployees.IsVisible = false; DesktopFlyoutDepartments.IsVisible = false; DesktopFlyoutChats.IsVisible = false; DesktopFlyoutRequests.IsVisible = false; DesktopFlyoutDocuments.IsVisible = false; DesktopFlyoutProfile.IsVisible = false; // Отключаем Flyout FlyoutBehavior = FlyoutBehavior.Disabled; // Переключаем MainPage на DesktopMainPage if (Application.Current?.Windows.Count > 0) { Application.Current.Windows[0].Page = new DesktopMainPage(); } Trace.WriteLine("[AppShell] ✅ Десктопный режим активирован"); } catch (Exception ex) { Trace.WriteLine($"[AppShell] ❌ Ошибка SwitchToDesktopMode: {ex.Message}"); } } private void SwitchToMobileMode() { try { Trace.WriteLine("[AppShell] >>> Переключение на мобильный режим (TabBar)"); // Если текущая страница - DesktopMainPage, возвращаем AppShell if (Application.Current?.Windows.Count > 0 && Application.Current.Windows[0].Page is DesktopMainPage) { Application.Current.Windows[0].Page = this; } // Скрываем FlyoutItems DesktopFlyoutNews.IsVisible = false; DesktopFlyoutEmployees.IsVisible = false; DesktopFlyoutDepartments.IsVisible = false; DesktopFlyoutChats.IsVisible = false; DesktopFlyoutRequests.IsVisible = false; DesktopFlyoutDocuments.IsVisible = false; DesktopFlyoutProfile.IsVisible = false; // Отключаем Flyout FlyoutBehavior = FlyoutBehavior.Disabled; FlyoutIsPresented = false; // Показываем TabBar MobileTabBar.IsVisible = true; // Переходим на TabBar CurrentItem = MobileTabBar; Trace.WriteLine("[AppShell] ✅ Мобильный режим активирован"); } catch (Exception ex) { Trace.WriteLine($"[AppShell] ❌ Ошибка SwitchToMobileMode: {ex.Message}"); } } }