Show "You" for active account in mail rendering page (#566)

* Added account contact view model to handle "You" case.

* fix namespaces again
This commit is contained in:
Aleh Khantsevich
2025-02-16 14:38:53 +01:00
committed by GitHub
parent f7836eedce
commit caae751698
9 changed files with 387 additions and 362 deletions

View File

@@ -36,14 +36,6 @@ public class AccountContact : IEquatable<AccountContact>
/// </summary> /// </summary>
public bool IsRootContact { get; set; } public bool IsRootContact { get; set; }
/// <summary>
/// Short display name of the contact.
/// Eather Name or Address.
/// </summary>
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) public override bool Equals(object obj)
{ {
return Equals(obj as AccountContact); return Equals(obj as AccountContact);
@@ -58,10 +50,7 @@ public class AccountContact : IEquatable<AccountContact>
public override int GetHashCode() public override int GetHashCode()
{ {
int hashCode = -1717786383; return HashCode.Combine(Address, Name);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Address);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Name);
return hashCode;
} }
public static bool operator ==(AccountContact left, AccountContact right) public static bool operator ==(AccountContact left, AccountContact right)

View File

@@ -15,6 +15,7 @@
"AccountSettingsDialog_AccountName": "Sender Display Name", "AccountSettingsDialog_AccountName": "Sender Display Name",
"AccountSettingsDialog_AccountNamePlaceholder": "eg. John Doe", "AccountSettingsDialog_AccountNamePlaceholder": "eg. John Doe",
"AddHyperlink": "Add", "AddHyperlink": "Add",
"AccountContactNameYou": "You",
"AutoDiscoveryProgressMessage": "Searching for mail settings...", "AutoDiscoveryProgressMessage": "Searching for mail settings...",
"AppCloseBackgroundSynchronizationWarningTitle": "Background Synchronization", "AppCloseBackgroundSynchronizationWarningTitle": "Background Synchronization",
"AppCloseTerminateBehaviorWarningMessageFirstLine": "You are terminating Wino Mail and your app close behavior is set to 'Terminate'.", "AppCloseTerminateBehaviorWarningMessageFirstLine": "You are terminating Wino Mail and your app close behavior is set to 'Terminate'.",

View File

@@ -22,228 +22,227 @@ using Wino.Core.Domain;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Services; 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<AppServiceConnection> AppServiceConnectionManager { get; }
protected IThemeService ThemeService { get; }
protected IDatabaseService DatabaseService { get; }
protected ITranslationService TranslationService { get; }
protected WinoApplication()
{ {
public new static WinoApplication Current => (WinoApplication)Application.Current; ConfigurePrelaunch();
public const string WinoLaunchLogPrefix = "[Wino Launch] ";
public IServiceProvider Services { get; } Services = ConfigureServices();
protected IWinoLogger LogInitializer { get; }
protected IApplicationConfiguration AppConfiguration { get; }
protected IWinoServerConnectionManager<AppServiceConnection> AppServiceConnectionManager { get; }
protected IThemeService ThemeService { get; }
protected IDatabaseService DatabaseService { get; }
protected ITranslationService TranslationService { get; }
protected WinoApplication() AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
UnhandledException += OnAppUnhandledException;
Resuming += OnResuming;
Suspending += OnSuspending;
LogInitializer = Services.GetService<IWinoLogger>();
AppConfiguration = Services.GetService<IApplicationConfiguration>();
AppServiceConnectionManager = Services.GetService<IWinoServerConnectionManager<AppServiceConnection>>();
ThemeService = Services.GetService<IThemeService>();
DatabaseService = Services.GetService<IDatabaseService>();
TranslationService = Services.GetService<ITranslationService>();
// 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<ActivationHandler> GetActivationHandlers();
protected abstract ActivationHandler<IActivatedEventArgs> GetDefaultActivationHandler();
protected override void OnWindowCreated(WindowCreatedEventArgs args)
{
base.OnWindowCreated(args);
ConfigureTitleBar();
LogActivation($"OnWindowCreated -> IsWindowNull: {args.Window == null}");
TryRegisterAppCloseChange();
}
public IEnumerable<IInitializeAsync> 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(); if (Window.Current.Content == null)
Services = ConfigureServices();
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
UnhandledException += OnAppUnhandledException;
Resuming += OnResuming;
Suspending += OnSuspending;
LogInitializer = Services.GetService<IWinoLogger>();
AppConfiguration = Services.GetService<IApplicationConfiguration>();
AppServiceConnectionManager = Services.GetService<IWinoServerConnectionManager<AppServiceConnection>>();
ThemeService = Services.GetService<IThemeService>();
DatabaseService = Services.GetService<IDatabaseService>();
TranslationService = Services.GetService<ITranslationService>();
// 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<ActivationHandler> GetActivationHandlers();
protected abstract ActivationHandler<IActivatedEventArgs> GetDefaultActivationHandler();
protected override void OnWindowCreated(WindowCreatedEventArgs args)
{
base.OnWindowCreated(args);
ConfigureTitleBar();
LogActivation($"OnWindowCreated -> IsWindowNull: {args.Window == null}");
TryRegisterAppCloseChange();
}
public IEnumerable<IInitializeAsync> 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) var mainFrame = new Frame();
{
var mainFrame = new Frame();
Window.Current.Content = mainFrame; Window.Current.Content = mainFrame;
await ThemeService.InitializeAsync(); await ThemeService.InitializeAsync();
}
}
await HandleActivationAsync(args);
if (IsInteractiveLaunchArgs(args))
{
Window.Current.Activate();
LogActivation("Window activated");
} }
} }
public async Task HandleActivationAsync(object activationArgs) await HandleActivationAsync(args);
if (IsInteractiveLaunchArgs(args))
{ {
if (GetActivationHandlers() != null) Window.Current.Activate();
{
var activationHandler = GetActivationHandlers().FirstOrDefault(h => h.CanHandle(activationArgs)) ?? null;
if (activationHandler != null) LogActivation("Window activated");
{
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);
} }
} }
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);
}
} }

