From caae751698a4e31b2b7f4f08b1053c0fe6b944cc Mon Sep 17 00:00:00 2001 From: Aleh Khantsevich Date: Sun, 16 Feb 2025 14:38:53 +0100 Subject: [PATCH] Show "You" for active account in mail rendering page (#566) * Added account contact view model to handle "You" case. * fix namespaces again --- .../Entities/Shared/AccountContact.cs | 13 +- .../Translations/en_US/resources.json | 1 + Wino.Core.UWP/WinoApplication.cs | 417 +++++++++--------- Wino.Core.ViewModels/AboutPageViewModel.cs | 193 ++++---- .../Data/AccountContactViewModel.cs | 37 ++ .../MailRenderingPageViewModel.cs | 37 +- Wino.Mail/Views/MailRenderingPage.xaml | 4 +- Wino.Services/ContactService.cs | 2 +- Wino.Services/ServicesContainerSetup.cs | 45 +- 9 files changed, 387 insertions(+), 362 deletions(-) create mode 100644 Wino.Mail.ViewModels/Data/AccountContactViewModel.cs diff --git a/Wino.Core.Domain/Entities/Shared/AccountContact.cs b/Wino.Core.Domain/Entities/Shared/AccountContact.cs index 9b2eae37..49949665 100644 --- a/Wino.Core.Domain/Entities/Shared/AccountContact.cs +++ b/Wino.Core.Domain/Entities/Shared/AccountContact.cs @@ -36,14 +36,6 @@ public class AccountContact : IEquatable /// public bool IsRootContact { get; set; } - /// - /// Short display name of the contact. - /// Eather Name or Address. - /// - public string ShortDisplayName => Address == Name || string.IsNullOrWhiteSpace(Name) ? $"{Address.ToLowerInvariant()};" : $"{Name};"; - - public string DisplayName => Address == Name || string.IsNullOrWhiteSpace(Name) ? Address.ToLowerInvariant() : $"{Name} <{Address.ToLowerInvariant()}>"; - public override bool Equals(object obj) { return Equals(obj as AccountContact); @@ -58,10 +50,7 @@ public class AccountContact : IEquatable public override int GetHashCode() { - int hashCode = -1717786383; - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Address); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Name); - return hashCode; + return HashCode.Combine(Address, Name); } public static bool operator ==(AccountContact left, AccountContact right) diff --git a/Wino.Core.Domain/Translations/en_US/resources.json b/Wino.Core.Domain/Translations/en_US/resources.json index e46ac92d..d7902599 100644 --- a/Wino.Core.Domain/Translations/en_US/resources.json +++ b/Wino.Core.Domain/Translations/en_US/resources.json @@ -15,6 +15,7 @@ "AccountSettingsDialog_AccountName": "Sender Display Name", "AccountSettingsDialog_AccountNamePlaceholder": "eg. John Doe", "AddHyperlink": "Add", + "AccountContactNameYou": "You", "AutoDiscoveryProgressMessage": "Searching for mail settings...", "AppCloseBackgroundSynchronizationWarningTitle": "Background Synchronization", "AppCloseTerminateBehaviorWarningMessageFirstLine": "You are terminating Wino Mail and your app close behavior is set to 'Terminate'.", diff --git a/Wino.Core.UWP/WinoApplication.cs b/Wino.Core.UWP/WinoApplication.cs index 5d47e059..bd99d917 100644 --- a/Wino.Core.UWP/WinoApplication.cs +++ b/Wino.Core.UWP/WinoApplication.cs @@ -22,228 +22,227 @@ using Wino.Core.Domain; using Wino.Core.Domain.Interfaces; using Wino.Services; -namespace Wino.Core.UWP +namespace Wino.Core.UWP; + +public abstract class WinoApplication : Application { - public abstract class WinoApplication : Application + public new static WinoApplication Current => (WinoApplication)Application.Current; + public const string WinoLaunchLogPrefix = "[Wino Launch] "; + + public IServiceProvider Services { get; } + protected IWinoLogger LogInitializer { get; } + protected IApplicationConfiguration AppConfiguration { get; } + protected IWinoServerConnectionManager AppServiceConnectionManager { get; } + protected IThemeService ThemeService { get; } + protected IDatabaseService DatabaseService { get; } + protected ITranslationService TranslationService { get; } + + protected WinoApplication() { - public new static WinoApplication Current => (WinoApplication)Application.Current; - public const string WinoLaunchLogPrefix = "[Wino Launch] "; + ConfigurePrelaunch(); - public IServiceProvider Services { get; } - protected IWinoLogger LogInitializer { get; } - protected IApplicationConfiguration AppConfiguration { get; } - protected IWinoServerConnectionManager AppServiceConnectionManager { get; } - protected IThemeService ThemeService { get; } - protected IDatabaseService DatabaseService { get; } - protected ITranslationService TranslationService { get; } + Services = ConfigureServices(); - protected WinoApplication() + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + TaskScheduler.UnobservedTaskException += OnUnobservedTaskException; + UnhandledException += OnAppUnhandledException; + + Resuming += OnResuming; + Suspending += OnSuspending; + + LogInitializer = Services.GetService(); + AppConfiguration = Services.GetService(); + + AppServiceConnectionManager = Services.GetService>(); + ThemeService = Services.GetService(); + DatabaseService = Services.GetService(); + TranslationService = Services.GetService(); + + // Make sure the paths are setup on app start. + AppConfiguration.ApplicationDataFolderPath = ApplicationData.Current.LocalFolder.Path; + AppConfiguration.PublisherSharedFolderPath = ApplicationData.Current.GetPublisherCacheFolder(ApplicationConfiguration.SharedFolderName).Path; + AppConfiguration.ApplicationTempFolderPath = ApplicationData.Current.TemporaryFolder.Path; + + ConfigureLogging(); + } + + private void CurrentDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e) + => Log.Fatal(e.ExceptionObject as Exception, "AppDomain Unhandled Exception"); + + private void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) + => Log.Error(e.Exception, "Unobserved Task Exception"); + + private void OnAppUnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e) + { + Log.Fatal(e.Exception, "Unhandled Exception"); + e.Handled = true; + } + + protected abstract void OnApplicationCloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e); + protected abstract IEnumerable GetActivationHandlers(); + protected abstract ActivationHandler GetDefaultActivationHandler(); + protected override void OnWindowCreated(WindowCreatedEventArgs args) + { + base.OnWindowCreated(args); + + ConfigureTitleBar(); + + LogActivation($"OnWindowCreated -> IsWindowNull: {args.Window == null}"); + + TryRegisterAppCloseChange(); + } + + public IEnumerable GetActivationServices() + { + yield return DatabaseService; + yield return TranslationService; + yield return ThemeService; + } + + public Task InitializeServicesAsync() => GetActivationServices().Select(a => a.InitializeAsync()).WhenAll(); + + public bool IsInteractiveLaunchArgs(object args) => args is IActivatedEventArgs; + + public void LogActivation(string log) => Log.Information($"{WinoLaunchLogPrefix}{log}"); + + private void ConfigureTitleBar() + { + var coreTitleBar = CoreApplication.GetCurrentView().TitleBar; + var applicationViewTitleBar = ApplicationView.GetForCurrentView().TitleBar; + + // Extend shell content into core window to meet design requirements. + coreTitleBar.ExtendViewIntoTitleBar = true; + + // Change system buttons and background colors to meet design requirements. + applicationViewTitleBar.ButtonBackgroundColor = Colors.Transparent; + applicationViewTitleBar.BackgroundColor = Colors.Transparent; + applicationViewTitleBar.ButtonInactiveBackgroundColor = Colors.Transparent; + applicationViewTitleBar.ButtonForegroundColor = Colors.White; + } + + public async Task ActivateWinoAsync(object args) + { + await InitializeServicesAsync(); + + if (IsInteractiveLaunchArgs(args)) { - ConfigurePrelaunch(); - - Services = ConfigureServices(); - - AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; - TaskScheduler.UnobservedTaskException += OnUnobservedTaskException; - UnhandledException += OnAppUnhandledException; - - Resuming += OnResuming; - Suspending += OnSuspending; - - LogInitializer = Services.GetService(); - AppConfiguration = Services.GetService(); - - AppServiceConnectionManager = Services.GetService>(); - ThemeService = Services.GetService(); - DatabaseService = Services.GetService(); - TranslationService = Services.GetService(); - - // Make sure the paths are setup on app start. - AppConfiguration.ApplicationDataFolderPath = ApplicationData.Current.LocalFolder.Path; - AppConfiguration.PublisherSharedFolderPath = ApplicationData.Current.GetPublisherCacheFolder(ApplicationConfiguration.SharedFolderName).Path; - AppConfiguration.ApplicationTempFolderPath = ApplicationData.Current.TemporaryFolder.Path; - - ConfigureLogging(); - } - - private void CurrentDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e) - => Log.Fatal(e.ExceptionObject as Exception, "AppDomain Unhandled Exception"); - - private void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) - => Log.Error(e.Exception, "Unobserved Task Exception"); - - private void OnAppUnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e) - { - Log.Fatal(e.Exception, "Unhandled Exception"); - e.Handled = true; - } - - protected abstract void OnApplicationCloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e); - protected abstract IEnumerable GetActivationHandlers(); - protected abstract ActivationHandler GetDefaultActivationHandler(); - protected override void OnWindowCreated(WindowCreatedEventArgs args) - { - base.OnWindowCreated(args); - - ConfigureTitleBar(); - - LogActivation($"OnWindowCreated -> IsWindowNull: {args.Window == null}"); - - TryRegisterAppCloseChange(); - } - - public IEnumerable GetActivationServices() - { - yield return DatabaseService; - yield return TranslationService; - yield return ThemeService; - } - - public Task InitializeServicesAsync() => GetActivationServices().Select(a => a.InitializeAsync()).WhenAll(); - - public bool IsInteractiveLaunchArgs(object args) => args is IActivatedEventArgs; - - public void LogActivation(string log) => Log.Information($"{WinoLaunchLogPrefix}{log}"); - - private void ConfigureTitleBar() - { - var coreTitleBar = CoreApplication.GetCurrentView().TitleBar; - var applicationViewTitleBar = ApplicationView.GetForCurrentView().TitleBar; - - // Extend shell content into core window to meet design requirements. - coreTitleBar.ExtendViewIntoTitleBar = true; - - // Change system buttons and background colors to meet design requirements. - applicationViewTitleBar.ButtonBackgroundColor = Colors.Transparent; - applicationViewTitleBar.BackgroundColor = Colors.Transparent; - applicationViewTitleBar.ButtonInactiveBackgroundColor = Colors.Transparent; - applicationViewTitleBar.ButtonForegroundColor = Colors.White; - } - - public async Task ActivateWinoAsync(object args) - { - await InitializeServicesAsync(); - - if (IsInteractiveLaunchArgs(args)) + if (Window.Current.Content == null) { - if (Window.Current.Content == null) - { - var mainFrame = new Frame(); + var mainFrame = new Frame(); - Window.Current.Content = mainFrame; + Window.Current.Content = mainFrame; - await ThemeService.InitializeAsync(); - } - } - - await HandleActivationAsync(args); - - if (IsInteractiveLaunchArgs(args)) - { - Window.Current.Activate(); - - LogActivation("Window activated"); + await ThemeService.InitializeAsync(); } } - public async Task HandleActivationAsync(object activationArgs) + await HandleActivationAsync(args); + + if (IsInteractiveLaunchArgs(args)) { - if (GetActivationHandlers() != null) - { - var activationHandler = GetActivationHandlers().FirstOrDefault(h => h.CanHandle(activationArgs)) ?? null; + Window.Current.Activate(); - if (activationHandler != null) - { - await activationHandler.HandleAsync(activationArgs); - } - } - - if (IsInteractiveLaunchArgs(activationArgs)) - { - var defaultHandler = GetDefaultActivationHandler(); - - if (defaultHandler.CanHandle(activationArgs)) - { - await defaultHandler.HandleAsync(activationArgs); - } - } - } - - protected override async void OnLaunched(LaunchActivatedEventArgs args) - { - LogActivation($"OnLaunched -> {args.GetType().Name}, Kind -> {args.Kind}, PreviousExecutionState -> {args.PreviousExecutionState}, IsPrelaunch -> {args.PrelaunchActivated}"); - - if (!args.PrelaunchActivated) - { - await ActivateWinoAsync(args); - } - } - - protected override async void OnFileActivated(FileActivatedEventArgs args) - { - base.OnFileActivated(args); - - LogActivation($"OnFileActivated -> ItemCount: {args.Files.Count}, Kind: {args.Kind}, PreviousExecutionState: {args.PreviousExecutionState}"); - - await ActivateWinoAsync(args); - } - - protected override async void OnActivated(IActivatedEventArgs args) - { - base.OnActivated(args); - - Log.Information($"OnActivated -> {args.GetType().Name}, Kind -> {args.Kind}, Prev Execution State -> {args.PreviousExecutionState}"); - - await ActivateWinoAsync(args); - } - - private void TryRegisterAppCloseChange() - { - try - { - var systemNavigationManagerPreview = SystemNavigationManagerPreview.GetForCurrentView(); - - systemNavigationManagerPreview.CloseRequested -= OnApplicationCloseRequested; - systemNavigationManagerPreview.CloseRequested += OnApplicationCloseRequested; - } - catch { } - } - - private void ConfigurePrelaunch() - { - if (ApiInformation.IsMethodPresent("Windows.ApplicationModel.Core.CoreApplication", "EnablePrelaunch")) - CoreApplication.EnablePrelaunch(true); - } - - - - public virtual async void OnResuming(object sender, object e) - { - // App Service connection was lost on suspension. - // We must restore it. - // Server might be running already, but re-launching it will trigger a new connection attempt. - - try - { - await AppServiceConnectionManager.ConnectAsync(); - } - catch (OperationCanceledException) - { - // Ignore - } - catch (Exception ex) - { - Log.Error(ex, "Failed to connect to server after resuming the app."); - } - } - public virtual void OnSuspending(object sender, SuspendingEventArgs e) { } - - public abstract IServiceProvider ConfigureServices(); - - public void ConfigureLogging() - { - string logFilePath = Path.Combine(ApplicationData.Current.LocalFolder.Path, Constants.ClientLogFile); - LogInitializer.SetupLogger(logFilePath); + LogActivation("Window activated"); } } + + public async Task HandleActivationAsync(object activationArgs) + { + if (GetActivationHandlers() != null) + { + var activationHandler = GetActivationHandlers().FirstOrDefault(h => h.CanHandle(activationArgs)) ?? null; + + if (activationHandler != null) + { + await activationHandler.HandleAsync(activationArgs); + } + } + + if (IsInteractiveLaunchArgs(activationArgs)) + { + var defaultHandler = GetDefaultActivationHandler(); + + if (defaultHandler.CanHandle(activationArgs)) + { + await defaultHandler.HandleAsync(activationArgs); + } + } + } + + protected override async void OnLaunched(LaunchActivatedEventArgs args) + { + LogActivation($"OnLaunched -> {args.GetType().Name}, Kind -> {args.Kind}, PreviousExecutionState -> {args.PreviousExecutionState}, IsPrelaunch -> {args.PrelaunchActivated}"); + + if (!args.PrelaunchActivated) + { + await ActivateWinoAsync(args); + } + } + + protected override async void OnFileActivated(FileActivatedEventArgs args) + { + base.OnFileActivated(args); + + LogActivation($"OnFileActivated -> ItemCount: {args.Files.Count}, Kind: {args.Kind}, PreviousExecutionState: {args.PreviousExecutionState}"); + + await ActivateWinoAsync(args); + } + + protected override async void OnActivated(IActivatedEventArgs args) + { + base.OnActivated(args); + + Log.Information($"OnActivated -> {args.GetType().Name}, Kind -> {args.Kind}, Prev Execution State -> {args.PreviousExecutionState}"); + + await ActivateWinoAsync(args); + } + + private void TryRegisterAppCloseChange() + { + try + { + var systemNavigationManagerPreview = SystemNavigationManagerPreview.GetForCurrentView(); + + systemNavigationManagerPreview.CloseRequested -= OnApplicationCloseRequested; + systemNavigationManagerPreview.CloseRequested += OnApplicationCloseRequested; + } + catch { } + } + + private void ConfigurePrelaunch() + { + if (ApiInformation.IsMethodPresent("Windows.ApplicationModel.Core.CoreApplication", "EnablePrelaunch")) + CoreApplication.EnablePrelaunch(true); + } + + + + public virtual async void OnResuming(object sender, object e) + { + // App Service connection was lost on suspension. + // We must restore it. + // Server might be running already, but re-launching it will trigger a new connection attempt. + + try + { + await AppServiceConnectionManager.ConnectAsync(); + } + catch (OperationCanceledException) + { + // Ignore + } + catch (Exception ex) + { + Log.Error(ex, "Failed to connect to server after resuming the app."); + } + } + public virtual void OnSuspending(object sender, SuspendingEventArgs e) { } + + public abstract IServiceProvider ConfigureServices(); + + public void ConfigureLogging() + { + string logFilePath = Path.Combine(ApplicationData.Current.LocalFolder.Path, Constants.ClientLogFile); + LogInitializer.SetupLogger(logFilePath); + } } diff --git a/Wino.Core.ViewModels/AboutPageViewModel.cs b/Wino.Core.ViewModels/AboutPageViewModel.cs index 123025cd..6e59210a 100644 --- a/Wino.Core.ViewModels/AboutPageViewModel.cs +++ b/Wino.Core.ViewModels/AboutPageViewModel.cs @@ -6,125 +6,124 @@ using Wino.Core.Domain; using Wino.Core.Domain.Enums; using Wino.Core.Domain.Interfaces; -namespace Wino.Core.ViewModels +namespace Wino.Core.ViewModels; + +public partial class AboutPageViewModel : CoreBaseViewModel { - public partial class AboutPageViewModel : CoreBaseViewModel + private readonly IStoreRatingService _storeRatingService; + private readonly IMailDialogService _dialogService; + private readonly INativeAppService _nativeAppService; + private readonly IApplicationConfiguration _appInitializerService; + private readonly IClipboardService _clipboardService; + private readonly IFileService _fileService; + private readonly IWinoLogger _logInitializer; + + public string VersionName => _nativeAppService.GetFullAppVersion(); + public string DiscordChannelUrl => "https://discord.gg/windows-apps-hub-714581497222398064"; + public string GitHubUrl => "https://github.com/bkaankose/Wino-Mail/"; + public string PrivacyPolicyUrl => "https://www.winomail.app/support/privacy"; + public string PaypalUrl => "https://paypal.me/bkaankose?country.x=PL&locale.x=en_US"; + + public IPreferencesService PreferencesService { get; } + + public AboutPageViewModel(IStoreRatingService storeRatingService, + IMailDialogService dialogService, + INativeAppService nativeAppService, + IPreferencesService preferencesService, + IApplicationConfiguration appInitializerService, + IClipboardService clipboardService, + IFileService fileService, + IWinoLogger logInitializer) { - private readonly IStoreRatingService _storeRatingService; - private readonly IMailDialogService _dialogService; - private readonly INativeAppService _nativeAppService; - private readonly IApplicationConfiguration _appInitializerService; - private readonly IClipboardService _clipboardService; - private readonly IFileService _fileService; - private readonly IWinoLogger _logInitializer; + _storeRatingService = storeRatingService; + _dialogService = dialogService; + _nativeAppService = nativeAppService; + _logInitializer = logInitializer; + _appInitializerService = appInitializerService; + _clipboardService = clipboardService; + _fileService = fileService; - public string VersionName => _nativeAppService.GetFullAppVersion(); - public string DiscordChannelUrl => "https://discord.gg/windows-apps-hub-714581497222398064"; - public string GitHubUrl => "https://github.com/bkaankose/Wino-Mail/"; - public string PrivacyPolicyUrl => "https://www.winomail.app/support/privacy"; - public string PaypalUrl => "https://paypal.me/bkaankose?country.x=PL&locale.x=en_US"; + PreferencesService = preferencesService; + } - public IPreferencesService PreferencesService { get; } + protected override void OnActivated() + { + base.OnActivated(); - public AboutPageViewModel(IStoreRatingService storeRatingService, - IMailDialogService dialogService, - INativeAppService nativeAppService, - IPreferencesService preferencesService, - IApplicationConfiguration appInitializerService, - IClipboardService clipboardService, - IFileService fileService, - IWinoLogger logInitializer) + PreferencesService.PreferenceChanged -= PreferencesChanged; + PreferencesService.PreferenceChanged += PreferencesChanged; + } + + protected override void OnDeactivated() + { + base.OnDeactivated(); + + PreferencesService.PreferenceChanged -= PreferencesChanged; + } + + private void PreferencesChanged(object sender, string e) + { + if (e == nameof(PreferencesService.IsLoggingEnabled)) { - _storeRatingService = storeRatingService; - _dialogService = dialogService; - _nativeAppService = nativeAppService; - _logInitializer = logInitializer; - _appInitializerService = appInitializerService; - _clipboardService = clipboardService; - _fileService = fileService; - - PreferencesService = preferencesService; + _logInitializer.RefreshLoggingLevel(); } + } - protected override void OnActivated() + [RelayCommand] + private async Task CopyDiagnosticId() + { + try { - base.OnActivated(); - - PreferencesService.PreferenceChanged -= PreferencesChanged; - PreferencesService.PreferenceChanged += PreferencesChanged; + await _clipboardService.CopyClipboardAsync(PreferencesService.DiagnosticId); + _dialogService.InfoBarMessage(Translator.Buttons_Copy, string.Format(Translator.ClipboardTextCopied_Message, "Id"), InfoBarMessageType.Success); } - - protected override void OnDeactivated() + catch (Exception ex) { - base.OnDeactivated(); - - PreferencesService.PreferenceChanged -= PreferencesChanged; + _dialogService.InfoBarMessage(Translator.GeneralTitle_Error, string.Format(Translator.ClipboardTextCopyFailed_Message, "Id"), InfoBarMessageType.Error); + Log.Error(ex, "Failed to copy diagnostic id to clipboard."); } + } - private void PreferencesChanged(object sender, string e) + [RelayCommand] + private async Task ShareWinoLogAsync() + { + var appDataFolder = _appInitializerService.ApplicationDataFolderPath; + + var selectedFolderPath = await _dialogService.PickWindowsFolderAsync(); + + if (string.IsNullOrEmpty(selectedFolderPath)) return; + + var areLogsSaved = await _fileService.SaveLogsToFolderAsync(appDataFolder, selectedFolderPath).ConfigureAwait(false); + + if (areLogsSaved) { - if (e == nameof(PreferencesService.IsLoggingEnabled)) - { - _logInitializer.RefreshLoggingLevel(); - } + _dialogService.InfoBarMessage(Translator.Info_LogsSavedTitle, string.Format(Translator.Info_LogsSavedMessage, Constants.LogArchiveFileName), InfoBarMessageType.Success); } - - [RelayCommand] - private async Task CopyDiagnosticId() + else { - try - { - await _clipboardService.CopyClipboardAsync(PreferencesService.DiagnosticId); - _dialogService.InfoBarMessage(Translator.Buttons_Copy, string.Format(Translator.ClipboardTextCopied_Message, "Id"), InfoBarMessageType.Success); - } - catch (Exception ex) - { - _dialogService.InfoBarMessage(Translator.GeneralTitle_Error, string.Format(Translator.ClipboardTextCopyFailed_Message, "Id"), InfoBarMessageType.Error); - Log.Error(ex, "Failed to copy diagnostic id to clipboard."); - } + _dialogService.InfoBarMessage(Translator.Info_LogsNotFoundTitle, Translator.Info_LogsNotFoundMessage, InfoBarMessageType.Error); } + } - [RelayCommand] - private async Task ShareWinoLogAsync() + [RelayCommand] + private async Task Navigate(object url) + { + if (url is string stringUrl) { - var appDataFolder = _appInitializerService.ApplicationDataFolderPath; - - var selectedFolderPath = await _dialogService.PickWindowsFolderAsync(); - - if (string.IsNullOrEmpty(selectedFolderPath)) return; - - var areLogsSaved = await _fileService.SaveLogsToFolderAsync(appDataFolder, selectedFolderPath).ConfigureAwait(false); - - if (areLogsSaved) - { - _dialogService.InfoBarMessage(Translator.Info_LogsSavedTitle, string.Format(Translator.Info_LogsSavedMessage, Constants.LogArchiveFileName), InfoBarMessageType.Success); - } + if (stringUrl == "Store") + await ShowRateDialogAsync(); else { - _dialogService.InfoBarMessage(Translator.Info_LogsNotFoundTitle, Translator.Info_LogsNotFoundMessage, InfoBarMessageType.Error); + // Discord disclaimer message about server. + if (stringUrl == DiscordChannelUrl) + await _dialogService.ShowMessageAsync(Translator.DiscordChannelDisclaimerMessage, + Translator.DiscordChannelDisclaimerTitle, + WinoCustomMessageDialogIcon.Warning); + + await _nativeAppService.LaunchUriAsync(new Uri(stringUrl)); } } - - [RelayCommand] - private async Task Navigate(object url) - { - if (url is string stringUrl) - { - if (stringUrl == "Store") - await ShowRateDialogAsync(); - else - { - // Discord disclaimer message about server. - if (stringUrl == DiscordChannelUrl) - await _dialogService.ShowMessageAsync(Translator.DiscordChannelDisclaimerMessage, - Translator.DiscordChannelDisclaimerTitle, - WinoCustomMessageDialogIcon.Warning); - - await _nativeAppService.LaunchUriAsync(new Uri(stringUrl)); - } - } - } - - private Task ShowRateDialogAsync() => _storeRatingService.LaunchStorePageForReviewAsync(); } + + private Task ShowRateDialogAsync() => _storeRatingService.LaunchStorePageForReviewAsync(); } diff --git a/Wino.Mail.ViewModels/Data/AccountContactViewModel.cs b/Wino.Mail.ViewModels/Data/AccountContactViewModel.cs new file mode 100644 index 00000000..27624955 --- /dev/null +++ b/Wino.Mail.ViewModels/Data/AccountContactViewModel.cs @@ -0,0 +1,37 @@ +using Wino.Core.Domain; +using Wino.Core.Domain.Entities.Shared; + +namespace Wino.Mail.ViewModels.Data; + +public class AccountContactViewModel: AccountContact +{ + public AccountContactViewModel(AccountContact contact) + { + Address = contact.Address; + Name = contact.Name; + Base64ContactPicture = contact.Base64ContactPicture; + IsRootContact = contact.IsRootContact; + } + + /// + /// Gets or sets whether the contact is the current account. + /// + public bool IsMe { get; set; } + + /// + /// Provides a short name of the contact. + /// or "You" + /// + public string ShortNameOrYou => IsMe ? Translator.AccountContactNameYou : ShortDisplayName; + + /// + /// Short display name of the contact. + /// Either Name or Address. + /// + public string ShortDisplayName => Address == Name || string.IsNullOrWhiteSpace(Name) ? $"{Address.ToLowerInvariant()};" : $"{Name};"; + + /// + /// Display name of the contact in a format: Name
. + ///
+ public string DisplayName => Address == Name || string.IsNullOrWhiteSpace(Name) ? Address.ToLowerInvariant() : $"{Name} <{Address.ToLowerInvariant()}>"; +} diff --git a/Wino.Mail.ViewModels/MailRenderingPageViewModel.cs b/Wino.Mail.ViewModels/MailRenderingPageViewModel.cs index 006a8140..13cbba42 100644 --- a/Wino.Mail.ViewModels/MailRenderingPageViewModel.cs +++ b/Wino.Mail.ViewModels/MailRenderingPageViewModel.cs @@ -90,35 +90,33 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel, [ObservableProperty] [NotifyPropertyChangedFor(nameof(ShouldDisplayDownloadProgress))] - private bool isIndetermineProgress; + public partial bool IsIndetermineProgress { get; set; } [ObservableProperty] [NotifyPropertyChangedFor(nameof(ShouldDisplayDownloadProgress))] - private double currentDownloadPercentage; + public partial double CurrentDownloadPercentage { get; set; } [ObservableProperty] [NotifyPropertyChangedFor(nameof(CanUnsubscribe))] - private MailRenderModel currentRenderModel; + public partial MailRenderModel CurrentRenderModel { get; set; } [ObservableProperty] - private string subject; + public partial string Subject { get; set; } [ObservableProperty] - private string fromAddress; + public partial string FromAddress { get; set; } [ObservableProperty] - private string fromName; + public partial string FromName { get; set; } [ObservableProperty] - private string contactPicture; + public partial string ContactPicture { get; set; } [ObservableProperty] - private DateTime creationDate; - - - public ObservableCollection ToItems { get; set; } = []; - public ObservableCollection CcItems { get; set; } = []; - public ObservableCollection BccItems { get; set; } = []; + public partial DateTime CreationDate { get; set; } + public ObservableCollection ToItems { get; set; } = []; + public ObservableCollection CcItems { get; set; } = []; + public ObservableCollection BccItems { get; set; } = []; public ObservableCollection Attachments { get; set; } = []; public ObservableCollection MenuItems { get; set; } = []; @@ -467,9 +465,9 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel, }); } - private async Task> GetAccountContacts(InternetAddressList internetAddresses) + private async Task> GetAccountContacts(InternetAddressList internetAddresses) { - var accounts = new List(); + List accounts = []; foreach (var item in internetAddresses) { if (item is MailboxAddress mailboxAddress) @@ -477,14 +475,17 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel, var foundContact = await _contactService.GetAddressInformationByAddressAsync(mailboxAddress.Address).ConfigureAwait(false) ?? new AccountContact() { Name = mailboxAddress.Name, Address = mailboxAddress.Address }; + var contactViewModel = new AccountContactViewModel(foundContact); + // Make sure that user account first in the list. - if (foundContact.Address == initializedMailItemViewModel?.AssignedAccount?.Address) + if (string.Equals(contactViewModel.Address, initializedMailItemViewModel?.AssignedAccount?.Address, StringComparison.OrdinalIgnoreCase)) { - accounts.Insert(0, foundContact); + contactViewModel.IsMe = true; + accounts.Insert(0, contactViewModel); } else { - accounts.Add(foundContact); + accounts.Add(contactViewModel); } } else if (item is GroupAddress groupAddress) diff --git a/Wino.Mail/Views/MailRenderingPage.xaml b/Wino.Mail/Views/MailRenderingPage.xaml index 29674759..8b46a7e5 100644 --- a/Wino.Mail/Views/MailRenderingPage.xaml +++ b/Wino.Mail/Views/MailRenderingPage.xaml @@ -21,12 +21,12 @@ mc:Ignorable="d"> - + diff --git a/Wino.Services/ContactService.cs b/Wino.Services/ContactService.cs index d665e184..202bde3b 100644 --- a/Wino.Services/ContactService.cs +++ b/Wino.Services/ContactService.cs @@ -37,7 +37,7 @@ public class ContactService : BaseDatabaseService, IContactService } public Task GetAddressInformationByAddressAsync(string address) - => Connection.Table().Where(a => a.Address == address).FirstOrDefaultAsync(); + => Connection.Table().FirstOrDefaultAsync(a => a.Address == address); public async Task SaveAddressInformationAsync(MimeMessage message) { diff --git a/Wino.Services/ServicesContainerSetup.cs b/Wino.Services/ServicesContainerSetup.cs index 749c3b66..894edc60 100644 --- a/Wino.Services/ServicesContainerSetup.cs +++ b/Wino.Services/ServicesContainerSetup.cs @@ -2,35 +2,34 @@ using Wino.Core.Domain.Interfaces; using Wino.Services.Threading; -namespace Wino.Services +namespace Wino.Services; + +public static class ServicesContainerSetup { - public static class ServicesContainerSetup + public static void RegisterSharedServices(this IServiceCollection services) { - public static void RegisterSharedServices(this IServiceCollection services) - { - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); - services.AddSingleton(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); + services.AddSingleton(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); - } } }