diff --git a/Marechai.App/Presentation/Converters/SidebarConverters.cs b/Marechai.App/Presentation/Converters/SidebarConverters.cs
new file mode 100644
index 00000000..5605f84f
--- /dev/null
+++ b/Marechai.App/Presentation/Converters/SidebarConverters.cs
@@ -0,0 +1,59 @@
+using System;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Data;
+
+namespace Marechai.App.Presentation.Converters;
+
+///
+/// Converts boolean value to collapse/expand arrow icon
+///
+public class CollapseExpandIconConverter : IValueConverter
+{
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ if(value is bool isOpen) return isOpen ? "◄" : "►";
+
+ return "►";
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language) =>
+ throw new NotImplementedException();
+}
+
+///
+/// Converts boolean value to collapse/expand tooltip text
+///
+public class CollapseExpandTooltipConverter : IValueConverter
+{
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ if(value is bool isOpen) return isOpen ? "Collapse Sidebar" : "Expand Sidebar";
+
+ return "Expand Sidebar";
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language) =>
+ throw new NotImplementedException();
+}
+
+///
+/// Converts boolean value to GridLength for sidebar column width
+///
+public class SidebarWidthConverter : IValueConverter
+{
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ if(value is bool isOpen)
+ {
+ // 280 when open, 60 when collapsed (to keep toggle button visible)
+ double width = isOpen ? 280 : 60;
+
+ return new GridLength(width, GridUnitType.Pixel);
+ }
+
+ return new GridLength(280, GridUnitType.Pixel);
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language) =>
+ throw new NotImplementedException();
+}
\ No newline at end of file
diff --git a/Marechai.App/Presentation/MainPage.xaml b/Marechai.App/Presentation/MainPage.xaml
index d0398720..37d70fc0 100644
--- a/Marechai.App/Presentation/MainPage.xaml
+++ b/Marechai.App/Presentation/MainPage.xaml
@@ -4,129 +4,159 @@
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Marechai.App/Presentation/MainPage.xaml.cs b/Marechai.App/Presentation/MainPage.xaml.cs
index 6cf1f66a..41093e85 100644
--- a/Marechai.App/Presentation/MainPage.xaml.cs
+++ b/Marechai.App/Presentation/MainPage.xaml.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
using Windows.Foundation;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
@@ -8,16 +9,94 @@ namespace Marechai.App.Presentation;
public sealed partial class MainPage : Page
{
- private bool _initialNewsLoaded;
+ private bool _initialNewsLoaded;
+ private PropertyChangedEventHandler _sidebarPropertyChangedHandler;
public MainPage()
{
InitializeComponent();
DataContextChanged += MainPage_DataContextChanged;
+ Loaded += MainPage_Loaded;
+ }
+
+ private void MainPage_Loaded(object sender, RoutedEventArgs e)
+ {
+ if(DataContext is not MainViewModel viewModel) return;
+
+ SidebarWrapper.Width = viewModel.IsSidebarOpen ? 280 : 60;
+
+ if(_sidebarPropertyChangedHandler != null) return;
+
+ _sidebarPropertyChangedHandler = (_, propArgs) =>
+ {
+ if(propArgs.PropertyName != nameof(MainViewModel.IsSidebarOpen)) return;
+
+ AnimateSidebarWidth(((MainViewModel)DataContext).IsSidebarOpen);
+ };
+
+ ((INotifyPropertyChanged)viewModel).PropertyChanged += _sidebarPropertyChangedHandler;
+ }
+
+ void AnimateSidebarWidth(bool isOpen)
+ {
+ double start = SidebarColumn.Width.Value;
+ double end = isOpen ? 280 : 60;
+
+ if(Math.Abs(start - end) < 0.1) return;
+
+ // If expanding, show content immediately
+ if(isOpen && DataContext is MainViewModel vm) vm.SidebarContentVisible = true;
+
+ const int durationMs = 250;
+ const int fps = 60;
+ var steps = (int)(durationMs / (1000.0 / fps));
+ var currentStep = 0;
+
+ var timer = new DispatcherTimer
+ {
+ Interval = TimeSpan.FromMilliseconds(1000.0 / fps)
+ };
+
+ timer.Tick += (_, _) =>
+ {
+ currentStep++;
+ double t = (double)currentStep / steps;
+
+ // Ease in-out cubic
+ double eased = t < 0.5 ? 4 * t * t * t : 1 - Math.Pow(-2 * t + 2, 3) / 2;
+ double value = start + (end - start) * eased;
+ SidebarColumn.Width = new GridLength(value, GridUnitType.Pixel);
+ SidebarWrapper.Width = value;
+
+ if(currentStep >= steps)
+ {
+ SidebarColumn.Width = new GridLength(end, GridUnitType.Pixel);
+ SidebarWrapper.Width = end;
+ timer.Stop();
+
+ // After collapse animation completes, hide sidebar content
+ if(!isOpen && DataContext is MainViewModel vm) vm.SidebarContentVisible = false;
+ }
+ };
+
+ timer.Start();
}
private void MainPage_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
{
+ if(args.NewValue is MainViewModel vm && _sidebarPropertyChangedHandler == null)
+ {
+ SidebarWrapper.Width = vm.IsSidebarOpen ? 280 : 60;
+
+ _sidebarPropertyChangedHandler = (_, propArgs) =>
+ {
+ if(propArgs.PropertyName != nameof(MainViewModel.IsSidebarOpen)) return;
+ AnimateSidebarWidth(vm.IsSidebarOpen);
+ };
+
+ ((INotifyPropertyChanged)vm).PropertyChanged += _sidebarPropertyChangedHandler;
+ }
+
if(_initialNewsLoaded) return;
if(args.NewValue is MainViewModel viewModel && viewModel.NewsViewModel is not null)
diff --git a/Marechai.App/Presentation/MainViewModel.cs b/Marechai.App/Presentation/MainViewModel.cs
index 9ee7655b..9614b388 100644
--- a/Marechai.App/Presentation/MainViewModel.cs
+++ b/Marechai.App/Presentation/MainViewModel.cs
@@ -1,3 +1,4 @@
+using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Input;
using Uno.Extensions.Navigation;
@@ -6,30 +7,163 @@ namespace Marechai.App.Presentation;
public partial class MainViewModel : ObservableObject
{
- private readonly INavigator _navigator;
+ private readonly IStringLocalizer _localizer;
+ private readonly INavigator _navigator;
+ [ObservableProperty]
+ private bool isSidebarOpen = true;
+ [ObservableProperty]
+ private Dictionary localizedStrings = new();
+ [ObservableProperty]
+ private string loginLogoutButtonText = "";
[ObservableProperty]
private string? name;
[ObservableProperty]
private NewsViewModel? newsViewModel;
+ [ObservableProperty]
+ private bool sidebarContentVisible = true;
public MainViewModel(IStringLocalizer localizer, IOptions appInfo, INavigator navigator,
NewsViewModel newsViewModel)
{
_navigator = navigator;
+ _localizer = localizer;
NewsViewModel = newsViewModel;
Title = "Marechai";
Title += $" - {localizer["ApplicationName"]}";
- Title += $" - {appInfo?.Value?.Environment}";
- GoToSecond = new AsyncRelayCommand(GoToSecondView);
+ if(appInfo?.Value?.Environment != null) Title += $" - {appInfo.Value.Environment}";
+
+ GoToSecond = new AsyncRelayCommand(GoToSecondView);
+
+ // Initialize localized strings
+ InitializeLocalizedStrings();
+
+ // Initialize commands
+ NavigateToNewsCommand = new AsyncRelayCommand(NavigateToMainAsync);
+ NavigateToBooksCommand = new AsyncRelayCommand(() => NavigateTo("books"));
+ NavigateToCompaniesCommand = new AsyncRelayCommand(() => NavigateTo("companies"));
+ NavigateToComputersCommand = new AsyncRelayCommand(() => NavigateTo("computers"));
+ NavigateToConsolesCommand = new AsyncRelayCommand(() => NavigateTo("consoles"));
+ NavigateToDocumentsCommand = new AsyncRelayCommand(() => NavigateTo("documents"));
+ NavigateToDumpsCommand = new AsyncRelayCommand(() => NavigateTo("dumps"));
+ NavigateToGraphicalProcessingUnitsCommand = new AsyncRelayCommand(() => NavigateTo("gpus"));
+ NavigateToMagazinesCommand = new AsyncRelayCommand(() => NavigateTo("magazines"));
+ NavigateToPeopleCommand = new AsyncRelayCommand(() => NavigateTo("people"));
+ NavigateToProcessorsCommand = new AsyncRelayCommand(() => NavigateTo("processors"));
+ NavigateToSoftwareCommand = new AsyncRelayCommand(() => NavigateTo("software"));
+ NavigateToSoundSynthesizersCommand = new AsyncRelayCommand(() => NavigateTo("soundsynthesizers"));
+ NavigateToSettingsCommand = new AsyncRelayCommand(() => NavigateTo("settings"));
+ LoginLogoutCommand = new RelayCommand(HandleLoginLogout);
+ ToggleSidebarCommand = new RelayCommand(() => IsSidebarOpen = !IsSidebarOpen);
+
+ UpdateLoginLogoutButtonText();
}
public string? Title { get; }
public ICommand GoToSecond { get; }
+ public ICommand NavigateToNewsCommand { get; }
+ public ICommand NavigateToBooksCommand { get; }
+ public ICommand NavigateToCompaniesCommand { get; }
+ public ICommand NavigateToComputersCommand { get; }
+ public ICommand NavigateToConsolesCommand { get; }
+ public ICommand NavigateToDocumentsCommand { get; }
+ public ICommand NavigateToDumpsCommand { get; }
+ public ICommand NavigateToGraphicalProcessingUnitsCommand { get; }
+ public ICommand NavigateToMagazinesCommand { get; }
+ public ICommand NavigateToPeopleCommand { get; }
+ public ICommand NavigateToProcessorsCommand { get; }
+ public ICommand NavigateToSoftwareCommand { get; }
+ public ICommand NavigateToSoundSynthesizersCommand { get; }
+ public ICommand NavigateToSettingsCommand { get; }
+ public ICommand LoginLogoutCommand { get; }
+ public ICommand ToggleSidebarCommand { get; }
+
+ private void InitializeLocalizedStrings()
+ {
+ LocalizedStrings = new Dictionary
+ {
+ {
+ "News", _localizer["News"]
+ },
+ {
+ "Books", _localizer["Books"]
+ },
+ {
+ "Companies", _localizer["Companies"]
+ },
+ {
+ "Computers", _localizer["Computers"]
+ },
+ {
+ "Consoles", _localizer["Consoles"]
+ },
+ {
+ "Documents", _localizer["Documents"]
+ },
+ {
+ "Dumps", _localizer["Dumps"]
+ },
+ {
+ "GraphicalProcessingUnits", _localizer["GraphicalProcessingUnits"]
+ },
+ {
+ "Magazines", _localizer["Magazines"]
+ },
+ {
+ "People", _localizer["People"]
+ },
+ {
+ "Processors", _localizer["Processors"]
+ },
+ {
+ "Software", _localizer["Software"]
+ },
+ {
+ "SoundSynthesizers", _localizer["SoundSynthesizers"]
+ },
+ {
+ "Settings", _localizer["Settings"]
+ },
+ {
+ "Login", _localizer["Login"]
+ },
+ {
+ "Logout", _localizer["Logout"]
+ }
+ };
+ }
+
+ private void UpdateLoginLogoutButtonText()
+ {
+ // TODO: Check if user is logged in
+ // For now, always show "Login"
+ LoginLogoutButtonText = LocalizedStrings["Login"];
+ }
+
+ private static void HandleLoginLogout()
+ {
+ // TODO: Implement login/logout logic
+ }
+
+ private async Task NavigateTo(string destination)
+ {
+ // TODO: Navigate to the specified destination
+ // These routes will need to be registered in App.xaml.cs RegisterRoutes method
+ // For now, placeholder implementation
+ await Task.CompletedTask;
+ }
+
+ private async Task NavigateToMainAsync()
+ {
+ // Stay on main page
+ await Task.CompletedTask;
+ }
+
private async Task GoToSecondView()
{
- await _navigator.NavigateViewModelAsync(this, data: new Entity(Name!));
+ // Navigate to Second view model providing qualifier and data
+ await _navigator.NavigateViewModelAsync(this, "Second", new Entity(Name ?? ""));
}
}
\ No newline at end of file
diff --git a/Marechai.App/Presentation/Sidebar.xaml b/Marechai.App/Presentation/Sidebar.xaml
new file mode 100644
index 00000000..a3dceb7f
--- /dev/null
+++ b/Marechai.App/Presentation/Sidebar.xaml
@@ -0,0 +1,260 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Marechai.App/Presentation/Sidebar.xaml.cs b/Marechai.App/Presentation/Sidebar.xaml.cs
new file mode 100644
index 00000000..ca7bd64f
--- /dev/null
+++ b/Marechai.App/Presentation/Sidebar.xaml.cs
@@ -0,0 +1,11 @@
+using Microsoft.UI.Xaml.Controls;
+
+namespace Marechai.App.Presentation;
+
+public sealed partial class Sidebar : UserControl
+{
+ public Sidebar()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/Marechai.App/Strings/en/Resources.resw b/Marechai.App/Strings/en/Resources.resw
index 222ecd3a..bb951adf 100644
--- a/Marechai.App/Strings/en/Resources.resw
+++ b/Marechai.App/Strings/en/Resources.resw
@@ -1,64 +1,5 @@
-
+
-
@@ -120,4 +61,52 @@
Marechai.App-en
+
+ News
+
+
+ Books
+
+
+ Companies
+
+
+ Computers
+
+
+ Consoles
+
+
+ Documents
+
+
+ Dumps
+
+
+ Graphical Processing Units
+
+
+ Magazines
+
+
+ People
+
+
+ Processors
+
+
+ Software
+
+
+ Sound Synthesizers
+
+
+ Settings
+
+
+ Login
+
+
+ Logout
+
diff --git a/Marechai.App/Strings/es/Resources.resw b/Marechai.App/Strings/es/Resources.resw
index 6eff8b60..16b08eb9 100644
--- a/Marechai.App/Strings/es/Resources.resw
+++ b/Marechai.App/Strings/es/Resources.resw
@@ -1,64 +1,5 @@
-
@@ -120,4 +61,52 @@
Marechai.App-es
+
+ Noticias
+
+
+ Libros
+
+
+ Empresas
+
+
+ Computadoras
+
+
+ Consolas
+
+
+ Documentos
+
+
+ Volcados
+
+
+ Unidades de Procesamiento Gráfico
+
+
+ Revistas
+
+
+ Personas
+
+
+ Procesadores
+
+
+ Software
+
+
+ Sintetizadores de Sonido
+
+
+ Configuración
+
+
+ Iniciar sesión
+
+
+ Cerrar sesión
+
diff --git a/Marechai.App/Strings/fr/Resources.resw b/Marechai.App/Strings/fr/Resources.resw
index d8659858..f448255b 100644
--- a/Marechai.App/Strings/fr/Resources.resw
+++ b/Marechai.App/Strings/fr/Resources.resw
@@ -1,64 +1,5 @@
-
@@ -120,4 +61,52 @@
Marechai.App-fr
+
+ Actualités
+
+
+ Livres
+
+
+ Entreprises
+
+
+ Ordinateurs
+
+
+ Consoles
+
+
+ Documents
+
+
+ Vidages
+
+
+ Processeurs Graphiques
+
+
+ Magazines
+
+
+ Personnes
+
+
+ Processeurs
+
+
+ Logiciels
+
+
+ Synthétiseurs de Son
+
+
+ Paramètres
+
+
+ Connexion
+
+
+ Déconnexion
+
diff --git a/Marechai.App/Strings/pt-BR/Resources.resw b/Marechai.App/Strings/pt-BR/Resources.resw
index 994e5cd8..50e4a3e8 100644
--- a/Marechai.App/Strings/pt-BR/Resources.resw
+++ b/Marechai.App/Strings/pt-BR/Resources.resw
@@ -1,64 +1,5 @@
-
@@ -120,4 +61,52 @@
Marechai.App-pt-BR
+
+ Notícias
+
+
+ Livros
+
+
+ Empresas
+
+
+ Computadores
+
+
+ Consoles
+
+
+ Documentos
+
+
+ Dumps
+
+
+ Unidades de Processamento Gráfico
+
+
+ Revistas
+
+
+ Pessoas
+
+
+ Processadores
+
+
+ Software
+
+
+ Sintetizadores de Som
+
+
+ Configurações
+
+
+ Fazer Login
+
+
+ Fazer Logout
+