312 lines
13 KiB
C#
312 lines
13 KiB
C#
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");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Сохраняет индекс текущей вкладки перед переходом на меню
|
||
/// </summary>
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Обновляет режим макета в зависимости от ширины экрана
|
||
/// На широких экранах: Flyout (вертикальное меню слева)
|
||
/// На узких экранах: TabBar (нижняя панель)
|
||
/// </summary>
|
||
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}");
|
||
}
|
||
}
|
||
}
|