diff --git a/Wino.Core.Domain/AppUrls.cs b/Wino.Core.Domain/AppUrls.cs new file mode 100644 index 00000000..72740d3f --- /dev/null +++ b/Wino.Core.Domain/AppUrls.cs @@ -0,0 +1,10 @@ +namespace Wino.Core.Domain; + +public static class AppUrls +{ + public const string Website = "https://www.winomail.app"; + public const string Discord = "https://discord.gg/windows-apps-hub-714581497222398064"; + public const string GitHub = "https://github.com/bkaankose/Wino-Mail/"; + public const string PrivacyPolicy = "https://www.winomail.app/support/privacy"; + public const string Paypal = "https://paypal.me/bkaankose?country.x=PL&locale.x=en_US"; +} diff --git a/Wino.Core.Domain/Translations/en_US/resources.json b/Wino.Core.Domain/Translations/en_US/resources.json index f7683c2f..70ce1ef6 100644 --- a/Wino.Core.Domain/Translations/en_US/resources.json +++ b/Wino.Core.Domain/Translations/en_US/resources.json @@ -763,6 +763,8 @@ "SettingsPrefer24HourClock_Title": "Display Clock Format in 24 Hours", "SettingsPrivacyPolicy_Description": "Review privacy policy.", "SettingsPrivacyPolicy_Title": "Privacy Policy", + "SettingsWebsite_Description": "Open the Wino Mail website.", + "SettingsWebsite_Title": "Website", "SettingsReadComposePane_Description": "Fonts, external content.", "SettingsReadComposePane_Title": "Reader & Composer", "SettingsReader_Title": "Reader", @@ -864,6 +866,7 @@ "SystemFolderConfigSetupSuccess_Message": "System folders are successfully configured.", "SystemFolderConfigSetupSuccess_Title": "System Folders Setup", "SystemTrayMenu_ShowWino": "Open Wino Mail", + "SystemTrayMenu_ShowWinoCalendar": "Open Wino Calendar", "SystemTrayMenu_ExitWino": "Exit", "TestingImapConnectionMessage": "Testing server connection...", "TitleBarServerDisconnectedButton_Description": "Wino is disconnected from the network. Click reconnect to restore connection.", diff --git a/Wino.Core.ViewModels/AboutPageViewModel.cs b/Wino.Core.ViewModels/AboutPageViewModel.cs index 2ea69416..b2755d8c 100644 --- a/Wino.Core.ViewModels/AboutPageViewModel.cs +++ b/Wino.Core.ViewModels/AboutPageViewModel.cs @@ -21,10 +21,11 @@ public partial class AboutPageViewModel : CoreBaseViewModel 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 string WebsiteUrl => AppUrls.Website; + public string DiscordChannelUrl => AppUrls.Discord; + public string GitHubUrl => AppUrls.GitHub; + public string PrivacyPolicyUrl => AppUrls.PrivacyPolicy; + public string PaypalUrl => AppUrls.Paypal; public IPreferencesService PreferencesService { get; } diff --git a/Wino.Core.ViewModels/SettingOptionsPageViewModel.cs b/Wino.Core.ViewModels/SettingOptionsPageViewModel.cs index f1840720..9f55c8b2 100644 --- a/Wino.Core.ViewModels/SettingOptionsPageViewModel.cs +++ b/Wino.Core.ViewModels/SettingOptionsPageViewModel.cs @@ -16,6 +16,9 @@ public partial class SettingOptionsPageViewModel : CoreBaseViewModel private readonly INativeAppService _nativeAppService; private readonly IAccountService _accountService; + public string WebsiteUrl => AppUrls.Website; + public string PaypalUrl => AppUrls.Paypal; + [ObservableProperty] public partial string VersionText { get; set; } = string.Empty; diff --git a/Wino.Mail.ViewModels/ContactsPageViewModel.cs b/Wino.Mail.ViewModels/ContactsPageViewModel.cs index b740a110..bc8e6170 100644 --- a/Wino.Mail.ViewModels/ContactsPageViewModel.cs +++ b/Wino.Mail.ViewModels/ContactsPageViewModel.cs @@ -12,6 +12,7 @@ using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Enums; using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Models.Navigation; +using Wino.Mail.ViewModels.Data; namespace Wino.Mail.ViewModels; @@ -56,8 +57,8 @@ public partial class ContactsPageViewModel : MailBaseViewModel public bool CanLoadMoreContacts => HasMoreContacts && !IsLoading && !IsLoadingMore; public bool CanDeleteSelectedContacts => SelectedContactsCount > 0; - public ObservableCollection Contacts { get; } = new(); - public ObservableCollection SelectedContacts { get; } = new(); + public ObservableCollection Contacts { get; } = new(); + public ObservableCollection SelectedContacts { get; } = new(); public ContactsPageViewModel(IContactService contactService, IMailDialogService dialogService) { @@ -150,7 +151,7 @@ public partial class ContactsPageViewModel : MailBaseViewModel foreach (var contact in page.Contacts) { - Contacts.Add(contact); + Contacts.Add(new AccountContactViewModel(contact)); } TotalContactsCount = page.TotalCount; @@ -217,8 +218,9 @@ public partial class ContactsPageViewModel : MailBaseViewModel } [RelayCommand] - private async Task EditContactAsync(AccountContact contact) + private async Task EditContactAsync(AccountContactViewModel contactViewModel) { + var contact = contactViewModel?.SourceContact; if (contact == null) return; var result = await _dialogService.ShowEditContactDialogAsync(contact); @@ -249,8 +251,9 @@ public partial class ContactsPageViewModel : MailBaseViewModel } [RelayCommand] - private async Task DeleteContactAsync(AccountContact contact) + private async Task DeleteContactAsync(AccountContactViewModel contactViewModel) { + var contact = contactViewModel?.SourceContact; if (contact == null || contact.IsRootContact) { _dialogService.InfoBarMessage( @@ -277,6 +280,7 @@ public partial class ContactsPageViewModel : MailBaseViewModel if (SelectedContacts.Count == 0) return; var deletableContacts = SelectedContacts + .Select(c => c?.SourceContact) .Where(c => c != null && !c.IsRootContact) .GroupBy(c => c.Address, StringComparer.OrdinalIgnoreCase) .Select(g => g.First()) @@ -366,8 +370,9 @@ public partial class ContactsPageViewModel : MailBaseViewModel } [RelayCommand] - private async Task PickContactPhotoAsync(AccountContact contact) + private async Task PickContactPhotoAsync(AccountContactViewModel contactViewModel) { + var contact = contactViewModel?.SourceContact; if (contact == null) return; try @@ -410,7 +415,7 @@ public partial class ContactsPageViewModel : MailBaseViewModel }); } - private static void ReplaceContactByAddress(ObservableCollection source, AccountContact updatedContact) + private static void ReplaceContactByAddress(ObservableCollection source, AccountContact updatedContact) { var index = source .Select((item, i) => new { item, i }) @@ -419,7 +424,7 @@ public partial class ContactsPageViewModel : MailBaseViewModel if (index < 0) return; - source[index] = CloneContact(updatedContact); + source[index] = new AccountContactViewModel(CloneContact(updatedContact)); } private static AccountContact CloneContact(AccountContact contact) diff --git a/Wino.Mail.ViewModels/Data/AccountContactViewModel.cs b/Wino.Mail.ViewModels/Data/AccountContactViewModel.cs index 41731b0c..30c1e302 100644 --- a/Wino.Mail.ViewModels/Data/AccountContactViewModel.cs +++ b/Wino.Mail.ViewModels/Data/AccountContactViewModel.cs @@ -8,17 +8,23 @@ namespace Wino.Mail.ViewModels.Data; public partial class AccountContactViewModel : ObservableObject, IMailItemDisplayInformation { + public AccountContact SourceContact { get; } public string Address { get; set; } public string Name { get; set; } + public Guid? ContactPictureFileId { get; set; } public string Base64ContactPicture { get; set; } public bool IsRootContact { get; set; } + public bool IsOverridden { get; set; } public AccountContactViewModel(AccountContact contact) { + SourceContact = contact; Address = contact.Address; Name = contact.Name; + ContactPictureFileId = contact.ContactPictureFileId; Base64ContactPicture = contact.Base64ContactPicture; IsRootContact = contact.IsRootContact; + IsOverridden = contact.IsOverridden; } /// @@ -68,7 +74,9 @@ public partial class AccountContactViewModel : ObservableObject, IMailItemDispla { Address = Address, Name = Name, + ContactPictureFileId = ContactPictureFileId, Base64ContactPicture = Base64ContactPicture, - IsRootContact = IsRootContact + IsRootContact = IsRootContact, + IsOverridden = IsOverridden }; } diff --git a/Wino.Mail.WinUI/Controls/ImagePreviewControl.cs b/Wino.Mail.WinUI/Controls/ImagePreviewControl.cs index fae89a8f..3f46b383 100644 --- a/Wino.Mail.WinUI/Controls/ImagePreviewControl.cs +++ b/Wino.Mail.WinUI/Controls/ImagePreviewControl.cs @@ -73,7 +73,6 @@ public sealed partial class ImagePreviewControl : PersonPicture RequestRefresh(); } - private void OnLoaded(object sender, RoutedEventArgs e) { RequestRefresh(); @@ -306,7 +305,6 @@ public sealed partial class ImagePreviewControl : PersonPicture return string.Empty; } - private async Task ApplyInitialVisualStateAsync(string displayName, long refreshVersion, CancellationToken cancellationToken) { await ExecuteOnUiThreadAsync(() => diff --git a/Wino.Mail.WinUI/ShellWindow.xaml b/Wino.Mail.WinUI/ShellWindow.xaml index 08d57726..b8d94b46 100644 --- a/Wino.Mail.WinUI/ShellWindow.xaml +++ b/Wino.Mail.WinUI/ShellWindow.xaml @@ -107,9 +107,6 @@ - - - + diff --git a/Wino.Mail.WinUI/ShellWindow.xaml.cs b/Wino.Mail.WinUI/ShellWindow.xaml.cs index 5dfe7043..6668236f 100644 --- a/Wino.Mail.WinUI/ShellWindow.xaml.cs +++ b/Wino.Mail.WinUI/ShellWindow.xaml.cs @@ -34,6 +34,7 @@ public sealed partial class ShellWindow : WindowEx, IWinoShellWindow, public INavigationService NavigationService { get; } = WinoApplication.Current.Services.GetService() ?? throw new Exception("NavigationService not registered in DI container."); public ICommand ShowWinoCommand { get; set; } + public ICommand ShowWinoCalendarCommand { get; set; } public ICommand ExitWinoCommand { get; set; } public ObservableCollection SyncActionItems { get; } = new(); @@ -60,7 +61,8 @@ public sealed partial class ShellWindow : WindowEx, IWinoShellWindow, // Register global mouse button listener for back button RegisterMouseBackButtonListener(); - ShowWinoCommand = new RelayCommand(RestoreFromTray); + ShowWinoCommand = new RelayCommand(() => RestoreAndSwitchMode(WinoApplicationMode.Mail)); + ShowWinoCalendarCommand = new RelayCommand(() => RestoreAndSwitchMode(WinoApplicationMode.Calendar)); ExitWinoCommand = new RelayCommand(ForceClose); this.SetIcon("Assets/Wino_Icon.ico"); @@ -281,6 +283,18 @@ public sealed partial class ShellWindow : WindowEx, IWinoShellWindow, BringToFront(); } + private void RestoreAndSwitchMode(WinoApplicationMode mode) + { + _currentMode = mode; + + _isApplyingActivationMode = true; + AppModeSegmentedControl.SelectedIndex = mode == WinoApplicationMode.Mail ? 0 : 1; + _isApplyingActivationMode = false; + + NavigationService.ChangeApplicationMode(mode); + RestoreFromTray(); + } + public void ForceClose() { // Unsubscribe from the closing event to avoid infinite loop diff --git a/Wino.Mail.WinUI/Views/Account/AccountDetailsPage.xaml.cs b/Wino.Mail.WinUI/Views/Account/AccountDetailsPage.xaml.cs index bd194bda..712f782b 100644 --- a/Wino.Mail.WinUI/Views/Account/AccountDetailsPage.xaml.cs +++ b/Wino.Mail.WinUI/Views/Account/AccountDetailsPage.xaml.cs @@ -49,15 +49,12 @@ public sealed partial class AccountDetailsPage : AccountDetailsPageAbstract { base.OnNavigatedTo(e); - if (e.NavigationMode == NavigationMode.New) + var targetTabIndex = ViewModel.SelectedTabIndex; + if (targetTabIndex < 0 || targetTabIndex >= TabSelector.Items.Count) { - var targetTabIndex = ViewModel.SelectedTabIndex; - if (targetTabIndex < 0 || targetTabIndex >= TabSelector.Items.Count) - { - targetTabIndex = 1; - } - - TabSelector.SelectedItem = TabSelector.Items[targetTabIndex]; + targetTabIndex = 1; } + + TabSelector.SelectedItem = TabSelector.Items[targetTabIndex]; } } diff --git a/Wino.Mail.WinUI/Views/SettingOptionsPage.xaml b/Wino.Mail.WinUI/Views/SettingOptionsPage.xaml index 52cd9d26..17bb7d32 100644 --- a/Wino.Mail.WinUI/Views/SettingOptionsPage.xaml +++ b/Wino.Mail.WinUI/Views/SettingOptionsPage.xaml @@ -33,19 +33,12 @@ - - - - + + + + + diff --git a/Wino.Mail.WinUI/Views/Settings/AboutPage.xaml b/Wino.Mail.WinUI/Views/Settings/AboutPage.xaml index b42b970c..3a9f92ee 100644 --- a/Wino.Mail.WinUI/Views/Settings/AboutPage.xaml +++ b/Wino.Mail.WinUI/Views/Settings/AboutPage.xaml @@ -17,9 +17,12 @@ + Width="128" + Height="128" + Margin="0,0,0,12" + HorizontalAlignment="Center" + Source="ms-appx:///Assets/AppEntries/MailAssets/Square150x150Logo.scale-100.png" + Stretch="Uniform" /> + + + + + + + + + - + diff --git a/Wino.Mail.WinUI/Views/Settings/ContactsPage.xaml b/Wino.Mail.WinUI/Views/Settings/ContactsPage.xaml index 9b1a82ec..79b15966 100644 --- a/Wino.Mail.WinUI/Views/Settings/ContactsPage.xaml +++ b/Wino.Mail.WinUI/Views/Settings/ContactsPage.xaml @@ -3,18 +3,20 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:abstract="using:Wino.Views.Abstract" + xmlns:controls="using:Wino.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:domain="using:Wino.Core.Domain" xmlns:entities="using:Wino.Core.Domain.Entities.Shared" xmlns:helpers="using:Wino.Helpers" xmlns:listview="using:Wino.Mail.WinUI.Controls.ListView" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:viewModels="using:Wino.Mail.ViewModels.Data" xmlns:toolkitExt="using:CommunityToolkit.WinUI" x:Name="root" mc:Ignorable="d"> - + - + MailItemInformation="{x:Bind Mode=OneWay}" /> ()) + foreach (var removedItem in e.RemovedItems.OfType()) { var selectedContact = ViewModel.SelectedContacts.FirstOrDefault(c => string.Equals(c.Address, removedItem.Address, StringComparison.OrdinalIgnoreCase)); @@ -64,7 +64,7 @@ public sealed partial class ContactsPage : ContactsPageAbstract } } - foreach (var addedItem in e.AddedItems.OfType()) + foreach (var addedItem in e.AddedItems.OfType()) { var alreadySelected = ViewModel.SelectedContacts.Any(c => string.Equals(c.Address, addedItem.Address, StringComparison.OrdinalIgnoreCase));