View File

@@ -6,125 +6,124 @@ using Wino.Core.Domain;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces; 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; _storeRatingService = storeRatingService;
private readonly IMailDialogService _dialogService; _dialogService = dialogService;
private readonly INativeAppService _nativeAppService; _nativeAppService = nativeAppService;
private readonly IApplicationConfiguration _appInitializerService; _logInitializer = logInitializer;
private readonly IClipboardService _clipboardService; _appInitializerService = appInitializerService;
private readonly IFileService _fileService; _clipboardService = clipboardService;
private readonly IWinoLogger _logInitializer; _fileService = fileService;
public string VersionName => _nativeAppService.GetFullAppVersion(); PreferencesService = preferencesService;
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; } protected override void OnActivated()
{
base.OnActivated();
public AboutPageViewModel(IStoreRatingService storeRatingService, PreferencesService.PreferenceChanged -= PreferencesChanged;
IMailDialogService dialogService, PreferencesService.PreferenceChanged += PreferencesChanged;
INativeAppService nativeAppService, }
IPreferencesService preferencesService,
IApplicationConfiguration appInitializerService, protected override void OnDeactivated()
IClipboardService clipboardService, {
IFileService fileService, base.OnDeactivated();
IWinoLogger logInitializer)
PreferencesService.PreferenceChanged -= PreferencesChanged;
}
private void PreferencesChanged(object sender, string e)
{
if (e == nameof(PreferencesService.IsLoggingEnabled))
{ {
_storeRatingService = storeRatingService; _logInitializer.RefreshLoggingLevel();
_dialogService = dialogService;
_nativeAppService = nativeAppService;
_logInitializer = logInitializer;
_appInitializerService = appInitializerService;
_clipboardService = clipboardService;
_fileService = fileService;
PreferencesService = preferencesService;
} }
}
protected override void OnActivated() [RelayCommand]
private async Task CopyDiagnosticId()
{
try
{ {
base.OnActivated(); await _clipboardService.CopyClipboardAsync(PreferencesService.DiagnosticId);
_dialogService.InfoBarMessage(Translator.Buttons_Copy, string.Format(Translator.ClipboardTextCopied_Message, "Id"), InfoBarMessageType.Success);
PreferencesService.PreferenceChanged -= PreferencesChanged;
PreferencesService.PreferenceChanged += PreferencesChanged;
} }
catch (Exception ex)
protected override void OnDeactivated()
{ {
base.OnDeactivated(); _dialogService.InfoBarMessage(Translator.GeneralTitle_Error, string.Format(Translator.ClipboardTextCopyFailed_Message, "Id"), InfoBarMessageType.Error);
Log.Error(ex, "Failed to copy diagnostic id to clipboard.");
PreferencesService.PreferenceChanged -= PreferencesChanged;
} }
}
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)) _dialogService.InfoBarMessage(Translator.Info_LogsSavedTitle, string.Format(Translator.Info_LogsSavedMessage, Constants.LogArchiveFileName), InfoBarMessageType.Success);
{
_logInitializer.RefreshLoggingLevel();
}
} }
else
[RelayCommand]
private async Task CopyDiagnosticId()
{ {
try _dialogService.InfoBarMessage(Translator.Info_LogsNotFoundTitle, Translator.Info_LogsNotFoundMessage, InfoBarMessageType.Error);
{
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.");
}
} }
}
[RelayCommand] [RelayCommand]
private async Task ShareWinoLogAsync() private async Task Navigate(object url)
{
if (url is string stringUrl)
{ {
var appDataFolder = _appInitializerService.ApplicationDataFolderPath; if (stringUrl == "Store")
await ShowRateDialogAsync();
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);
}
else 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();
} }

