Settings shell.

This commit is contained in:
Burak Kaan Köse
2026-03-12 19:04:47 +01:00
parent de5309ea56
commit 7f0b671b62
28 changed files with 615 additions and 224 deletions
@@ -4,5 +4,6 @@ public enum WinoApplicationMode
{ {
Mail, Mail,
Calendar, Calendar,
Contacts Contacts,
Settings
} }
@@ -0,0 +1,22 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.MenuItems;
public partial class SettingsShellPageMenuItem(
WinoPage pageType,
string title,
string description,
string glyph) : MenuItemBase
{
public WinoPage PageType { get; } = pageType;
[ObservableProperty]
public partial string Title { get; set; } = title;
[ObservableProperty]
public partial string Description { get; set; } = description;
[ObservableProperty]
public partial string Glyph { get; set; } = glyph;
}
@@ -0,0 +1,12 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace Wino.Core.Domain.MenuItems;
public partial class SettingsShellSectionMenuItem(string title, string glyph) : MenuItemBase
{
[ObservableProperty]
public partial string Title { get; set; } = title;
[ObservableProperty]
public partial string Glyph { get; set; } = glyph;
}
@@ -3,4 +3,5 @@ namespace Wino.Core.Domain.Models.Navigation;
public sealed class ShellModeActivationContext public sealed class ShellModeActivationContext
{ {
public bool IsInitialActivation { get; init; } public bool IsInitialActivation { get; init; }
public object Parameter { get; init; }
} }
@@ -0,0 +1,122 @@
using System.Collections.Generic;
using System.Linq;
using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Models.Settings;
public sealed class SettingsNavigationItemInfo(
WinoPage? pageType,
string title,
string description,
string glyph = "",
bool isSeparator = false)
{
public WinoPage? PageType { get; } = pageType;
public string Title { get; } = title;
public string Description { get; } = description;
public string Glyph { get; } = glyph;
public bool IsSeparator { get; } = isSeparator;
}
public static class SettingsNavigationInfoProvider
{
public static IReadOnlyList<SettingsNavigationItemInfo> GetNavigationItems(string manageAccountsDescription = "")
{
return
[
new(WinoPage.SettingOptionsPage,
Translator.SettingsHome_Title,
Translator.SettingsOptions_HeroDescription,
"\uE80F"),
new(WinoPage.ManageAccountsPage,
Translator.SettingsManageAccountSettings_Title,
manageAccountsDescription,
"\uE77B"),
new(null, Translator.SettingsOptions_GeneralSection, string.Empty, "\uE713", isSeparator: true),
new(WinoPage.AppPreferencesPage,
Translator.SettingsAppPreferences_Title,
Translator.SettingsAppPreferences_Description,
"\uE770"),
new(WinoPage.LanguageTimePage,
Translator.SettingsLanguageTime_Title,
Translator.SettingsLanguageTime_Description,
"\uE775"),
new(WinoPage.PersonalizationPage,
Translator.SettingsPersonalization_Title,
Translator.SettingsPersonalization_Description,
"\uE771"),
new(WinoPage.AboutPage,
Translator.SettingsAbout_Title,
Translator.SettingsAbout_Description,
"\uE946"),
new(null, Translator.SettingsOptions_MailSection, string.Empty, "\uE715", isSeparator: true),
new(WinoPage.KeyboardShortcutsPage,
Translator.Settings_KeyboardShortcuts_Title,
Translator.Settings_KeyboardShortcuts_Description,
"\uE765"),
new(WinoPage.MessageListPage,
Translator.SettingsMessageList_Title,
Translator.SettingsMessageList_Description,
"\uE8C4"),
new(WinoPage.ReadComposePanePage,
Translator.SettingsReadComposePane_Title,
Translator.SettingsReadComposePane_Description,
"\uE8BD"),
new(WinoPage.SignatureAndEncryptionPage,
Translator.SettingsSignatureAndEncryption_Title,
Translator.SettingsSignatureAndEncryption_Description,
"\uE8D7"),
new(WinoPage.StoragePage,
Translator.SettingsStorage_Title,
Translator.SettingsStorage_Description,
"\uE81C"),
new(null, Translator.SettingsOptions_CalendarSection, string.Empty, "\uE787", isSeparator: true),
new(WinoPage.CalendarSettingsPage,
Translator.SettingsCalendarSettings_Title,
Translator.SettingsCalendarSettings_Description,
"\uE787")
];
}
public static SettingsNavigationItemInfo GetInfo(WinoPage pageType, string manageAccountsDescription = "")
{
var rootPage = GetRootPage(pageType);
return GetNavigationItems(manageAccountsDescription).First(item => item.PageType == rootPage);
}
public static string GetPageTitle(WinoPage pageType)
=> pageType switch
{
WinoPage.SettingOptionsPage => Translator.MenuSettings,
WinoPage.ManageAccountsPage => Translator.SettingsManageAccountSettings_Title,
WinoPage.AccountManagementPage => Translator.SettingsManageAccountSettings_Title,
WinoPage.PersonalizationPage => Translator.SettingsPersonalization_Title,
WinoPage.AboutPage => Translator.SettingsAbout_Title,
WinoPage.MessageListPage => Translator.SettingsMessageList_Title,
WinoPage.ReadComposePanePage => Translator.SettingsReadComposePane_Title,
WinoPage.LanguageTimePage => Translator.SettingsLanguageTime_Title,
WinoPage.AppPreferencesPage => Translator.SettingsAppPreferences_Title,
WinoPage.CalendarSettingsPage => Translator.SettingsCalendarSettings_Title,
WinoPage.SignatureAndEncryptionPage => Translator.SettingsSignatureAndEncryption_Title,
WinoPage.KeyboardShortcutsPage => Translator.Settings_KeyboardShortcuts_Title,
WinoPage.StoragePage => Translator.SettingsStorage_Title,
WinoPage.EmailTemplatesPage => Translator.SettingsEmailTemplates_Title,
WinoPage.CreateEmailTemplatePage => Translator.SettingsEmailTemplates_Title,
_ => GetInfo(pageType).Title
};
public static WinoPage GetRootPage(WinoPage pageType)
=> pageType switch
{
WinoPage.AccountManagementPage => WinoPage.ManageAccountsPage,
WinoPage.AccountDetailsPage => WinoPage.ManageAccountsPage,
WinoPage.MergedAccountDetailsPage => WinoPage.ManageAccountsPage,
WinoPage.AliasManagementPage => WinoPage.ManageAccountsPage,
WinoPage.SignatureManagementPage => WinoPage.ManageAccountsPage,
WinoPage.ImapCalDavSettingsPage => WinoPage.ManageAccountsPage,
WinoPage.EmailTemplatesPage => WinoPage.ManageAccountsPage,
WinoPage.CreateEmailTemplatePage => WinoPage.ManageAccountsPage,
WinoPage.CalendarAccountSettingsPage => WinoPage.CalendarSettingsPage,
_ => pageType
};
}
@@ -815,6 +815,7 @@
"SettingsNotifications_Title": "Notifications", "SettingsNotifications_Title": "Notifications",
"SettingsNotificationsAndTaskbar_Description": "Change whether notifications should be displayed and taskbar badge for this account.", "SettingsNotificationsAndTaskbar_Description": "Change whether notifications should be displayed and taskbar badge for this account.",
"SettingsNotificationsAndTaskbar_Title": "Notifications & Taskbar", "SettingsNotificationsAndTaskbar_Title": "Notifications & Taskbar",
"SettingsHome_Title": "Home",
"SettingsOptions_Title": "Settings", "SettingsOptions_Title": "Settings",
"SettingsOptions_GeneralSection": "General", "SettingsOptions_GeneralSection": "General",
"SettingsOptions_MailSection": "Mail", "SettingsOptions_MailSection": "Mail",
@@ -44,6 +44,7 @@ public partial class KeyboardShortcutViewModel : ObservableObject
WinoApplicationMode.Mail => Translator.KeyboardShortcuts_ModeMail, WinoApplicationMode.Mail => Translator.KeyboardShortcuts_ModeMail,
WinoApplicationMode.Calendar => Translator.KeyboardShortcuts_ModeCalendar, WinoApplicationMode.Calendar => Translator.KeyboardShortcuts_ModeCalendar,
WinoApplicationMode.Contacts => Translator.ContactsPage_Title, WinoApplicationMode.Contacts => Translator.ContactsPage_Title,
WinoApplicationMode.Settings => Translator.MenuSettings,
_ => Mode.ToString() _ => Mode.ToString()
}; };
@@ -7,6 +7,7 @@ using Wino.Core.Domain;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Navigation; using Wino.Core.Domain.Models.Navigation;
using Wino.Core.Domain.Models.Settings;
using Wino.Messaging.Client.Navigation; using Wino.Messaging.Client.Navigation;
namespace Wino.Core.ViewModels; namespace Wino.Core.ViewModels;
@@ -63,23 +64,8 @@ public partial class SettingOptionsPageViewModel : CoreBaseViewModel
{ {
if (type is WinoPage pageType) if (type is WinoPage pageType)
{ {
string pageTitle = pageType switch var pageInfo = SettingsNavigationInfoProvider.GetInfo(pageType, AccountSummaryText);
{ Messenger.Send(new BreadcrumbNavigationRequested(pageInfo.Title, pageType));
WinoPage.ManageAccountsPage => Translator.SettingsManageAccountSettings_Title,
WinoPage.PersonalizationPage => Translator.SettingsPersonalization_Title,
WinoPage.AboutPage => Translator.SettingsAbout_Title,
WinoPage.MessageListPage => Translator.SettingsMessageList_Title,
WinoPage.ReadComposePanePage => Translator.SettingsReadComposePane_Title,
WinoPage.LanguageTimePage => Translator.SettingsLanguageTime_Title,
WinoPage.AppPreferencesPage => Translator.SettingsAppPreferences_Title,
WinoPage.CalendarSettingsPage => Translator.SettingsCalendarSettings_Title,
WinoPage.SignatureAndEncryptionPage => Translator.SettingsSignatureAndEncryption_Title,
WinoPage.KeyboardShortcutsPage => Translator.Settings_KeyboardShortcuts_Title,
WinoPage.StoragePage => Translator.SettingsStorage_Title,
_ => throw new NotImplementedException()
};
Messenger.Send(new BreadcrumbNavigationRequested(pageTitle, pageType));
} }
} }
+42 -3
View File
@@ -1,15 +1,54 @@
using Wino.Core.Domain.Interfaces; using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using Wino.Core.Domain;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Settings;
namespace Wino.Core.ViewModels; namespace Wino.Core.ViewModels;
public class SettingsPageViewModel : CoreBaseViewModel public partial class SettingsPageViewModel : CoreBaseViewModel
{ {
public SettingsPageViewModel(INavigationService navigationService, IStatePersistanceService statePersistenceService) private readonly IAccountService _accountService;
public SettingsPageViewModel(
INavigationService navigationService,
IStatePersistanceService statePersistenceService,
IAccountService accountService)
{ {
NavigationService = navigationService; NavigationService = navigationService;
StatePersistenceService = statePersistenceService; StatePersistenceService = statePersistenceService;
_accountService = accountService;
} }
public INavigationService NavigationService { get; } public INavigationService NavigationService { get; }
public IStatePersistanceService StatePersistenceService { get; } public IStatePersistanceService StatePersistenceService { get; }
[ObservableProperty]
public partial string CurrentDescription { get; set; } = string.Empty;
[ObservableProperty]
public partial string ManageAccountsDescription { get; set; } = string.Empty;
public async Task UpdateActivePageAsync(WinoPage pageType)
{
await EnsureAccountSummaryAsync();
var info = SettingsNavigationInfoProvider.GetInfo(pageType, ManageAccountsDescription);
await ExecuteUIThread(() => CurrentDescription = info.Description);
}
private async Task EnsureAccountSummaryAsync()
{
if (!string.IsNullOrWhiteSpace(ManageAccountsDescription))
return;
var accounts = await _accountService.GetAccountsAsync().ConfigureAwait(false);
var count = accounts?.Count ?? 0;
await ExecuteUIThread(() =>
{
ManageAccountsDescription = string.Format(Translator.SettingsOptions_AccountsSummary, count);
});
}
} }
@@ -84,7 +84,8 @@ public partial class AppPreferencesPageViewModel : MailBaseViewModel
[ [
Translator.SettingsAppPreferences_ApplicationMode_Mail, Translator.SettingsAppPreferences_ApplicationMode_Mail,
Translator.SettingsAppPreferences_ApplicationMode_Calendar, Translator.SettingsAppPreferences_ApplicationMode_Calendar,
Translator.ContactsPage_Title Translator.ContactsPage_Title,
Translator.MenuSettings
]; ];
SelectedDefaultSearchMode = SearchModes[(int)PreferencesService.DefaultSearchMode]; SelectedDefaultSearchMode = SearchModes[(int)PreferencesService.DefaultSearchMode];
@@ -1147,7 +1147,7 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
public async void Receive(NavigateAppPreferencesRequested message) public async void Receive(NavigateAppPreferencesRequested message)
{ {
await MenuItemInvokedOrSelectedAsync(SettingsItem, WinoPage.AppPreferencesPage); NavigationService.Navigate(WinoPage.SettingsPage, WinoPage.AppPreferencesPage);
} }
protected override void RegisterRecipients() protected override void RegisterRecipients()
@@ -53,6 +53,16 @@ internal static class AppModeActivationResolver
return true; return true;
} }
if (Contains(value, "wino-settings") ||
Contains(value, "--mode=settings") ||
Contains(value, "mode=settings") ||
Contains(value, "settingsapp") ||
EqualsToken(value, "settings"))
{
mode = WinoApplicationMode.Settings;
return true;
}
if (Contains(value, "wino-mail") || if (Contains(value, "wino-mail") ||
Contains(value, "--mode=mail") || Contains(value, "--mode=mail") ||
Contains(value, "mode=mail") || Contains(value, "mode=mail") ||
+3
View File
@@ -144,12 +144,14 @@ public partial class App : WinoApplication,
services.AddSingleton(typeof(MailAppShellViewModel)); services.AddSingleton(typeof(MailAppShellViewModel));
services.AddSingleton(typeof(CalendarAppShellViewModel)); services.AddSingleton(typeof(CalendarAppShellViewModel));
services.AddSingleton(typeof(ContactsShellClient)); services.AddSingleton(typeof(ContactsShellClient));
services.AddSingleton(typeof(SettingsShellClient));
services.AddSingleton(typeof(WinoAppShellViewModel)); services.AddSingleton(typeof(WinoAppShellViewModel));
services.AddSingleton<IMailShellClient>(serviceProvider => serviceProvider.GetRequiredService<MailAppShellViewModel>()); services.AddSingleton<IMailShellClient>(serviceProvider => serviceProvider.GetRequiredService<MailAppShellViewModel>());
services.AddSingleton<ICalendarShellClient>(serviceProvider => serviceProvider.GetRequiredService<CalendarAppShellViewModel>()); services.AddSingleton<ICalendarShellClient>(serviceProvider => serviceProvider.GetRequiredService<CalendarAppShellViewModel>());
services.AddSingleton<IShellClient>(serviceProvider => serviceProvider.GetRequiredService<MailAppShellViewModel>()); services.AddSingleton<IShellClient>(serviceProvider => serviceProvider.GetRequiredService<MailAppShellViewModel>());
services.AddSingleton<IShellClient>(serviceProvider => serviceProvider.GetRequiredService<CalendarAppShellViewModel>()); services.AddSingleton<IShellClient>(serviceProvider => serviceProvider.GetRequiredService<CalendarAppShellViewModel>());
services.AddSingleton<IShellClient>(serviceProvider => serviceProvider.GetRequiredService<ContactsShellClient>()); services.AddSingleton<IShellClient>(serviceProvider => serviceProvider.GetRequiredService<ContactsShellClient>());
services.AddSingleton<IShellClient>(serviceProvider => serviceProvider.GetRequiredService<SettingsShellClient>());
services.AddTransient(typeof(MailListPageViewModel)); services.AddTransient(typeof(MailListPageViewModel));
services.AddTransient(typeof(MailRenderingPageViewModel)); services.AddTransient(typeof(MailRenderingPageViewModel));
@@ -966,6 +968,7 @@ public partial class App : WinoApplication,
{ {
WinoApplicationMode.Calendar => "--mode=calendar", WinoApplicationMode.Calendar => "--mode=calendar",
WinoApplicationMode.Contacts => "--mode=contacts", WinoApplicationMode.Contacts => "--mode=contacts",
WinoApplicationMode.Settings => "--mode=settings",
_ => "--mode=mail" _ => "--mode=mail"
}; };
@@ -64,17 +64,11 @@ public sealed partial class AppModeFooterSwitcherControl : Segmented
if (_isUpdatingSelection) if (_isUpdatingSelection)
return; return;
if (SelectedIndex == 3)
{
_navigationService.Navigate(WinoPage.SettingsPage);
UpdateSelection(_statePersistenceService.ApplicationMode);
return;
}
var selectedMode = SelectedIndex switch var selectedMode = SelectedIndex switch
{ {
1 => WinoApplicationMode.Calendar, 1 => WinoApplicationMode.Calendar,
2 => WinoApplicationMode.Contacts, 2 => WinoApplicationMode.Contacts,
3 => WinoApplicationMode.Settings,
_ => WinoApplicationMode.Mail _ => WinoApplicationMode.Mail
}; };
@@ -91,6 +85,7 @@ public sealed partial class AppModeFooterSwitcherControl : Segmented
{ {
WinoApplicationMode.Calendar => 1, WinoApplicationMode.Calendar => 1,
WinoApplicationMode.Contacts => 2, WinoApplicationMode.Contacts => 2,
WinoApplicationMode.Settings => 3,
_ => 0 _ => 0
}; };
_isUpdatingSelection = false; _isUpdatingSelection = false;
@@ -14,6 +14,8 @@ public partial class NavigationMenuTemplateSelector : DataTemplateSelector
public DataTemplate MergedAccountMoreExpansionItemTemplate { get; set; } = null!; public DataTemplate MergedAccountMoreExpansionItemTemplate { get; set; } = null!;
public DataTemplate FolderMenuTemplate { get; set; } = null!; public DataTemplate FolderMenuTemplate { get; set; } = null!;
public DataTemplate SettingsItemTemplate { get; set; } = null!; public DataTemplate SettingsItemTemplate { get; set; } = null!;
public DataTemplate SettingsShellPageItemTemplate { get; set; } = null!;
public DataTemplate SettingsShellSectionItemTemplate { get; set; } = null!;
public DataTemplate StoreUpdateItemTemplate { get; set; } = null!; public DataTemplate StoreUpdateItemTemplate { get; set; } = null!;
public DataTemplate MoreItemsFolderTemplate { get; set; } = null!; public DataTemplate MoreItemsFolderTemplate { get; set; } = null!;
public DataTemplate RatingItemTemplate { get; set; } = null!; public DataTemplate RatingItemTemplate { get; set; } = null!;
@@ -38,6 +40,10 @@ public partial class NavigationMenuTemplateSelector : DataTemplateSelector
return ContactsMenuItemTemplate; return ContactsMenuItemTemplate;
else if (item is SettingsItem) else if (item is SettingsItem)
return SettingsItemTemplate; return SettingsItemTemplate;
else if (item is SettingsShellPageMenuItem)
return SettingsShellPageItemTemplate;
else if (item is SettingsShellSectionMenuItem)
return SettingsShellSectionItemTemplate;
else if (item is StoreUpdateMenuItem) else if (item is StoreUpdateMenuItem)
return StoreUpdateItemTemplate; return StoreUpdateItemTemplate;
else if (item is SeperatorItem) else if (item is SeperatorItem)
+65 -8
View File
@@ -8,6 +8,7 @@ using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Calendar; using Wino.Core.Domain.Models.Calendar;
using Wino.Core.Domain.Models.Navigation; using Wino.Core.Domain.Models.Navigation;
using Wino.Core.Domain.Models.Settings;
using Wino.Helpers; using Wino.Helpers;
using Wino.Mail.ViewModels.Data; using Wino.Mail.ViewModels.Data;
using Wino.Mail.ViewModels.Messages; using Wino.Mail.ViewModels.Messages;
@@ -66,6 +67,32 @@ public class NavigationService : NavigationServiceBase, INavigationService
WinoPage.ContactsPage WinoPage.ContactsPage
]; ];
private static readonly WinoPage[] SettingsOnlyPages =
[
WinoPage.SettingsPage,
WinoPage.SettingOptionsPage,
WinoPage.ManageAccountsPage,
WinoPage.AccountManagementPage,
WinoPage.AccountDetailsPage,
WinoPage.MergedAccountDetailsPage,
WinoPage.SignatureManagementPage,
WinoPage.AboutPage,
WinoPage.PersonalizationPage,
WinoPage.MessageListPage,
WinoPage.ReadComposePanePage,
WinoPage.LanguageTimePage,
WinoPage.AppPreferencesPage,
WinoPage.AliasManagementPage,
WinoPage.ImapCalDavSettingsPage,
WinoPage.KeyboardShortcutsPage,
WinoPage.SignatureAndEncryptionPage,
WinoPage.EmailTemplatesPage,
WinoPage.CreateEmailTemplatePage,
WinoPage.StoragePage,
WinoPage.CalendarSettingsPage,
WinoPage.CalendarAccountSettingsPage
];
public NavigationService(IStatePersistanceService statePersistanceService, IDispatcher dispatcher, IWinoWindowManager windowManager) public NavigationService(IStatePersistanceService statePersistanceService, IDispatcher dispatcher, IWinoWindowManager windowManager)
{ {
_statePersistanceService = statePersistanceService; _statePersistanceService = statePersistanceService;
@@ -186,7 +213,7 @@ public class NavigationService : NavigationServiceBase, INavigationService
public bool ChangeApplicationMode(WinoApplicationMode mode) public bool ChangeApplicationMode(WinoApplicationMode mode)
=> ExecuteOnNavigationThread(() => ChangeApplicationModeInternal(mode)); => ExecuteOnNavigationThread(() => ChangeApplicationModeInternal(mode));
private bool ChangeApplicationModeInternal(WinoApplicationMode mode) private bool ChangeApplicationModeInternal(WinoApplicationMode mode, object? activationParameter = null)
{ {
var coreFrame = GetCoreFrameInternal(NavigationReferenceFrame.ShellFrame); var coreFrame = GetCoreFrameInternal(NavigationReferenceFrame.ShellFrame);
@@ -217,7 +244,8 @@ public class NavigationService : NavigationServiceBase, INavigationService
{ {
shell.ActivateMode(mode, new ShellModeActivationContext shell.ActivateMode(mode, new ShellModeActivationContext
{ {
IsInitialActivation = isInitialShellNavigation IsInitialActivation = isInitialShellNavigation,
Parameter = activationParameter
}); });
return true; return true;
} }
@@ -237,9 +265,15 @@ public class NavigationService : NavigationServiceBase, INavigationService
NavigationReferenceFrame frame = NavigationReferenceFrame.InnerShellFrame, NavigationReferenceFrame frame = NavigationReferenceFrame.InnerShellFrame,
NavigationTransitionType transition = NavigationTransitionType.None) NavigationTransitionType transition = NavigationTransitionType.None)
{ {
if (page is WinoPage.ManageAccountsPage or WinoPage.AccountManagementPage) if (TryGetSettingsActivationTarget(page, parameter, out var settingsTarget))
{ {
return NavigateInternal(WinoPage.SettingsPage, WinoPage.ManageAccountsPage, frame, transition); if (_statePersistanceService.ApplicationMode != WinoApplicationMode.Settings)
{
return ChangeApplicationModeInternal(WinoApplicationMode.Settings, settingsTarget);
}
page = WinoPage.SettingsPage;
parameter = settingsTarget;
} }
var pageType = GetPageType(page); var pageType = GetPageType(page);
@@ -372,12 +406,16 @@ public class NavigationService : NavigationServiceBase, INavigationService
private static bool IsContactsOnlyPage(WinoPage page) private static bool IsContactsOnlyPage(WinoPage page)
=> ContactsOnlyPages.Contains(page); => ContactsOnlyPages.Contains(page);
private static bool IsSettingsOnlyPage(WinoPage page)
=> SettingsOnlyPages.Contains(page);
private static bool IsPageAllowedInMode(WinoApplicationMode mode, WinoPage page) private static bool IsPageAllowedInMode(WinoApplicationMode mode, WinoPage page)
=> mode switch => mode switch
{ {
WinoApplicationMode.Mail => !IsCalendarOnlyPage(page) && !IsContactsOnlyPage(page), WinoApplicationMode.Mail => !IsCalendarOnlyPage(page) && !IsContactsOnlyPage(page) && !IsSettingsOnlyPage(page),
WinoApplicationMode.Calendar => !IsMailOnlyPage(page) && !IsContactsOnlyPage(page), WinoApplicationMode.Calendar => !IsMailOnlyPage(page) && !IsContactsOnlyPage(page) && !IsSettingsOnlyPage(page),
WinoApplicationMode.Contacts => !IsMailOnlyPage(page) && !IsCalendarOnlyPage(page), WinoApplicationMode.Contacts => !IsMailOnlyPage(page) && !IsCalendarOnlyPage(page) && !IsSettingsOnlyPage(page),
WinoApplicationMode.Settings => IsSettingsOnlyPage(page),
_ => true _ => true
}; };
@@ -386,6 +424,7 @@ public class NavigationService : NavigationServiceBase, INavigationService
{ {
WinoApplicationMode.Calendar => "Wino Calendar", WinoApplicationMode.Calendar => "Wino Calendar",
WinoApplicationMode.Contacts => "Wino Contacts", WinoApplicationMode.Contacts => "Wino Contacts",
WinoApplicationMode.Settings => "Wino Settings",
_ => "Wino Mail" _ => "Wino Mail"
}; };
@@ -406,10 +445,28 @@ public class NavigationService : NavigationServiceBase, INavigationService
{ {
WinoApplicationMode.Mail => targetMode == WinoApplicationMode.Calendar, WinoApplicationMode.Mail => targetMode == WinoApplicationMode.Calendar,
WinoApplicationMode.Calendar => targetMode == WinoApplicationMode.Contacts, WinoApplicationMode.Calendar => targetMode == WinoApplicationMode.Contacts,
WinoApplicationMode.Contacts => targetMode == WinoApplicationMode.Mail, WinoApplicationMode.Contacts => targetMode == WinoApplicationMode.Settings,
WinoApplicationMode.Settings => targetMode == WinoApplicationMode.Mail,
_ => false _ => false
}; };
private static bool TryGetSettingsActivationTarget(WinoPage page, object? parameter, out WinoPage targetPage)
{
targetPage = WinoPage.SettingOptionsPage;
if (page == WinoPage.SettingsPage)
{
targetPage = parameter as WinoPage? ?? WinoPage.SettingOptionsPage;
return true;
}
if (!IsSettingsOnlyPage(page))
return false;
targetPage = SettingsNavigationInfoProvider.GetRootPage(page);
return true;
}
private static LoadCalendarMessage CreateLoadCalendarMessage(CalendarPageNavigationArgs args) private static LoadCalendarMessage CreateLoadCalendarMessage(CalendarPageNavigationArgs args)
{ {
var targetDate = args.RequestDefaultNavigation var targetDate = args.RequestDefaultNavigation
@@ -365,6 +365,7 @@
<controls:ItemsRepeaterScrollHost HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <controls:ItemsRepeaterScrollHost HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ScrollViewer <ScrollViewer
x:Name="MenuItemsScrollViewer" x:Name="MenuItemsScrollViewer"
VerticalAlignment="Top"
TabNavigation="Local" TabNavigation="Local"
VerticalScrollBarVisibility="Auto"> VerticalScrollBarVisibility="Auto">
<!-- Left nav ItemsRepeater --> <!-- Left nav ItemsRepeater -->
+33
View File
@@ -53,6 +53,39 @@
</coreControls:WinoNavigationViewItem.Icon> </coreControls:WinoNavigationViewItem.Icon>
</coreControls:WinoNavigationViewItem> </coreControls:WinoNavigationViewItem>
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="SettingsShellPageItemTemplate" x:DataType="menu:SettingsShellPageMenuItem">
<coreControls:WinoNavigationViewItem
Content="{x:Bind Title}"
DataContext="{x:Bind}">
<muxc:NavigationViewItem.Icon>
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="{x:Bind Glyph}" />
</muxc:NavigationViewItem.Icon>
</coreControls:WinoNavigationViewItem>
</DataTemplate>
<DataTemplate x:Key="SettingsShellSectionItemTemplate" x:DataType="menu:SettingsShellSectionMenuItem">
<coreControls:WinoNavigationViewItem
Margin="0,10,0,2"
HorizontalContentAlignment="Center"
DataContext="{x:Bind}"
IsEnabled="False"
SelectsOnInvoked="False">
<muxc:NavigationViewItem.Icon>
<FontIcon
FontFamily="{StaticResource SymbolThemeFontFamily}"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Glyph="{x:Bind Glyph}" />
</muxc:NavigationViewItem.Icon>
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind Title}"
TextWrapping="NoWrap" />
</coreControls:WinoNavigationViewItem>
</DataTemplate>
<!-- Store Update Item --> <!-- Store Update Item -->
<DataTemplate x:Key="StoreUpdateItemTemplate" x:DataType="menu:StoreUpdateMenuItem"> <DataTemplate x:Key="StoreUpdateItemTemplate" x:DataType="menu:StoreUpdateMenuItem">
<coreControls:WinoNavigationViewItem Content="{x:Bind domain:Translator.MenuUpdateAvailable}" DataContext="{x:Bind}"> <coreControls:WinoNavigationViewItem Content="{x:Bind domain:Translator.MenuUpdateAvailable}" DataContext="{x:Bind}">
@@ -0,0 +1,149 @@
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.MenuItems;
using Wino.Core.Domain.Models;
using Wino.Core.Domain.Models.Navigation;
using Wino.Core.Domain.Models.Settings;
using Wino.Core.ViewModels;
using Wino.Messaging.Client.Navigation;
using Wino.Messaging.Client.Shell;
namespace Wino.Mail.WinUI.ViewModels;
public partial class SettingsShellClient(INavigationService navigationService) :
CoreBaseViewModel,
IShellClient,
IRecipient<ActiveSettingsPageChanged>,
IRecipient<LanguageChanged>
{
public WinoApplicationMode Mode => WinoApplicationMode.Settings;
public MenuItemCollection? MenuItems { get; private set; }
[ObservableProperty]
public partial object? SelectedMenuItem { get; set; } = null;
public bool HandlesNavigationSelection => true;
protected override void OnDispatcherAssigned()
{
base.OnDispatcherAssigned();
MenuItems ??= new MenuItemCollection(Dispatcher);
RebuildMenuItems();
}
public void Activate(ShellModeActivationContext activationContext)
{
RebuildMenuItems();
var targetPage = activationContext.Parameter as WinoPage? ?? WinoPage.SettingOptionsPage;
SetSelectedRootPage(SettingsNavigationInfoProvider.GetRootPage(targetPage));
OnNavigatedTo(NavigationMode.New, activationContext);
navigationService.Navigate(WinoPage.SettingsPage, targetPage, NavigationReferenceFrame.InnerShellFrame);
}
public void Deactivate()
{
OnNavigatedFrom(NavigationMode.New, null!);
}
public Task HandleNavigationItemInvokedAsync(IMenuItem? menuItem)
{
if (menuItem is not SettingsShellPageMenuItem settingsMenuItem)
return Task.CompletedTask;
var currentPage = (SelectedMenuItem as SettingsShellPageMenuItem)?.PageType;
if (currentPage == settingsMenuItem.PageType && settingsMenuItem.PageType != WinoPage.SettingOptionsPage)
return Task.CompletedTask;
SetSelectedRootPage(settingsMenuItem.PageType);
Messenger.Send(new SettingsRootNavigationRequested(settingsMenuItem.PageType));
return Task.CompletedTask;
}
public Task HandleNavigationSelectionChangedAsync(IMenuItem? menuItem)
{
if (menuItem is not SettingsShellPageMenuItem settingsMenuItem)
return Task.CompletedTask;
if ((SelectedMenuItem as SettingsShellPageMenuItem)?.PageType == settingsMenuItem.PageType)
return Task.CompletedTask;
SetSelectedRootPage(settingsMenuItem.PageType);
Messenger.Send(new SettingsRootNavigationRequested(settingsMenuItem.PageType));
return Task.CompletedTask;
}
public override Task KeyboardShortcutHook(KeyboardShortcutTriggerDetails args) => Task.CompletedTask;
public void Receive(ActiveSettingsPageChanged message)
{
SetSelectedRootPage(message.RootPage);
}
public void Receive(LanguageChanged message)
{
var selectedPage = (SelectedMenuItem as SettingsShellPageMenuItem)?.PageType ?? WinoPage.SettingOptionsPage;
RebuildMenuItems();
SetSelectedRootPage(selectedPage);
}
private void RebuildMenuItems()
{
if (MenuItems == null)
return;
var selectedPage = (SelectedMenuItem as SettingsShellPageMenuItem)?.PageType ?? WinoPage.SettingOptionsPage;
MenuItems.Clear();
foreach (var item in SettingsNavigationInfoProvider.GetNavigationItems())
{
if (item.IsSeparator)
{
MenuItems.Add(new SettingsShellSectionMenuItem(item.Title, item.Glyph));
continue;
}
if (!item.PageType.HasValue)
continue;
MenuItems.Add(new SettingsShellPageMenuItem(item.PageType.Value, item.Title, item.Description, item.Glyph));
}
SetSelectedRootPage(selectedPage);
}
private void SetSelectedRootPage(WinoPage pageType)
{
if (MenuItems == null)
return;
var rootPage = SettingsNavigationInfoProvider.GetRootPage(pageType);
var selectedItem = MenuItems.OfType<SettingsShellPageMenuItem>()
.FirstOrDefault(item => item.PageType == rootPage);
if (ReferenceEquals(SelectedMenuItem, selectedItem))
return;
SelectedMenuItem = selectedItem;
}
protected override void RegisterRecipients()
{
base.RegisterRecipients();
Messenger.Register<ActiveSettingsPageChanged>(this);
Messenger.Register<LanguageChanged>(this);
}
protected override void UnregisterRecipients()
{
base.UnregisterRecipients();
Messenger.Unregister<ActiveSettingsPageChanged>(this);
Messenger.Unregister<LanguageChanged>(this);
}
}
@@ -54,6 +54,7 @@ public sealed class WinoAppShellViewModel : CoreBaseViewModel, IShellViewModel
OnPropertyChanged(nameof(IsMailMode)); OnPropertyChanged(nameof(IsMailMode));
OnPropertyChanged(nameof(IsCalendarMode)); OnPropertyChanged(nameof(IsCalendarMode));
OnPropertyChanged(nameof(IsContactsMode)); OnPropertyChanged(nameof(IsContactsMode));
OnPropertyChanged(nameof(IsSettingsMode));
OnPropertyChanged(nameof(SelectedMenuItem)); OnPropertyChanged(nameof(SelectedMenuItem));
} }
} }
@@ -63,6 +64,7 @@ public sealed class WinoAppShellViewModel : CoreBaseViewModel, IShellViewModel
public bool IsMailMode => CurrentMode == WinoApplicationMode.Mail; public bool IsMailMode => CurrentMode == WinoApplicationMode.Mail;
public bool IsCalendarMode => CurrentMode == WinoApplicationMode.Calendar; public bool IsCalendarMode => CurrentMode == WinoApplicationMode.Calendar;
public bool IsContactsMode => CurrentMode == WinoApplicationMode.Contacts; public bool IsContactsMode => CurrentMode == WinoApplicationMode.Contacts;
public bool IsSettingsMode => CurrentMode == WinoApplicationMode.Settings;
public MenuItemCollection? CurrentMenuItems => CurrentClient.MenuItems; public MenuItemCollection? CurrentMenuItems => CurrentClient.MenuItems;
public object? SelectedMenuItem public object? SelectedMenuItem
@@ -81,7 +81,10 @@
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<controls:SettingsExpander Description="{x:Bind ViewModel.Address, Mode=OneWay}" Header="{x:Bind ViewModel.AccountName, Mode=OneWay}"> <controls:SettingsExpander
Description="{x:Bind ViewModel.Address, Mode=OneWay}"
Header="{x:Bind ViewModel.AccountName, Mode=OneWay}"
IsExpanded="True">
<controls:SettingsExpander.HeaderIcon> <controls:SettingsExpander.HeaderIcon>
<BitmapIcon ShowAsMonochrome="False" UriSource="{x:Bind ViewModel.ProviderIconPath, Mode=OneWay}" /> <BitmapIcon ShowAsMonochrome="False" UriSource="{x:Bind ViewModel.ProviderIconPath, Mode=OneWay}" />
</controls:SettingsExpander.HeaderIcon> </controls:SettingsExpander.HeaderIcon>
+3 -134
View File
@@ -9,18 +9,17 @@
xmlns:enums="using:Wino.Core.Domain.Enums" xmlns:enums="using:Wino.Core.Domain.Enums"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="root" x:Name="root"
Title="{x:Bind domain:Translator.SettingsOptions_Title}" Title="{x:Bind domain:Translator.SettingsHome_Title}"
mc:Ignorable="d"> mc:Ignorable="d">
<ScrollViewer Padding="0,0,16,0" VerticalScrollBarVisibility="Auto"> <ScrollViewer Padding="0,0,16,0" VerticalScrollBarVisibility="Auto">
<StackPanel Margin="0,8,0,24" Spacing="4"> <StackPanel Margin="0,8,0,24" Spacing="12">
<StackPanel.ChildrenTransitions> <StackPanel.ChildrenTransitions>
<TransitionCollection> <TransitionCollection>
<EntranceThemeTransition FromVerticalOffset="50" IsStaggeringEnabled="True" /> <EntranceThemeTransition FromVerticalOffset="50" IsStaggeringEnabled="True" />
</TransitionCollection> </TransitionCollection>
</StackPanel.ChildrenTransitions> </StackPanel.ChildrenTransitions>
<!-- Hero Banner - Windows 11 style -->
<Grid <Grid
Margin="0,0,0,12" Margin="0,0,0,12"
Padding="24,28" Padding="24,28"
@@ -33,8 +32,6 @@
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<!-- App Info -->
<Grid <Grid
Grid.Column="1" Grid.Column="1"
Margin="8,0,0,0" Margin="8,0,0,0"
@@ -43,6 +40,7 @@
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<StackPanel <StackPanel
VerticalAlignment="Center" VerticalAlignment="Center"
Orientation="Horizontal" Orientation="Horizontal"
@@ -120,7 +118,6 @@
</Grid> </Grid>
</Grid> </Grid>
<!-- Account Management Card -->
<controls:SettingsCard <controls:SettingsCard
Margin="0,0,0,12" Margin="0,0,0,12"
Click="SettingOptionClicked" Click="SettingOptionClicked"
@@ -141,134 +138,6 @@
</StackPanel> </StackPanel>
</Button> </Button>
</controls:SettingsCard> </controls:SettingsCard>
<!-- General Section -->
<TextBlock
Margin="1,8,0,4"
Style="{StaticResource BodyStrongTextBlockStyle}"
Text="{x:Bind domain:Translator.SettingsOptions_GeneralSection}" />
<controls:SettingsCard
Click="SettingOptionClicked"
Description="{x:Bind domain:Translator.SettingsAppPreferences_Description}"
Header="{x:Bind domain:Translator.SettingsAppPreferences_Title}"
IsClickEnabled="True"
Tag="{x:Bind enums:WinoPage.AppPreferencesPage}">
<controls:SettingsCard.HeaderIcon>
<FontIcon Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" Glyph="&#xE770;" />
</controls:SettingsCard.HeaderIcon>
</controls:SettingsCard>
<controls:SettingsCard
Click="SettingOptionClicked"
Description="{x:Bind domain:Translator.SettingsLanguageTime_Description}"
Header="{x:Bind domain:Translator.SettingsLanguageTime_Title}"
IsClickEnabled="True"
Tag="{x:Bind enums:WinoPage.LanguageTimePage}">
<controls:SettingsCard.HeaderIcon>
<FontIcon Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" Glyph="&#xE775;" />
</controls:SettingsCard.HeaderIcon>
</controls:SettingsCard>
<controls:SettingsCard
Click="SettingOptionClicked"
Description="{x:Bind domain:Translator.SettingsPersonalization_Description}"
Header="{x:Bind domain:Translator.SettingsPersonalization_Title}"
IsClickEnabled="True"
Tag="{x:Bind enums:WinoPage.PersonalizationPage}">
<controls:SettingsCard.HeaderIcon>
<FontIcon Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" Glyph="&#xE771;" />
</controls:SettingsCard.HeaderIcon>
</controls:SettingsCard>
<controls:SettingsCard
Click="SettingOptionClicked"
Description="{x:Bind domain:Translator.SettingsAbout_Description}"
Header="{x:Bind domain:Translator.SettingsAbout_Title}"
IsClickEnabled="True"
Tag="{x:Bind enums:WinoPage.AboutPage}">
<controls:SettingsCard.HeaderIcon>
<FontIcon Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" Glyph="&#xE946;" />
</controls:SettingsCard.HeaderIcon>
</controls:SettingsCard>
<!-- Mail Section -->
<TextBlock
Margin="1,16,0,4"
Style="{StaticResource BodyStrongTextBlockStyle}"
Text="{x:Bind domain:Translator.SettingsOptions_MailSection}" />
<controls:SettingsCard
Click="SettingOptionClicked"
Description="{x:Bind domain:Translator.Settings_KeyboardShortcuts_Description}"
Header="{x:Bind domain:Translator.Settings_KeyboardShortcuts_Title}"
IsClickEnabled="True"
Tag="{x:Bind enums:WinoPage.KeyboardShortcutsPage}">
<controls:SettingsCard.HeaderIcon>
<FontIcon Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" Glyph="&#xE765;" />
</controls:SettingsCard.HeaderIcon>
</controls:SettingsCard>
<controls:SettingsCard
Click="SettingOptionClicked"
Description="{x:Bind domain:Translator.SettingsMessageList_Description}"
Header="{x:Bind domain:Translator.SettingsMessageList_Title}"
IsClickEnabled="True"
Tag="{x:Bind enums:WinoPage.MessageListPage}">
<controls:SettingsCard.HeaderIcon>
<FontIcon Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" Glyph="&#xE8C4;" />
</controls:SettingsCard.HeaderIcon>
</controls:SettingsCard>
<controls:SettingsCard
Click="SettingOptionClicked"
Description="{x:Bind domain:Translator.SettingsReadComposePane_Description}"
Header="{x:Bind domain:Translator.SettingsReadComposePane_Title}"
IsClickEnabled="True"
Tag="{x:Bind enums:WinoPage.ReadComposePanePage}">
<controls:SettingsCard.HeaderIcon>
<FontIcon Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" Glyph="&#xE8BD;" />
</controls:SettingsCard.HeaderIcon>
</controls:SettingsCard>
<controls:SettingsCard
Click="SettingOptionClicked"
Description="{x:Bind domain:Translator.SettingsSignatureAndEncryption_Description}"
Header="{x:Bind domain:Translator.SettingsSignatureAndEncryption_Title}"
IsClickEnabled="True"
Tag="{x:Bind enums:WinoPage.SignatureAndEncryptionPage}">
<controls:SettingsCard.HeaderIcon>
<FontIcon Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" Glyph="&#xE8D7;" />
</controls:SettingsCard.HeaderIcon>
</controls:SettingsCard>
<controls:SettingsCard
Click="SettingOptionClicked"
Description="{x:Bind domain:Translator.SettingsStorage_Description}"
Header="{x:Bind domain:Translator.SettingsStorage_Title}"
IsClickEnabled="True"
Tag="{x:Bind enums:WinoPage.StoragePage}">
<controls:SettingsCard.HeaderIcon>
<FontIcon Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" Glyph="&#xE81C;" />
</controls:SettingsCard.HeaderIcon>
</controls:SettingsCard>
<!-- Calendar Section -->
<TextBlock
Margin="1,16,0,4"
Style="{StaticResource BodyStrongTextBlockStyle}"
Text="{x:Bind domain:Translator.SettingsOptions_CalendarSection}" />
<controls:SettingsCard
Click="SettingOptionClicked"
Description="{x:Bind domain:Translator.SettingsCalendarSettings_Description}"
Header="{x:Bind domain:Translator.SettingsCalendarSettings_Title}"
IsClickEnabled="True"
Tag="{x:Bind enums:WinoPage.CalendarSettingsPage}">
<controls:SettingsCard.HeaderIcon>
<FontIcon Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" Glyph="&#xE787;" />
</controls:SettingsCard.HeaderIcon>
</controls:SettingsCard>
</StackPanel> </StackPanel>
</ScrollViewer> </ScrollViewer>
</abstract:SettingOptionsPageAbstract> </abstract:SettingOptionsPageAbstract>
+10 -1
View File
@@ -18,6 +18,7 @@
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
RowSpacing="20"> RowSpacing="20">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
@@ -43,7 +44,15 @@
</winuiControls:BreadcrumbBar.ItemTemplate> </winuiControls:BreadcrumbBar.ItemTemplate>
</winuiControls:BreadcrumbBar> </winuiControls:BreadcrumbBar>
<Frame x:Name="SettingsFrame" Grid.Row="1" /> <TextBlock
Grid.Row="1"
Margin="0,-4,0,4"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource BodyStrongTextBlockStyle}"
Text="{x:Bind ViewModel.CurrentDescription, Mode=OneWay}"
TextWrapping="WrapWholeWords" />
<Frame x:Name="SettingsFrame" Grid.Row="2" />
</Grid> </Grid>
</Border> </Border>
</abstract:SettingsPageAbstract> </abstract:SettingsPageAbstract>
+59 -36
View File
@@ -1,10 +1,12 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Microsoft.UI.Xaml.Media.Animation; using Microsoft.UI.Xaml.Media.Animation;
using Microsoft.UI.Xaml.Navigation; using Microsoft.UI.Xaml.Navigation;
using Wino.Core.Domain; using Wino.Core.Domain;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Settings;
using Wino.Helpers; using Wino.Helpers;
using Wino.Mail.ViewModels.Data; using Wino.Mail.ViewModels.Data;
using Wino.Messaging.Client.Navigation; using Wino.Messaging.Client.Navigation;
@@ -17,6 +19,7 @@ namespace Wino.Views;
public sealed partial class SettingsPage : SettingsPageAbstract, public sealed partial class SettingsPage : SettingsPageAbstract,
IRecipient<BreadcrumbNavigationRequested>, IRecipient<BreadcrumbNavigationRequested>,
IRecipient<BackBreadcrumNavigationRequested>, IRecipient<BackBreadcrumNavigationRequested>,
IRecipient<SettingsRootNavigationRequested>,
IRecipient<MergedInboxRenamed>, IRecipient<MergedInboxRenamed>,
IRecipient<AccountUpdatedMessage> IRecipient<AccountUpdatedMessage>
{ {
@@ -34,39 +37,9 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
// Register for frame navigation events to track back button visibility // Register for frame navigation events to track back button visibility
SettingsFrame.Navigated -= SettingsFrameNavigated; SettingsFrame.Navigated -= SettingsFrameNavigated;
SettingsFrame.Navigated += SettingsFrameNavigated; SettingsFrame.Navigated += SettingsFrameNavigated;
PageHistory.Clear();
SettingsFrame.BackStack.Clear();
SettingsFrame.ForwardStack.Clear();
SettingsFrame.Navigate(typeof(SettingOptionsPage), null, new SuppressNavigationTransitionInfo()); var initialPage = e.Parameter as WinoPage? ?? WinoPage.SettingOptionsPage;
NavigateToRootPage(initialPage);
var initialRequest = new BreadcrumbNavigationRequested(Translator.MenuSettings, WinoPage.SettingOptionsPage);
PageHistory.Add(new BreadcrumbNavigationItemViewModel(initialRequest, true, backStackDepth: SettingsFrame.BackStack.Count + 1));
if (e.Parameter is WinoPage parameterPage)
{
switch (parameterPage)
{
case WinoPage.AppPreferencesPage:
NavigateBreadcrumb(new BreadcrumbNavigationRequested(Translator.SettingsAppPreferences_Title, WinoPage.AppPreferencesPage));
break;
case WinoPage.PersonalizationPage:
NavigateBreadcrumb(new BreadcrumbNavigationRequested(Translator.SettingsPersonalization_Title, WinoPage.PersonalizationPage));
break;
case WinoPage.StoragePage:
NavigateBreadcrumb(new BreadcrumbNavigationRequested(Translator.SettingsStorage_Title, WinoPage.StoragePage));
break;
case WinoPage.EmailTemplatesPage:
NavigateBreadcrumb(new BreadcrumbNavigationRequested(Translator.SettingsEmailTemplates_Title, WinoPage.EmailTemplatesPage));
break;
case WinoPage.ManageAccountsPage:
case WinoPage.AccountManagementPage:
NavigateBreadcrumb(new BreadcrumbNavigationRequested(Translator.SettingsManageAccountSettings_Title, WinoPage.ManageAccountsPage));
break;
}
}
UpdateWindowTitle();
} }
public override void OnLanguageChanged() public override void OnLanguageChanged()
@@ -88,6 +61,7 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
manageAccountsEntry.Title = Translator.SettingsManageAccountSettings_Title; manageAccountsEntry.Title = Translator.SettingsManageAccountSettings_Title;
} }
_ = RefreshCurrentPageStateAsync();
UpdateWindowTitle(); UpdateWindowTitle();
} }
@@ -108,6 +82,7 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
WeakReferenceMessenger.Default.Register<BreadcrumbNavigationRequested>(this); WeakReferenceMessenger.Default.Register<BreadcrumbNavigationRequested>(this);
WeakReferenceMessenger.Default.Register<BackBreadcrumNavigationRequested>(this); WeakReferenceMessenger.Default.Register<BackBreadcrumNavigationRequested>(this);
WeakReferenceMessenger.Default.Register<SettingsRootNavigationRequested>(this);
WeakReferenceMessenger.Default.Register<MergedInboxRenamed>(this); WeakReferenceMessenger.Default.Register<MergedInboxRenamed>(this);
WeakReferenceMessenger.Default.Register<AccountUpdatedMessage>(this); WeakReferenceMessenger.Default.Register<AccountUpdatedMessage>(this);
} }
@@ -118,6 +93,7 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
WeakReferenceMessenger.Default.Unregister<BreadcrumbNavigationRequested>(this); WeakReferenceMessenger.Default.Unregister<BreadcrumbNavigationRequested>(this);
WeakReferenceMessenger.Default.Unregister<BackBreadcrumNavigationRequested>(this); WeakReferenceMessenger.Default.Unregister<BackBreadcrumNavigationRequested>(this);
WeakReferenceMessenger.Default.Unregister<SettingsRootNavigationRequested>(this);
WeakReferenceMessenger.Default.Unregister<MergedInboxRenamed>(this); WeakReferenceMessenger.Default.Unregister<MergedInboxRenamed>(this);
WeakReferenceMessenger.Default.Unregister<AccountUpdatedMessage>(this); WeakReferenceMessenger.Default.Unregister<AccountUpdatedMessage>(this);
} }
@@ -129,8 +105,8 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
private void SettingsFrameNavigated(object sender, NavigationEventArgs e) private void SettingsFrameNavigated(object sender, NavigationEventArgs e)
{ {
// Update back button visibility based on whether we can go back within the frame UpdateBackNavigationState();
ViewModel.StatePersistenceService.IsSettingsNavigating = SettingsFrame.CanGoBack; _ = RefreshCurrentPageStateAsync();
} }
private void GoBackFrame(Core.Domain.Enums.NavigationTransitionEffect slideEffect) private void GoBackFrame(Core.Domain.Enums.NavigationTransitionEffect slideEffect)
@@ -138,7 +114,8 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
if (!BreadcrumbNavigationHelper.GoBack(SettingsFrame, PageHistory, slideEffect)) if (!BreadcrumbNavigationHelper.GoBack(SettingsFrame, PageHistory, slideEffect))
return; return;
ViewModel.StatePersistenceService.IsSettingsNavigating = SettingsFrame.CanGoBack; UpdateBackNavigationState();
_ = RefreshCurrentPageStateAsync();
UpdateWindowTitle(); UpdateWindowTitle();
} }
@@ -147,7 +124,8 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
if (!BreadcrumbNavigationHelper.NavigateTo(SettingsFrame, PageHistory, args.Index)) if (!BreadcrumbNavigationHelper.NavigateTo(SettingsFrame, PageHistory, args.Index))
return; return;
ViewModel.StatePersistenceService.IsSettingsNavigating = SettingsFrame.CanGoBack; UpdateBackNavigationState();
_ = RefreshCurrentPageStateAsync();
UpdateWindowTitle(); UpdateWindowTitle();
} }
@@ -156,6 +134,15 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
GoBackFrame(message.SlideEffect); GoBackFrame(message.SlideEffect);
} }
public void Receive(SettingsRootNavigationRequested message)
{
var currentRootPage = SettingsNavigationInfoProvider.GetRootPage(PageHistory.LastOrDefault()?.Request.PageType ?? WinoPage.SettingOptionsPage);
if (message.PageType != WinoPage.SettingOptionsPage && currentRootPage == message.PageType)
return;
NavigateToRootPage(message.PageType);
}
public void Receive(AccountUpdatedMessage message) public void Receive(AccountUpdatedMessage message)
{ {
var activePage = PageHistory.LastOrDefault(a => a.Request.PageType == WinoPage.AccountDetailsPage); var activePage = PageHistory.LastOrDefault(a => a.Request.PageType == WinoPage.AccountDetailsPage);
@@ -166,6 +153,7 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
DispatcherQueue.TryEnqueue(() => DispatcherQueue.TryEnqueue(() =>
{ {
activePage.Title = message.Account.Name; activePage.Title = message.Account.Name;
_ = RefreshCurrentPageStateAsync();
UpdateWindowTitle(); UpdateWindowTitle();
}); });
} }
@@ -180,6 +168,7 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
DispatcherQueue.TryEnqueue(() => DispatcherQueue.TryEnqueue(() =>
{ {
activePage.Title = message.NewName; activePage.Title = message.NewName;
_ = RefreshCurrentPageStateAsync();
UpdateWindowTitle(); UpdateWindowTitle();
}); });
} }
@@ -189,9 +178,43 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
if (!BreadcrumbNavigationHelper.Navigate(SettingsFrame, PageHistory, message, ViewModel.NavigationService.GetPageType)) if (!BreadcrumbNavigationHelper.Navigate(SettingsFrame, PageHistory, message, ViewModel.NavigationService.GetPageType))
return; return;
UpdateBackNavigationState();
_ = RefreshCurrentPageStateAsync();
UpdateWindowTitle(); UpdateWindowTitle();
} }
private void NavigateToRootPage(WinoPage targetPage)
{
PageHistory.Clear();
SettingsFrame.BackStack.Clear();
SettingsFrame.ForwardStack.Clear();
NavigateBreadcrumb(new BreadcrumbNavigationRequested(Translator.MenuSettings, WinoPage.SettingOptionsPage));
if (targetPage != WinoPage.SettingOptionsPage)
{
NavigateBreadcrumb(new BreadcrumbNavigationRequested(
SettingsNavigationInfoProvider.GetPageTitle(targetPage),
targetPage));
return;
}
UpdateWindowTitle();
}
private void UpdateBackNavigationState()
{
ViewModel.StatePersistenceService.IsSettingsNavigating = PageHistory.Count > 1 && SettingsFrame.CanGoBack;
}
private async Task RefreshCurrentPageStateAsync()
{
var activePage = PageHistory.LastOrDefault()?.Request.PageType ?? WinoPage.SettingOptionsPage;
var rootPage = SettingsNavigationInfoProvider.GetRootPage(activePage);
await ViewModel.UpdateActivePageAsync(rootPage);
WeakReferenceMessenger.Default.Send(new ActiveSettingsPageChanged(rootPage));
}
private void UpdateWindowTitle() private void UpdateWindowTitle()
{ {
var activeTitle = PageHistory.LastOrDefault()?.Title; var activeTitle = PageHistory.LastOrDefault()?.Title;
+2
View File
@@ -425,6 +425,8 @@
NewMailTemplate="{StaticResource CreateNewMailTemplate}" NewMailTemplate="{StaticResource CreateNewMailTemplate}"
RatingItemTemplate="{StaticResource RatingItemTemplate}" RatingItemTemplate="{StaticResource RatingItemTemplate}"
SeperatorTemplate="{StaticResource SeperatorTemplate}" SeperatorTemplate="{StaticResource SeperatorTemplate}"
SettingsShellPageItemTemplate="{StaticResource SettingsShellPageItemTemplate}"
SettingsShellSectionItemTemplate="{StaticResource SettingsShellSectionItemTemplate}"
StoreUpdateItemTemplate="{StaticResource StoreUpdateItemTemplate}" /> StoreUpdateItemTemplate="{StaticResource StoreUpdateItemTemplate}" />
<Style <Style
+47 -14
View File
@@ -50,6 +50,7 @@ public sealed partial class WinoAppShell : Views.Abstract.WinoAppShellAbstract,
private const string StateDefaultShellContent = "DefaultShellContentState"; private const string StateDefaultShellContent = "DefaultShellContentState";
private const string StateEventDetailsContent = "EventDetailsContentState"; private const string StateEventDetailsContent = "EventDetailsContentState";
private WinoApplicationMode? _activeMode; private WinoApplicationMode? _activeMode;
private bool _isSyncingNavigationViewSelection;
public WinoAppShell() public WinoAppShell()
{ {
@@ -59,9 +60,11 @@ public sealed partial class WinoAppShell : Views.Abstract.WinoAppShellAbstract,
ViewModel.MailClient.Dispatcher = pageDispatcher; ViewModel.MailClient.Dispatcher = pageDispatcher;
ViewModel.CalendarClient.Dispatcher = pageDispatcher; ViewModel.CalendarClient.Dispatcher = pageDispatcher;
ViewModel.GetClient(WinoApplicationMode.Contacts).Dispatcher = pageDispatcher; ViewModel.GetClient(WinoApplicationMode.Contacts).Dispatcher = pageDispatcher;
ViewModel.GetClient(WinoApplicationMode.Settings).Dispatcher = pageDispatcher;
ViewModel.MailClient.PropertyChanged += MailClientPropertyChanged; ViewModel.MailClient.PropertyChanged += MailClientPropertyChanged;
ViewModel.CalendarClient.PropertyChanged += CalendarClientPropertyChanged; ViewModel.CalendarClient.PropertyChanged += CalendarClientPropertyChanged;
ViewModel.PropertyChanged += ViewModelPropertyChanged;
ViewModel.StatePersistenceService.StatePropertyChanged += StatePersistenceServiceChanged; ViewModel.StatePersistenceService.StatePropertyChanged += StatePersistenceServiceChanged;
CalendarTypeSelector.RegisterPropertyChangedCallback(WinoCalendarTypeSelectorControl.SelectedTypeProperty, CalendarTypeSelectorSelectedTypeChanged); CalendarTypeSelector.RegisterPropertyChangedCallback(WinoCalendarTypeSelectorControl.SelectedTypeProperty, CalendarTypeSelectorSelectedTypeChanged);
@@ -151,6 +154,10 @@ public sealed partial class WinoAppShell : Views.Abstract.WinoAppShellAbstract,
{ {
ViewModel.CurrentClient.Deactivate(); ViewModel.CurrentClient.Deactivate();
} }
else if (_activeMode == WinoApplicationMode.Settings)
{
ViewModel.CurrentClient.Deactivate();
}
DynamicPageShellContentPresenter.Content = null; DynamicPageShellContentPresenter.Content = null;
} }
@@ -229,12 +236,9 @@ public sealed partial class WinoAppShell : Views.Abstract.WinoAppShellAbstract,
private void RefreshNavigationViewBindings(bool syncMailSelection = true) private void RefreshNavigationViewBindings(bool syncMailSelection = true)
{ {
navigationView.MenuItemsSource = ViewModel.CurrentMenuItems; navigationView.MenuItemsSource = ViewModel.CurrentMenuItems;
SetNavigationViewSelectedItem(ViewModel.CurrentClient.HandlesNavigationSelection && syncMailSelection
navigationView.SelectionChanged -= MenuSelectionChanged;
navigationView.SelectedItem = ViewModel.CurrentClient.HandlesNavigationSelection && syncMailSelection
? ViewModel.SelectedMenuItem ? ViewModel.SelectedMenuItem
: null; : null);
navigationView.SelectionChanged += MenuSelectionChanged;
} }
private void UpdateEventDetailsVisualState() private void UpdateEventDetailsVisualState()
@@ -262,6 +266,9 @@ public sealed partial class WinoAppShell : Views.Abstract.WinoAppShellAbstract,
private async void NavigationViewItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args) private async void NavigationViewItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
{ {
if (_isSyncingNavigationViewSelection)
return;
if (ViewModel.IsCalendarMode) if (ViewModel.IsCalendarMode)
{ {
if (args.InvokedItemContainer is FrameworkElement { DataContext: IMenuItem menuItem }) if (args.InvokedItemContainer is FrameworkElement { DataContext: IMenuItem menuItem })
@@ -283,7 +290,13 @@ public sealed partial class WinoAppShell : Views.Abstract.WinoAppShellAbstract,
private async void MenuSelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args) private async void MenuSelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
{ {
if (!ViewModel.IsMailMode) if (_isSyncingNavigationViewSelection)
return;
if (!ViewModel.CurrentClient.HandlesNavigationSelection)
return;
if (ReferenceEquals(args.SelectedItem, ViewModel.SelectedMenuItem))
return; return;
if (args.SelectedItem is IMenuItem invokedMenuItem) if (args.SelectedItem is IMenuItem invokedMenuItem)
@@ -306,7 +319,7 @@ public sealed partial class WinoAppShell : Views.Abstract.WinoAppShellAbstract,
{ {
foundMenuItem.Expand(); foundMenuItem.Expand();
await ViewModel.MailClient.NavigateFolderAsync(foundMenuItem); await ViewModel.MailClient.NavigateFolderAsync(foundMenuItem);
navigationView.SelectedItem = foundMenuItem; SetNavigationViewSelectedItem(foundMenuItem);
if (message.NavigateMailItem != null) if (message.NavigateMailItem != null)
{ {
@@ -327,7 +340,7 @@ public sealed partial class WinoAppShell : Views.Abstract.WinoAppShellAbstract,
{ {
accountFolderMenuItem.Expand(); accountFolderMenuItem.Expand();
await ViewModel.MailClient.NavigateFolderAsync(accountFolderMenuItem); await ViewModel.MailClient.NavigateFolderAsync(accountFolderMenuItem);
navigationView.SelectedItem = accountFolderMenuItem; SetNavigationViewSelectedItem(accountFolderMenuItem);
WeakReferenceMessenger.Default.Send(new MailItemNavigationRequested(message.NavigateMailItem.UniqueId, ScrollToItem: true)); WeakReferenceMessenger.Default.Send(new MailItemNavigationRequested(message.NavigateMailItem.UniqueId, ScrollToItem: true));
} }
} }
@@ -345,9 +358,7 @@ public sealed partial class WinoAppShell : Views.Abstract.WinoAppShellAbstract,
ViewModel.NavigationService.Navigate(WinoPage.MailListPage, navigateFolderArgs, NavigationReferenceFrame.InnerShellFrame); ViewModel.NavigationService.Navigate(WinoPage.MailListPage, navigateFolderArgs, NavigationReferenceFrame.InnerShellFrame);
navigationView.SelectionChanged -= MenuSelectionChanged; SetNavigationViewSelectedItem(message.BaseFolderMenuItem);
navigationView.SelectedItem = message.BaseFolderMenuItem;
navigationView.SelectionChanged += MenuSelectionChanged;
} }
else else
{ {
@@ -430,9 +441,7 @@ public sealed partial class WinoAppShell : Views.Abstract.WinoAppShellAbstract,
{ {
if (e.PropertyName == nameof(IShellClient.SelectedMenuItem) && ViewModel.IsMailMode) if (e.PropertyName == nameof(IShellClient.SelectedMenuItem) && ViewModel.IsMailMode)
{ {
navigationView.SelectionChanged -= MenuSelectionChanged; SetNavigationViewSelectedItem(ViewModel.MailClient.SelectedMenuItem);
navigationView.SelectedItem = ViewModel.MailClient.SelectedMenuItem;
navigationView.SelectionChanged += MenuSelectionChanged;
} }
} }
@@ -467,6 +476,30 @@ public sealed partial class WinoAppShell : Views.Abstract.WinoAppShellAbstract,
} }
} }
private void ViewModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName != nameof(ViewModel.SelectedMenuItem) || !ViewModel.CurrentClient.HandlesNavigationSelection)
return;
SetNavigationViewSelectedItem(ViewModel.SelectedMenuItem);
}
private void SetNavigationViewSelectedItem(object? item)
{
if (ReferenceEquals(navigationView.SelectedItem, item))
return;
_isSyncingNavigationViewSelection = true;
try
{
navigationView.SelectedItem = item;
}
finally
{
_isSyncingNavigationViewSelection = false;
}
}
private void StatePersistenceServiceChanged(object? sender, string propertyName) private void StatePersistenceServiceChanged(object? sender, string propertyName)
{ {
if (propertyName == nameof(IStatePersistanceService.CalendarDisplayType)) if (propertyName == nameof(IStatePersistanceService.CalendarDisplayType))
@@ -0,0 +1,5 @@
using Wino.Core.Domain.Enums;
namespace Wino.Messaging.Client.Navigation;
public sealed record ActiveSettingsPageChanged(WinoPage RootPage);
@@ -0,0 +1,5 @@
using Wino.Core.Domain.Enums;
namespace Wino.Messaging.Client.Navigation;
public sealed record SettingsRootNavigationRequested(WinoPage PageType);