diff --git a/Directory.Packages.props b/Directory.Packages.props index f3633522..e7548dd6 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -34,4 +34,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Marechai.App/Marechai.App.csproj b/Marechai.App/Marechai.App.csproj index bd744c02..f996c7af 100644 --- a/Marechai.App/Marechai.App.csproj +++ b/Marechai.App/Marechai.App.csproj @@ -80,4 +80,14 @@ + + + + + + + + + + diff --git a/Marechai.App/Presentation/ViewModels/CompanyDetailViewModel.cs b/Marechai.App/Presentation/ViewModels/CompanyDetailViewModel.cs index 9b11b698..9a107a23 100644 --- a/Marechai.App/Presentation/ViewModels/CompanyDetailViewModel.cs +++ b/Marechai.App/Presentation/ViewModels/CompanyDetailViewModel.cs @@ -32,6 +32,9 @@ public partial class CompanyDetailViewModel : ObservableObject [ObservableProperty] private int _companyId; + [ObservableProperty] + private ObservableCollection _companyLogos = []; + [ObservableProperty] private ObservableCollection _computers = []; @@ -106,6 +109,11 @@ public partial class CompanyDetailViewModel : ObservableObject /// public bool HasLogoContent => LogoImageSource != null; + /// + /// Gets whether company has multiple logos + /// + public bool HasMultipleLogos => CompanyLogos.Count > 1; + public IAsyncRelayCommand LoadData { get; } public ICommand GoBackCommand { get; } public IAsyncRelayCommand NavigateToMachineCommand { get; } @@ -130,6 +138,13 @@ public partial class CompanyDetailViewModel : ObservableObject OnPropertyChanged(nameof(HasLogoContent)); } + partial void OnCompanyLogosChanged(ObservableCollection oldValue, + ObservableCollection newValue) + { + // Notify that HasMultipleLogos has changed + OnPropertyChanged(nameof(HasMultipleLogos)); + } + partial void OnComputersFilterTextChanged(string value) { FilterComputers(value); @@ -324,6 +339,7 @@ public partial class CompanyDetailViewModel : ObservableObject IsDataLoaded = false; FlagImageSource = null; LogoImageSource = null; + CompanyLogos.Clear(); if(CompanyId <= 0) { @@ -379,7 +395,7 @@ public partial class CompanyDetailViewModel : ObservableObject { try { - Stream? logoStream = await _logoCache.GetFlagAsync(Company.LastLogo.Value); + Stream? logoStream = await _logoCache.GetLogoAsync(Company.LastLogo.Value); var logoSource = new SvgImageSource(); await logoSource.SetSourceAsync(logoStream.AsRandomAccessStream()); @@ -395,6 +411,58 @@ public partial class CompanyDetailViewModel : ObservableObject } } + // Load all logos for carousel + try + { + // Get all logos for this company + List logosList = await _companyDetailService.GetCompanyLogosAsync(CompanyId); + + // Convert to list with extracted years for sorting + var logosWithYears = logosList.Select(logo => new + { + Logo = logo, + Year = logo.Year != null + ? (int?)UntypedNodeExtractor.ExtractInt(logo.Year) + : null + }) + .OrderBy(l => l.Year) + .ToList(); + + var loadedLogos = new ObservableCollection(); + + foreach(var logoData in logosWithYears) + { + try + { + if(logoData.Logo.Guid == null) continue; + + Stream? logoStream = await _logoCache.GetLogoAsync(logoData.Logo.Guid.Value); + var logoSource = new SvgImageSource(); + await logoSource.SetSourceAsync(logoStream.AsRandomAccessStream()); + + loadedLogos.Add(new CompanyLogoItem + { + LogoGuid = logoData.Logo.Guid.Value, + LogoSource = logoSource, + Year = logoData.Year + }); + } + catch(Exception ex) + { + _logger.LogError("Failed to load carousel logo: {Exception}", ex.Message); + } + } + + // Assign the new collection (this will trigger OnCompanyLogosChanged) + CompanyLogos = loadedLogos; + + _logger.LogInformation("Loaded {Count} logos for company {CompanyId}", CompanyLogos.Count, CompanyId); + } + catch(Exception ex) + { + _logger.LogError("Failed to load company logos for carousel: {Exception}", ex.Message); + } + // Load computers and consoles made by this company List machines = await _companyDetailService.GetComputersByCompanyAsync(CompanyId); Computers.Clear(); @@ -452,4 +520,14 @@ public class CompanyDetailMachine { public int Id { get; set; } public string Name { get; set; } = string.Empty; +} + +/// +/// Data model for a company logo in the carousel +/// +public class CompanyLogoItem +{ + public Guid LogoGuid { get; set; } + public SvgImageSource? LogoSource { get; set; } + public int? Year { get; set; } } \ No newline at end of file diff --git a/Marechai.App/Presentation/Views/CompanyDetailPage.xaml b/Marechai.App/Presentation/Views/CompanyDetailPage.xaml index bd2c9a14..0a5ae832 100644 --- a/Marechai.App/Presentation/Views/CompanyDetailPage.xaml +++ b/Marechai.App/Presentation/Views/CompanyDetailPage.xaml @@ -4,6 +4,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:utu="using:Uno.Toolkit.UI" + xmlns:controls="using:CommunityToolkit.WinUI.UI.Controls" NavigationCacheMode="Required" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> @@ -201,6 +202,49 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Marechai.App/Services/Caching/LogoCache.cs b/Marechai.App/Services/Caching/LogoCache.cs index 500ec06f..12b117a9 100644 --- a/Marechai.App/Services/Caching/LogoCache.cs +++ b/Marechai.App/Services/Caching/LogoCache.cs @@ -10,7 +10,7 @@ namespace Marechai.App.Services.Caching; public sealed class CompanyLogoCache { readonly IConfiguration _configuration; - StorageFolder _flagsFolder; + StorageFolder _logosFolder; public CompanyLogoCache(IConfiguration configuration) { @@ -21,32 +21,32 @@ public sealed class CompanyLogoCache async Task EnsureFolderExistAsync() { StorageFolder localFolder = ApplicationData.Current.LocalCacheFolder; - _flagsFolder = await localFolder.CreateFolderAsync("logos", CreationCollisionOption.OpenIfExists); + _logosFolder = await localFolder.CreateFolderAsync("logos", CreationCollisionOption.OpenIfExists); } - public async Task GetFlagAsync(Guid companyLogoId) + public async Task GetLogoAsync(Guid companyLogoId) { var filename = $"{companyLogoId}.svg"; Stream retStream; - if(await _flagsFolder.TryGetItemAsync(filename) is StorageFile file) + if(await _logosFolder.TryGetItemAsync(filename) is StorageFile file) { retStream = await file.OpenStreamForReadAsync(); return retStream; } - await CacheFlagAsync(companyLogoId); + await CacheLogoAsync(companyLogoId); - file = await _flagsFolder.GetFileAsync(filename); + file = await _logosFolder.GetFileAsync(filename); retStream = await file.OpenStreamForReadAsync(); return retStream; } - async Task CacheFlagAsync(Guid companyLogoId) + async Task CacheLogoAsync(Guid companyLogoId) { var filename = $"{companyLogoId}.svg"; string baseUrl = _configuration.GetSection("ApiClient:Url").Value; @@ -56,7 +56,7 @@ public sealed class CompanyLogoCache response.EnsureSuccessStatusCode(); using Stream stream = await response.Content.ReadAsStreamAsync(); - StorageFile file = await _flagsFolder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting); + StorageFile file = await _logosFolder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting); using Stream fileStream = await file.OpenStreamForWriteAsync(); await stream.CopyToAsync(fileStream); diff --git a/Marechai.App/Services/CompanyDetailService.cs b/Marechai.App/Services/CompanyDetailService.cs index b66b7588..cb4032c1 100644 --- a/Marechai.App/Services/CompanyDetailService.cs +++ b/Marechai.App/Services/CompanyDetailService.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Marechai.App.Models; namespace Marechai.App.Services; @@ -12,7 +11,7 @@ namespace Marechai.App.Services; /// public class CompanyDetailService { - private readonly ApiClient _apiClient; + private readonly ApiClient _apiClient; private readonly ILogger _logger; public CompanyDetailService(ApiClient apiClient, ILogger logger) @@ -64,7 +63,9 @@ public class CompanyDetailService if(machines == null) return []; - _logger.LogInformation("Successfully fetched {Count} computers for company {CompanyId}", machines.Count, companyId); + _logger.LogInformation("Successfully fetched {Count} computers for company {CompanyId}", + machines.Count, + companyId); return machines; } @@ -98,4 +99,31 @@ public class CompanyDetailService return null; } } -} + + /// + /// Gets all logos for a company + /// + public async Task> GetCompanyLogosAsync(int companyId) + { + try + { + _logger.LogInformation("Fetching logos for company {CompanyId}", companyId); + + List? logos = await _apiClient.Companies[companyId].Logos.GetAsync(); + + if(logos == null) return []; + + _logger.LogInformation("Successfully fetched {Count} logos for company {CompanyId}", + logos.Count, + companyId); + + return logos; + } + catch(Exception ex) + { + _logger.LogError(ex, "Error fetching logos for company {CompanyId}", companyId); + + return []; + } + } +} \ No newline at end of file