View File

@@ -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;
}
/// <summary>
/// Gets or sets whether the contact is the current account.
/// </summary>
public bool IsMe { get; set; }
/// <summary>
/// Provides a short name of the contact.
/// <see cref="ShortDisplayName"/> or "You"
/// </summary>
public string ShortNameOrYou => IsMe ? Translator.AccountContactNameYou : ShortDisplayName;
/// <summary>
/// Short display name of the contact.
/// Either Name or Address.
/// </summary>
public string ShortDisplayName => Address == Name || string.IsNullOrWhiteSpace(Name) ? $"{Address.ToLowerInvariant()};" : $"{Name};";
/// <summary>
/// Display name of the contact in a format: Name <Address>.
/// </summary>
public string DisplayName => Address == Name || string.IsNullOrWhiteSpace(Name) ? Address.ToLowerInvariant() : $"{Name} <{Address.ToLowerInvariant()}>";
}

View File

@@ -90,35 +90,33 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
[ObservableProperty] [ObservableProperty]
[NotifyPropertyChangedFor(nameof(ShouldDisplayDownloadProgress))] [NotifyPropertyChangedFor(nameof(ShouldDisplayDownloadProgress))]
private bool isIndetermineProgress; public partial bool IsIndetermineProgress { get; set; }
[ObservableProperty] [ObservableProperty]
[NotifyPropertyChangedFor(nameof(ShouldDisplayDownloadProgress))] [NotifyPropertyChangedFor(nameof(ShouldDisplayDownloadProgress))]
private double currentDownloadPercentage; public partial double CurrentDownloadPercentage { get; set; }
[ObservableProperty] [ObservableProperty]
[NotifyPropertyChangedFor(nameof(CanUnsubscribe))] [NotifyPropertyChangedFor(nameof(CanUnsubscribe))]
private MailRenderModel currentRenderModel; public partial MailRenderModel CurrentRenderModel { get; set; }
[ObservableProperty] [ObservableProperty]
private string subject; public partial string Subject { get; set; }
[ObservableProperty] [ObservableProperty]
private string fromAddress; public partial string FromAddress { get; set; }
[ObservableProperty] [ObservableProperty]
private string fromName; public partial string FromName { get; set; }
[ObservableProperty] [ObservableProperty]
private string contactPicture; public partial string ContactPicture { get; set; }
[ObservableProperty] [ObservableProperty]
private DateTime creationDate; public partial DateTime CreationDate { get; set; }
public ObservableCollection<AccountContactViewModel> ToItems { get; set; } = [];
public ObservableCollection<AccountContactViewModel> CcItems { get; set; } = [];
public ObservableCollection<AccountContact> ToItems { get; set; } = []; public ObservableCollection<AccountContactViewModel> BccItems { get; set; } = [];
public ObservableCollection<AccountContact> CcItems { get; set; } = [];
public ObservableCollection<AccountContact> BccItems { get; set; } = [];
public ObservableCollection<MailAttachmentViewModel> Attachments { get; set; } = []; public ObservableCollection<MailAttachmentViewModel> Attachments { get; set; } = [];
public ObservableCollection<MailOperationMenuItem> MenuItems { get; set; } = []; public ObservableCollection<MailOperationMenuItem> MenuItems { get; set; } = [];
@@ -467,9 +465,9 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
}); });
} }
private async Task<List<AccountContact>> GetAccountContacts(InternetAddressList internetAddresses) private async Task<List<AccountContactViewModel>> GetAccountContacts(InternetAddressList internetAddresses)
{ {
var accounts = new List<AccountContact>(); List<AccountContactViewModel> accounts = [];
foreach (var item in internetAddresses) foreach (var item in internetAddresses)
{ {
if (item is MailboxAddress mailboxAddress) if (item is MailboxAddress mailboxAddress)
@@ -477,14 +475,17 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
var foundContact = await _contactService.GetAddressInformationByAddressAsync(mailboxAddress.Address).ConfigureAwait(false) var foundContact = await _contactService.GetAddressInformationByAddressAsync(mailboxAddress.Address).ConfigureAwait(false)
?? new AccountContact() { Name = mailboxAddress.Name, Address = mailboxAddress.Address }; ?? new AccountContact() { Name = mailboxAddress.Name, Address = mailboxAddress.Address };
var contactViewModel = new AccountContactViewModel(foundContact);
// Make sure that user account first in the list. // 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 else
{ {
accounts.Add(foundContact); accounts.Add(contactViewModel);
} }
} }
else if (item is GroupAddress groupAddress) else if (item is GroupAddress groupAddress)

View File

@@ -21,12 +21,12 @@
mc:Ignorable="d"> mc:Ignorable="d">
<Page.Resources> <Page.Resources>
<DataTemplate x:Key="InternetAddressTemplate" x:DataType="entities:AccountContact"> <DataTemplate x:Key="InternetAddressTemplate" x:DataType="viewModelData:AccountContactViewModel">
<HyperlinkButton <HyperlinkButton
Margin="-2,-2" Margin="-2,-2"
Padding="4,2" Padding="4,2"
Click="InternetAddressClicked" Click="InternetAddressClicked"
Content="{x:Bind ShortDisplayName}" Content="{x:Bind ShortNameOrYou}"
ToolTipService.ToolTip="{x:Bind DisplayName}"> ToolTipService.ToolTip="{x:Bind DisplayName}">
<HyperlinkButton.ContextFlyout> <HyperlinkButton.ContextFlyout>
<Flyout Placement="Bottom"> <Flyout Placement="Bottom">

View File

@@ -37,7 +37,7 @@ public class ContactService : BaseDatabaseService, IContactService
} }
public Task<AccountContact> GetAddressInformationByAddressAsync(string address) public Task<AccountContact> GetAddressInformationByAddressAsync(string address)
=> Connection.Table<AccountContact>().Where(a => a.Address == address).FirstOrDefaultAsync(); => Connection.Table<AccountContact>().FirstOrDefaultAsync(a => a.Address == address);
public async Task SaveAddressInformationAsync(MimeMessage message) public async Task SaveAddressInformationAsync(MimeMessage message)
{ {

View File

@@ -2,35 +2,34 @@
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Services.Threading; 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<ITranslationService, TranslationService>();
{ services.AddSingleton<IDatabaseService, DatabaseService>();
services.AddSingleton<ITranslationService, TranslationService>();
services.AddSingleton<IDatabaseService, DatabaseService>();
services.AddSingleton<IApplicationConfiguration, ApplicationConfiguration>(); services.AddSingleton<IApplicationConfiguration, ApplicationConfiguration>();
services.AddSingleton<IWinoLogger, WinoLogger>(); services.AddSingleton<IWinoLogger, WinoLogger>();
services.AddSingleton<ILaunchProtocolService, LaunchProtocolService>(); services.AddSingleton<ILaunchProtocolService, LaunchProtocolService>();
services.AddSingleton<IMimeFileService, MimeFileService>(); services.AddSingleton<IMimeFileService, MimeFileService>();
services.AddTransient<ICalendarService, CalendarService>(); services.AddTransient<ICalendarService, CalendarService>();
services.AddTransient<IMailService, MailService>(); services.AddTransient<IMailService, MailService>();
services.AddTransient<IFolderService, FolderService>(); services.AddTransient<IFolderService, FolderService>();
services.AddTransient<IAccountService, AccountService>(); services.AddTransient<IAccountService, AccountService>();
services.AddTransient<IContactService, ContactService>(); services.AddTransient<IContactService, ContactService>();
services.AddTransient<ISignatureService, SignatureService>(); services.AddTransient<ISignatureService, SignatureService>();
services.AddTransient<IContextMenuItemService, ContextMenuItemService>(); services.AddTransient<IContextMenuItemService, ContextMenuItemService>();
services.AddTransient<ISpecialImapProviderConfigResolver, SpecialImapProviderConfigResolver>(); services.AddTransient<ISpecialImapProviderConfigResolver, SpecialImapProviderConfigResolver>();
services.AddSingleton<IThreadingStrategyProvider, ThreadingStrategyProvider>(); services.AddSingleton<IThreadingStrategyProvider, ThreadingStrategyProvider>();
services.AddTransient<IOutlookThreadingStrategy, OutlookThreadingStrategy>(); services.AddTransient<IOutlookThreadingStrategy, OutlookThreadingStrategy>();
services.AddTransient<IGmailThreadingStrategy, GmailThreadingStrategy>(); services.AddTransient<IGmailThreadingStrategy, GmailThreadingStrategy>();
services.AddTransient<IImapThreadingStrategy, ImapThreadingStrategy>(); services.AddTransient<IImapThreadingStrategy, ImapThreadingStrategy>();
}
} }
} }