Skip to content

Commit

Permalink
Show "You" for active account in mail rendering page (#566)
Browse files Browse the repository at this point in the history
* Added account contact view model to handle "You" case.

* fix namespaces again
  • Loading branch information
Tiktack authored Feb 16, 2025
1 parent f7836ee commit caae751
Show file tree
Hide file tree
Showing 9 changed files with 337 additions and 312 deletions.
13 changes: 1 addition & 12 deletions Wino.Core.Domain/Entities/Shared/AccountContact.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,6 @@ public class AccountContact : IEquatable<AccountContact>
/// </summary>
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)
{
return Equals(obj as AccountContact);
Expand All @@ -58,10 +50,7 @@ public bool Equals(AccountContact other)

public override int GetHashCode()
{
int hashCode = -1717786383;
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Address);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Name);
return hashCode;
return HashCode.Combine(Address, Name);
}

public static bool operator ==(AccountContact left, AccountContact right)
Expand Down
1 change: 1 addition & 0 deletions Wino.Core.Domain/Translations/en_US/resources.json
Original file line number Diff line number Diff line change
Expand Up @@ -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'.",
Expand Down
317 changes: 158 additions & 159 deletions Wino.Core.UWP/WinoApplication.cs

Large diffs are not rendered by default.

193 changes: 96 additions & 97 deletions Wino.Core.ViewModels/AboutPageViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,125 +6,124 @@
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;

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)
{
_storeRatingService = storeRatingService;
_dialogService = dialogService;
_nativeAppService = nativeAppService;
_logInitializer = logInitializer;
_appInitializerService = appInitializerService;
_clipboardService = clipboardService;
_fileService = fileService;

PreferencesService = preferencesService;
}
_storeRatingService = storeRatingService;
_dialogService = dialogService;
_nativeAppService = nativeAppService;
_logInitializer = logInitializer;
_appInitializerService = appInitializerService;
_clipboardService = clipboardService;
_fileService = fileService;

PreferencesService = preferencesService;
}

protected override void OnActivated()
{
base.OnActivated();
protected override void OnActivated()
{
base.OnActivated();

PreferencesService.PreferenceChanged -= PreferencesChanged;
PreferencesService.PreferenceChanged += PreferencesChanged;
}
PreferencesService.PreferenceChanged -= PreferencesChanged;
PreferencesService.PreferenceChanged += PreferencesChanged;
}

protected override void OnDeactivated()
{
base.OnDeactivated();
protected override void OnDeactivated()
{
base.OnDeactivated();

PreferencesService.PreferenceChanged -= PreferencesChanged;
}
PreferencesService.PreferenceChanged -= PreferencesChanged;
}

private void PreferencesChanged(object sender, string e)
private void PreferencesChanged(object sender, string e)
{
if (e == nameof(PreferencesService.IsLoggingEnabled))
{
if (e == nameof(PreferencesService.IsLoggingEnabled))
{
_logInitializer.RefreshLoggingLevel();
}
_logInitializer.RefreshLoggingLevel();
}
}

[RelayCommand]
private async Task CopyDiagnosticId()
[RelayCommand]
private async Task CopyDiagnosticId()
{
try
{
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.");
}
await _clipboardService.CopyClipboardAsync(PreferencesService.DiagnosticId);
_dialogService.InfoBarMessage(Translator.Buttons_Copy, string.Format(Translator.ClipboardTextCopied_Message, "Id"), InfoBarMessageType.Success);
}

[RelayCommand]
private async Task ShareWinoLogAsync()
catch (Exception ex)
{
var appDataFolder = _appInitializerService.ApplicationDataFolderPath;
_dialogService.InfoBarMessage(Translator.GeneralTitle_Error, string.Format(Translator.ClipboardTextCopyFailed_Message, "Id"), InfoBarMessageType.Error);
Log.Error(ex, "Failed to copy diagnostic id to clipboard.");
}
}

var selectedFolderPath = await _dialogService.PickWindowsFolderAsync();
[RelayCommand]
private async Task ShareWinoLogAsync()
{
var appDataFolder = _appInitializerService.ApplicationDataFolderPath;

if (string.IsNullOrEmpty(selectedFolderPath)) return;
var selectedFolderPath = await _dialogService.PickWindowsFolderAsync();

var areLogsSaved = await _fileService.SaveLogsToFolderAsync(appDataFolder, selectedFolderPath).ConfigureAwait(false);
if (string.IsNullOrEmpty(selectedFolderPath)) return;

if (areLogsSaved)
{
_dialogService.InfoBarMessage(Translator.Info_LogsSavedTitle, string.Format(Translator.Info_LogsSavedMessage, Constants.LogArchiveFileName), InfoBarMessageType.Success);
}
else
{
_dialogService.InfoBarMessage(Translator.Info_LogsNotFoundTitle, Translator.Info_LogsNotFoundMessage, InfoBarMessageType.Error);
}
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
{
_dialogService.InfoBarMessage(Translator.Info_LogsNotFoundTitle, Translator.Info_LogsNotFoundMessage, InfoBarMessageType.Error);
}
}

[RelayCommand]
private async Task Navigate(object url)
[RelayCommand]
private async Task Navigate(object url)
{
if (url is string stringUrl)
{
if (url is string stringUrl)
if (stringUrl == "Store")
await ShowRateDialogAsync();
else
{
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));
}
// 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();
}
37 changes: 37 additions & 0 deletions Wino.Mail.ViewModels/Data/AccountContactViewModel.cs
Original file line number Diff line number Diff line change
@@ -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()}>";
}
37 changes: 19 additions & 18 deletions Wino.Mail.ViewModels/MailRenderingPageViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,35 +90,33 @@ public bool IsDarkWebviewRenderer

[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<AccountContact> ToItems { get; set; } = [];
public ObservableCollection<AccountContact> CcItems { get; set; } = [];
public ObservableCollection<AccountContact> BccItems { get; set; } = [];
public partial DateTime CreationDate { get; set; }
public ObservableCollection<AccountContactViewModel> ToItems { get; set; } = [];
public ObservableCollection<AccountContactViewModel> CcItems { get; set; } = [];
public ObservableCollection<AccountContactViewModel> BccItems { get; set; } = [];
public ObservableCollection<MailAttachmentViewModel> Attachments { get; set; } = [];
public ObservableCollection<MailOperationMenuItem> MenuItems { get; set; } = [];

Expand Down Expand Up @@ -467,24 +465,27 @@ await ExecuteUIThread(() =>
});
}

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)
{
if (item is MailboxAddress mailboxAddress)
{
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)
Expand Down
4 changes: 2 additions & 2 deletions Wino.Mail/Views/MailRenderingPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@
mc:Ignorable="d">

<Page.Resources>
<DataTemplate x:Key="InternetAddressTemplate" x:DataType="entities:AccountContact">
<DataTemplate x:Key="InternetAddressTemplate" x:DataType="viewModelData:AccountContactViewModel">
<HyperlinkButton
Margin="-2,-2"
Padding="4,2"
Click="InternetAddressClicked"
Content="{x:Bind ShortDisplayName}"
Content="{x:Bind ShortNameOrYou}"
ToolTipService.ToolTip="{x:Bind DisplayName}">
<HyperlinkButton.ContextFlyout>
<Flyout Placement="Bottom">
Expand Down
2 changes: 1 addition & 1 deletion Wino.Services/ContactService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public Task<List<AccountContact>> GetAddressInformationAsync(string queryText)
}

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)
{
Expand Down
Loading

0 comments on commit caae751

Please sign in to comment.