Live store update notifications.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
@@ -67,6 +67,9 @@ public partial class CalendarAppShellViewModel : CalendarBaseViewModel,
|
||||
|
||||
public bool IsVerticalCalendar => StatePersistenceService.CalendarDisplayType == CalendarDisplayType.Month;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isStoreUpdateItemVisible;
|
||||
|
||||
// For updating account calendars asynchronously.
|
||||
private SemaphoreSlim _accountCalendarUpdateSemaphoreSlim = new(1);
|
||||
|
||||
@@ -77,12 +80,14 @@ public partial class CalendarAppShellViewModel : CalendarBaseViewModel,
|
||||
IAccountCalendarStateService accountCalendarStateService,
|
||||
INavigationService navigationService,
|
||||
IMailDialogService dialogService,
|
||||
IUpdateManager updateManager)
|
||||
IUpdateManager updateManager,
|
||||
IStoreUpdateService storeUpdateService)
|
||||
{
|
||||
_accountService = accountService;
|
||||
_calendarService = calendarService;
|
||||
_dialogService = dialogService;
|
||||
_updateManager = updateManager;
|
||||
_storeUpdateService = storeUpdateService;
|
||||
|
||||
AccountCalendarStateService = accountCalendarStateService;
|
||||
AccountCalendarStateService.AccountCalendarSelectionStateChanged += UpdateAccountCalendarRequested;
|
||||
@@ -100,6 +105,7 @@ public partial class CalendarAppShellViewModel : CalendarBaseViewModel,
|
||||
base.OnDispatcherAssigned();
|
||||
|
||||
AccountCalendarStateService.Dispatcher = Dispatcher;
|
||||
_ = RefreshFooterItemsAsync(false);
|
||||
}
|
||||
|
||||
private void PrefefencesChanged(object sender, string e)
|
||||
@@ -115,10 +121,23 @@ public partial class CalendarAppShellViewModel : CalendarBaseViewModel,
|
||||
}
|
||||
}
|
||||
|
||||
private async void PreferencesServiceChanged(object sender, string e)
|
||||
{
|
||||
if (e == nameof(IPreferencesService.IsStoreUpdateNotificationsEnabled))
|
||||
{
|
||||
await RefreshFooterItemsAsync(false);
|
||||
}
|
||||
}
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
PreferencesService.PreferenceChanged -= PreferencesServiceChanged;
|
||||
PreferencesService.PreferenceChanged += PreferencesServiceChanged;
|
||||
|
||||
await RefreshFooterItemsAsync(mode == NavigationMode.New);
|
||||
|
||||
// Preserve the existing calendar shell frame state when the user switches
|
||||
// between Mail and Calendar modes. Back/forward restoration should not
|
||||
// force a new CalendarPage navigation, otherwise pages like
|
||||
@@ -141,6 +160,13 @@ public partial class CalendarAppShellViewModel : CalendarBaseViewModel,
|
||||
TodayClicked();
|
||||
}
|
||||
|
||||
public override void OnNavigatedFrom(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedFrom(mode, parameters);
|
||||
|
||||
PreferencesService.PreferenceChanged -= PreferencesServiceChanged;
|
||||
}
|
||||
|
||||
private async Task ShowWhatIsNewIfNeededAsync()
|
||||
{
|
||||
if (!_updateManager.ShouldShowUpdateNotes())
|
||||
@@ -154,6 +180,22 @@ public partial class CalendarAppShellViewModel : CalendarBaseViewModel,
|
||||
await _dialogService.ShowWhatIsNewDialogAsync(notes);
|
||||
}
|
||||
|
||||
private async Task RefreshFooterItemsAsync(bool showNotification)
|
||||
{
|
||||
await _storeUpdateService.RefreshAvailabilityAsync(showNotification).ConfigureAwait(false);
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
IsStoreUpdateItemVisible = _storeUpdateService.HasAvailableUpdate && PreferencesService.IsStoreUpdateNotificationsEnabled;
|
||||
});
|
||||
}
|
||||
|
||||
private async Task StartStoreUpdateAsync()
|
||||
{
|
||||
await _storeUpdateService.StartUpdateAsync().ConfigureAwait(false);
|
||||
await RefreshFooterItemsAsync(false).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async void AccountCalendarStateCollectivelyChanged(object sender, GroupedAccountCalendarViewModel e)
|
||||
{
|
||||
// When using three-state checkbox, multiple accounts will be selected/unselected at the same time.
|
||||
@@ -211,40 +253,34 @@ public partial class CalendarAppShellViewModel : CalendarBaseViewModel,
|
||||
|
||||
private void ForceNavigateCalendarDate()
|
||||
{
|
||||
if (SelectedMenuItemIndex == -1)
|
||||
var args = new CalendarPageNavigationArgs()
|
||||
{
|
||||
var args = new CalendarPageNavigationArgs()
|
||||
{
|
||||
NavigationDate = _navigationDate ?? DateTime.Now.Date
|
||||
};
|
||||
NavigationDate = _navigationDate ?? DateTime.Now.Date
|
||||
};
|
||||
|
||||
// Already on calendar. Just navigate.
|
||||
NavigationService.Navigate(WinoPage.CalendarPage, args);
|
||||
|
||||
_navigationDate = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedMenuItemIndex = -1;
|
||||
}
|
||||
NavigationService.Navigate(WinoPage.CalendarPage, args);
|
||||
_navigationDate = null;
|
||||
}
|
||||
|
||||
partial void OnSelectedMenuItemIndexChanged(int oldValue, int newValue)
|
||||
{
|
||||
switch (newValue)
|
||||
if (newValue < 0)
|
||||
return;
|
||||
|
||||
if (newValue == 0)
|
||||
{
|
||||
case -1:
|
||||
ForceNavigateCalendarDate();
|
||||
break;
|
||||
case 0:
|
||||
NavigationService.Navigate(WinoPage.ManageAccountsPage);
|
||||
break;
|
||||
case 1:
|
||||
NavigationService.Navigate(WinoPage.SettingsPage);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
NavigationService.Navigate(WinoPage.ManageAccountsPage);
|
||||
}
|
||||
else if (newValue == 1)
|
||||
{
|
||||
NavigationService.Navigate(WinoPage.SettingsPage);
|
||||
}
|
||||
else if (IsStoreUpdateItemVisible && newValue == 2)
|
||||
{
|
||||
_ = StartStoreUpdateAsync();
|
||||
}
|
||||
|
||||
SelectedMenuItemIndex = -1;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -301,6 +337,7 @@ public partial class CalendarAppShellViewModel : CalendarBaseViewModel,
|
||||
private readonly ICalendarService _calendarService;
|
||||
private readonly IMailDialogService _dialogService;
|
||||
private readonly IUpdateManager _updateManager;
|
||||
private readonly IStoreUpdateService _storeUpdateService;
|
||||
|
||||
#region Commands
|
||||
|
||||
@@ -476,7 +513,7 @@ public partial class CalendarAppShellViewModel : CalendarBaseViewModel,
|
||||
public async void Receive(CalendarEnableStatusChangedMessage message)
|
||||
=> await ExecuteUIThread(() => IsCalendarEnabled = message.IsEnabled);
|
||||
|
||||
public void Receive(NavigateManageAccountsRequested message) => SelectedMenuItemIndex = 1;
|
||||
public void Receive(NavigateManageAccountsRequested message) => SelectedMenuItemIndex = 0;
|
||||
|
||||
public void Receive(CalendarDisplayTypeChangedMessage message)
|
||||
{
|
||||
@@ -539,3 +576,12 @@ public partial class CalendarAppShellViewModel : CalendarBaseViewModel,
|
||||
return (startDate, startDate.AddMinutes(30));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Wino.Core.Domain;
|
||||
namespace Wino.Core.Domain;
|
||||
|
||||
public static class Constants
|
||||
{
|
||||
@@ -21,6 +21,8 @@ public static class Constants
|
||||
public const string ToastModeKey = nameof(ToastModeKey);
|
||||
public const string ToastModeMail = nameof(ToastModeMail);
|
||||
public const string ToastModeCalendar = nameof(ToastModeCalendar);
|
||||
public const string ToastStoreUpdateActionKey = nameof(ToastStoreUpdateActionKey);
|
||||
public const string ToastStoreUpdateActionInstall = nameof(ToastStoreUpdateActionInstall);
|
||||
public const string ClientLogFile = "Client_.log";
|
||||
public const string ServerLogFile = "Server_.log";
|
||||
public const string LogArchiveFileName = "WinoLogs.zip";
|
||||
@@ -28,3 +30,4 @@ public static class Constants
|
||||
public const string WinoMailIdentiifer = nameof(WinoMailIdentiifer);
|
||||
public const string WinoCalendarIdentifier = nameof(WinoCalendarIdentifier);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,8 +36,14 @@ public interface INotificationBuilder
|
||||
/// </summary>
|
||||
void CreateWebView2RuntimeMissingNotification();
|
||||
|
||||
/// <summary>
|
||||
/// Shows a notification when a Microsoft Store update is available.
|
||||
/// </summary>
|
||||
void CreateStoreUpdateNotification();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a calendar reminder toast for the specified calendar item.
|
||||
/// </summary>
|
||||
Task CreateCalendarReminderNotificationAsync(CalendarItem calendarItem, long reminderDurationInSeconds);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
@@ -57,6 +57,11 @@ public interface IPreferencesService : INotifyPropertyChanged
|
||||
/// </summary>
|
||||
WinoApplicationMode DefaultApplicationMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Setting: Whether Microsoft Store update notifications should be shown.
|
||||
/// </summary>
|
||||
bool IsStoreUpdateNotificationsEnabled { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Mail
|
||||
@@ -241,3 +246,4 @@ public interface IPreferencesService : INotifyPropertyChanged
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Wino.Core.Domain.Interfaces;
|
||||
|
||||
public interface IStoreUpdateService
|
||||
{
|
||||
bool HasAvailableUpdate { get; }
|
||||
|
||||
Task<bool> RefreshAvailabilityAsync(bool showNotification = false);
|
||||
|
||||
Task<bool> StartUpdateAsync();
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace Wino.Core.Domain.MenuItems;
|
||||
|
||||
public class StoreUpdateMenuItem : MenuItemBase { }
|
||||
@@ -599,6 +599,7 @@
|
||||
"MenuNewMail": "New Mail",
|
||||
"MenuRate": "Rate Wino",
|
||||
"MenuSettings": "Settings",
|
||||
"MenuUpdateAvailable": "Update Available",
|
||||
"MergedAccountCommonFolderArchive": "Archive",
|
||||
"MergedAccountCommonFolderDraft": "Draft",
|
||||
"MergedAccountCommonFolderInbox": "Inbox",
|
||||
@@ -622,6 +623,8 @@
|
||||
"Notifications_MultipleNotificationsTitle": "New Mail",
|
||||
"Notifications_WinoUpdatedMessage": "Checkout new version {0}",
|
||||
"Notifications_WinoUpdatedTitle": "Wino Mail has been updated.",
|
||||
"Notifications_StoreUpdateAvailableTitle": "Update available",
|
||||
"Notifications_StoreUpdateAvailableMessage": "A newer version of Wino Mail is ready to install from Microsoft Store.",
|
||||
"OnlineSearchFailed_Message": "Failed to perform search\n{0}\n\nListing offline mails.",
|
||||
"OnlineSearchTry_Line1": "Can't find what you are looking for?",
|
||||
"OnlineSearchTry_Line2": "Try online search.",
|
||||
@@ -700,6 +703,8 @@
|
||||
"SettingsAppPreferences_StartupBehavior_FatalError": "Fatal error occurred while changing the startup mode for Wino Mail.",
|
||||
"SettingsAppPreferences_StartupBehavior_Title": "Start minimized on Windows startup",
|
||||
"SettingsAppPreferences_Title": "App Preferences",
|
||||
"SettingsAppPreferences_StoreUpdateNotifications_Title": "Store update notifications",
|
||||
"SettingsAppPreferences_StoreUpdateNotifications_Description": "Show notifications and footer actions when a Microsoft Store update is available.",
|
||||
"SettingsAutoSelectNextItem_Description": "Select the next item after you delete or move a mail.",
|
||||
"SettingsAutoSelectNextItem_Title": "Auto select next item",
|
||||
"SettingsAvailableThemes_Description": "Select a theme from Wino's own collection for your taste or apply your own themes.",
|
||||
@@ -1117,3 +1122,5 @@
|
||||
"AccountSetup_TryAgainButton": "Try Again",
|
||||
"ImapCalDavSettings_AutoDiscoveryFailed": "Auto-discovery failed. Please enter settings manually in the Advanced tab."
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
@@ -55,6 +55,7 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
||||
private readonly SettingsItem SettingsItem = new SettingsItem();
|
||||
private readonly ManageAccountsMenuItem ManageAccountsMenuItem = new ManageAccountsMenuItem();
|
||||
private readonly ContactsMenuItem ContactsMenuItem = new ContactsMenuItem();
|
||||
private readonly StoreUpdateMenuItem StoreUpdateMenuItem = new StoreUpdateMenuItem();
|
||||
|
||||
public IMenuItem CreateMailMenuItem = new NewMailMenuItem();
|
||||
|
||||
@@ -79,6 +80,7 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
||||
private readonly IMimeFileService _mimeFileService;
|
||||
private readonly IWebView2RuntimeValidatorService _webView2RuntimeValidatorService;
|
||||
private readonly IUpdateManager _updateManager;
|
||||
private readonly IStoreUpdateService _storeUpdateService;
|
||||
|
||||
private readonly INativeAppService _nativeAppService;
|
||||
private readonly IMailService _mailService;
|
||||
@@ -102,7 +104,8 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
||||
IConfigurationService configurationService,
|
||||
IStartupBehaviorService startupBehaviorService,
|
||||
IWebView2RuntimeValidatorService webView2RuntimeValidatorService,
|
||||
IUpdateManager updateManager)
|
||||
IUpdateManager updateManager,
|
||||
IStoreUpdateService storeUpdateService)
|
||||
{
|
||||
StatePersistenceService = statePersistanceService;
|
||||
|
||||
@@ -124,6 +127,7 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
||||
_winoRequestDelegator = winoRequestDelegator;
|
||||
_webView2RuntimeValidatorService = webView2RuntimeValidatorService;
|
||||
_updateManager = updateManager;
|
||||
_storeUpdateService = storeUpdateService;
|
||||
}
|
||||
|
||||
protected override void OnDispatcherAssigned()
|
||||
@@ -142,8 +146,10 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
||||
return _contextMenuItemService.GetFolderContextMenuActions(folder);
|
||||
}
|
||||
|
||||
private async Task CreateFooterItemsAsync()
|
||||
private async Task CreateFooterItemsAsync(bool showNotification = false)
|
||||
{
|
||||
await _storeUpdateService.RefreshAvailabilityAsync(showNotification).ConfigureAwait(false);
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
// TODO: Selected footer item container still remains selected after re-creation.
|
||||
@@ -159,6 +165,12 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
||||
|
||||
FooterItems.Add(ContactsMenuItem);
|
||||
FooterItems.Add(ManageAccountsMenuItem);
|
||||
|
||||
if (_storeUpdateService.HasAvailableUpdate && PreferencesService.IsStoreUpdateNotificationsEnabled)
|
||||
{
|
||||
FooterItems.Add(StoreUpdateMenuItem);
|
||||
}
|
||||
|
||||
FooterItems.Add(SettingsItem);
|
||||
});
|
||||
}
|
||||
@@ -223,10 +235,21 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
||||
}
|
||||
}
|
||||
|
||||
private async void PreferencesServiceChanged(object sender, string e)
|
||||
{
|
||||
if (e == nameof(IPreferencesService.IsStoreUpdateNotificationsEnabled))
|
||||
{
|
||||
await CreateFooterItemsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
PreferencesService.PreferenceChanged -= PreferencesServiceChanged;
|
||||
PreferencesService.PreferenceChanged += PreferencesServiceChanged;
|
||||
|
||||
if (mode == NavigationMode.Back)
|
||||
{
|
||||
// Preserve current mail/folder selection and active rendering page when
|
||||
@@ -243,7 +266,7 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
||||
return;
|
||||
}
|
||||
|
||||
await CreateFooterItemsAsync();
|
||||
await CreateFooterItemsAsync(true);
|
||||
|
||||
await RecreateMenuItemsAsync();
|
||||
await ProcessLaunchOptionsAsync();
|
||||
@@ -268,6 +291,13 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnNavigatedFrom(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedFrom(mode, parameters);
|
||||
|
||||
PreferencesService.PreferenceChanged -= PreferencesServiceChanged;
|
||||
}
|
||||
|
||||
private async Task ShowWhatIsNewIfNeededAsync()
|
||||
{
|
||||
if (!_updateManager.ShouldShowUpdateNotes())
|
||||
@@ -638,6 +668,11 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
||||
// Don't navigate to merged account if it's already selected. Preserve user's already selected folder.
|
||||
await ChangeLoadedAccountAsync(clickedMergedAccountMenuItem, true);
|
||||
}
|
||||
else if (clickedMenuItem is StoreUpdateMenuItem)
|
||||
{
|
||||
await _storeUpdateService.StartUpdateAsync().ConfigureAwait(false);
|
||||
await CreateFooterItemsAsync().ConfigureAwait(false);
|
||||
}
|
||||
else if (clickedMenuItem is SettingsItem)
|
||||
{
|
||||
NavigationService.Navigate(WinoPage.SettingsPage, parameter, NavigationReferenceFrame.InnerShellFrame, NavigationTransitionType.None);
|
||||
@@ -1051,7 +1086,7 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
||||
|
||||
public async void Receive(LanguageChanged message)
|
||||
{
|
||||
await CreateFooterItemsAsync();
|
||||
await CreateFooterItemsAsync(true);
|
||||
await RecreateMenuItemsAsync();
|
||||
await RestoreSelectedAccountAfterMenuRefreshAsync(false);
|
||||
}
|
||||
@@ -1245,3 +1280,10 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -296,6 +296,13 @@ public partial class App : WinoApplication,
|
||||
{
|
||||
var toastArguments = ToastArguments.Parse(toastArgs.Argument);
|
||||
|
||||
if (toastArguments.TryGetValue(Constants.ToastStoreUpdateActionKey, out string storeUpdateAction) &&
|
||||
storeUpdateAction == Constants.ToastStoreUpdateActionInstall)
|
||||
{
|
||||
await HandleStoreUpdateToastAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check calendar reminder toast activation first.
|
||||
if (toastArguments.TryGetValue(Constants.ToastCalendarActionKey, out string calendarAction) &&
|
||||
toastArguments.TryGetValue(Constants.ToastCalendarItemIdKey, out string calendarItemIdString) &&
|
||||
@@ -333,6 +340,21 @@ public partial class App : WinoApplication,
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleStoreUpdateToastAsync()
|
||||
{
|
||||
if (!IsAppRunning())
|
||||
{
|
||||
await CreateAndActivateWindow(null!);
|
||||
}
|
||||
else
|
||||
{
|
||||
EnsureMainWindowVisibleAndForeground();
|
||||
}
|
||||
|
||||
var storeUpdateService = Services.GetRequiredService<IStoreUpdateService>();
|
||||
await storeUpdateService.StartUpdateAsync();
|
||||
}
|
||||
|
||||
private async Task HandleCalendarToastNavigationAsync(Guid calendarItemId)
|
||||
{
|
||||
var calendarService = Services.GetRequiredService<ICalendarService>();
|
||||
@@ -997,3 +1019,5 @@ public partial class App : WinoApplication,
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ public static class CoreUWPContainerSetup
|
||||
services.AddTransient<IConfigurationService, ConfigurationService>();
|
||||
services.AddTransient<IFileService, FileService>();
|
||||
services.AddTransient<IStoreRatingService, StoreRatingService>();
|
||||
services.AddSingleton<IStoreUpdateService, StoreUpdateService>();
|
||||
services.AddTransient<IKeyPressService, KeyPressService>();
|
||||
services.AddTransient<IWebView2RuntimeValidatorService, WebView2RuntimeValidatorService>();
|
||||
services.AddTransient<INotificationBuilder, NotificationBuilder>();
|
||||
@@ -53,3 +54,5 @@ public static class CoreUWPContainerSetup
|
||||
services.AddTransient(typeof(KeyboardShortcutsPageViewModel));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -387,7 +387,8 @@
|
||||
NewMailTemplate="{StaticResource CreateNewMailTemplate}"
|
||||
RatingItemTemplate="{StaticResource RatingItemTemplate}"
|
||||
SeperatorTemplate="{StaticResource SeperatorTemplate}"
|
||||
SettingsItemTemplate="{StaticResource SettingsItemTemplate}" />
|
||||
SettingsItemTemplate="{StaticResource SettingsItemTemplate}"
|
||||
StoreUpdateItemTemplate="{StaticResource StoreUpdateItemTemplate}" />
|
||||
</Page.Resources>
|
||||
|
||||
<Grid
|
||||
@@ -477,3 +478,5 @@
|
||||
|
||||
</Grid>
|
||||
</abstract:MailAppShellAbstract>
|
||||
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ public partial class NavigationMenuTemplateSelector : DataTemplateSelector
|
||||
public DataTemplate MergedAccountMoreExpansionItemTemplate { get; set; } = null!;
|
||||
public DataTemplate FolderMenuTemplate { get; set; } = null!;
|
||||
public DataTemplate SettingsItemTemplate { get; set; } = null!;
|
||||
public DataTemplate StoreUpdateItemTemplate { get; set; } = null!;
|
||||
public DataTemplate MoreItemsFolderTemplate { get; set; } = null!;
|
||||
public DataTemplate RatingItemTemplate { get; set; } = null!;
|
||||
public DataTemplate CreateNewFolderTemplate { get; set; } = null!;
|
||||
@@ -32,6 +33,8 @@ public partial class NavigationMenuTemplateSelector : DataTemplateSelector
|
||||
return ContactsMenuItemTemplate;
|
||||
else if (item is SettingsItem)
|
||||
return SettingsItemTemplate;
|
||||
else if (item is StoreUpdateMenuItem)
|
||||
return StoreUpdateItemTemplate;
|
||||
else if (item is SeperatorItem)
|
||||
return SeperatorTemplate;
|
||||
else if (item is AccountMenuItem)
|
||||
@@ -55,3 +58,5 @@ public partial class NavigationMenuTemplateSelector : DataTemplateSelector
|
||||
return MenuItemTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -293,6 +293,19 @@ public class NotificationBuilder : INotificationBuilder
|
||||
ShowToast(builder);
|
||||
}
|
||||
|
||||
public void CreateStoreUpdateNotification()
|
||||
{
|
||||
var builder = new ToastContentBuilder();
|
||||
builder.SetToastScenario(ToastScenario.Default);
|
||||
|
||||
builder.AddText(Translator.Notifications_StoreUpdateAvailableTitle);
|
||||
builder.AddText(Translator.Notifications_StoreUpdateAvailableMessage);
|
||||
builder.AddArgument(Constants.ToastStoreUpdateActionKey, Constants.ToastStoreUpdateActionInstall);
|
||||
builder.AddButton(GetDismissButton());
|
||||
|
||||
ShowToast(builder, "store-update-available");
|
||||
}
|
||||
|
||||
public Task CreateCalendarReminderNotificationAsync(CalendarItem calendarItem, long reminderDurationInSeconds)
|
||||
{
|
||||
if (calendarItem == null)
|
||||
@@ -437,3 +450,4 @@ public class NotificationBuilder : INotificationBuilder
|
||||
return SupportedIconScales.OrderBy(s => Math.Abs(s - requestedScale)).First();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -308,6 +308,11 @@ public class PreferencesService(IConfigurationService configurationService) : Ob
|
||||
set => SetPropertyAndSave(nameof(EmailSyncIntervalMinutes), value);
|
||||
}
|
||||
|
||||
public bool IsStoreUpdateNotificationsEnabled
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsStoreUpdateNotificationsEnabled), true);
|
||||
set => SetPropertyAndSave(nameof(IsStoreUpdateNotificationsEnabled), value);
|
||||
}
|
||||
public WinoApplicationMode DefaultApplicationMode
|
||||
{
|
||||
get
|
||||
@@ -357,3 +362,5 @@ public class PreferencesService(IConfigurationService configurationService) : Ob
|
||||
return daysOfWeek;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Services.Store;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Mail.WinUI.Services;
|
||||
|
||||
public class StoreUpdateService : IStoreUpdateService
|
||||
{
|
||||
private const string NotificationShownKeyFormat = "StoreUpdateNotificationShown_{0}";
|
||||
|
||||
private readonly IConfigurationService _configurationService;
|
||||
private readonly INotificationBuilder _notificationBuilder;
|
||||
private readonly IPreferencesService _preferencesService;
|
||||
private readonly INativeAppService _nativeAppService;
|
||||
private readonly SemaphoreSlim _refreshSemaphore = new(1, 1);
|
||||
private readonly StoreContext _storeContext = StoreContext.GetDefault();
|
||||
|
||||
public bool HasAvailableUpdate { get; private set; }
|
||||
|
||||
public StoreUpdateService(IConfigurationService configurationService,
|
||||
INotificationBuilder notificationBuilder,
|
||||
IPreferencesService preferencesService,
|
||||
INativeAppService nativeAppService)
|
||||
{
|
||||
_configurationService = configurationService;
|
||||
_notificationBuilder = notificationBuilder;
|
||||
_preferencesService = preferencesService;
|
||||
_nativeAppService = nativeAppService;
|
||||
}
|
||||
|
||||
public async Task<bool> RefreshAvailabilityAsync(bool showNotification = false)
|
||||
{
|
||||
await _refreshSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
var updates = await _storeContext.GetAppAndOptionalStorePackageUpdatesAsync();
|
||||
HasAvailableUpdate = updates?.Count > 0;
|
||||
|
||||
if (showNotification &&
|
||||
HasAvailableUpdate &&
|
||||
_preferencesService.IsStoreUpdateNotificationsEnabled &&
|
||||
!HasShownNotificationForCurrentVersion())
|
||||
{
|
||||
_notificationBuilder.CreateStoreUpdateNotification();
|
||||
MarkNotificationShownForCurrentVersion();
|
||||
}
|
||||
|
||||
return HasAvailableUpdate;
|
||||
}
|
||||
catch
|
||||
{
|
||||
HasAvailableUpdate = false;
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_refreshSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> StartUpdateAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var updates = await _storeContext.GetAppAndOptionalStorePackageUpdatesAsync();
|
||||
|
||||
if (updates == null || updates.Count == 0)
|
||||
{
|
||||
HasAvailableUpdate = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
await _storeContext.RequestDownloadAndInstallStorePackageUpdatesAsync(updates);
|
||||
await RefreshAvailabilityAsync(false).ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasShownNotificationForCurrentVersion()
|
||||
=> _configurationService.Get(GetNotificationShownKey(), false);
|
||||
|
||||
private void MarkNotificationShownForCurrentVersion()
|
||||
=> _configurationService.Set(GetNotificationShownKey(), true);
|
||||
|
||||
private string GetNotificationShownKey()
|
||||
=> string.Format(NotificationShownKeyFormat, _nativeAppService.GetFullAppVersion().Replace(".", "_"));
|
||||
}
|
||||
@@ -62,6 +62,14 @@
|
||||
</coreControls:WinoNavigationViewItem.Icon>
|
||||
</coreControls:WinoNavigationViewItem>
|
||||
</DataTemplate>
|
||||
<!-- Store Update Item -->
|
||||
<DataTemplate x:Key="StoreUpdateItemTemplate" x:DataType="menu:StoreUpdateMenuItem">
|
||||
<coreControls:WinoNavigationViewItem Content="{x:Bind domain:Translator.MenuUpdateAvailable}" DataContext="{x:Bind}">
|
||||
<muxc:NavigationViewItem.Icon>
|
||||
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" />
|
||||
</muxc:NavigationViewItem.Icon>
|
||||
</coreControls:WinoNavigationViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<!-- Rating Item -->
|
||||
<DataTemplate x:Key="RatingItemTemplate" x:DataType="menu:RateMenuItem">
|
||||
@@ -271,3 +279,7 @@
|
||||
<!--#endregion-->
|
||||
|
||||
</ResourceDictionary>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -304,6 +304,12 @@
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.MenuSettings}" />
|
||||
</StackPanel>
|
||||
</ListViewItem>
|
||||
<ListViewItem Visibility="{x:Bind ViewModel.IsStoreUpdateItemVisible, Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.MenuUpdateAvailable}" />
|
||||
</StackPanel>
|
||||
</ListViewItem>
|
||||
</ListView.Items>
|
||||
</ListView>
|
||||
</Grid>
|
||||
@@ -398,3 +404,5 @@
|
||||
</abstract:CalendarAppShellAbstract>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -47,6 +47,12 @@
|
||||
<PathIcon Data="F1 M 0 9.375 C 0 8.509115 0.110677 7.677409 0.332031 6.879883 C 0.553385 6.082357 0.867513 5.335287 1.274414 4.638672 C 1.681315 3.942059 2.169596 3.30892 2.739258 2.739258 C 3.308919 2.169598 3.942057 1.681316 4.638672 1.274414 C 5.335286 0.867514 6.082356 0.553387 6.879883 0.332031 C 7.677409 0.110678 8.509114 0 9.375 0 C 10.234375 0 11.062825 0.112305 11.860352 0.336914 C 12.657877 0.561523 13.404947 0.877279 14.101562 1.28418 C 14.798176 1.691082 15.431314 2.179363 16.000977 2.749023 C 16.570637 3.318686 17.058918 3.951824 17.46582 4.648438 C 17.872721 5.345053 18.188477 6.092123 18.413086 6.889648 C 18.637695 7.687175 18.75 8.515625 18.75 9.375 C 18.75 10.240886 18.637695 11.072592 18.413086 11.870117 C 18.188477 12.667644 17.872721 13.413086 17.46582 14.106445 C 17.058918 14.799805 16.570637 15.431315 16.000977 16.000977 C 15.431314 16.570639 14.799804 17.05892 14.106445 17.46582 C 13.413085 17.872721 12.666015 18.188477 11.865234 18.413086 C 11.064453 18.637695 10.234375 18.75 9.375 18.75 C 8.509114 18.75 7.675781 18.639322 6.875 18.417969 C 6.074219 18.196615 5.327148 17.882486 4.633789 17.475586 C 3.94043 17.068686 3.308919 16.580404 2.739258 16.010742 C 2.169596 15.441081 1.681315 14.80957 1.274414 14.116211 C 0.867513 13.422852 0.553385 12.675781 0.332031 11.875 C 0.110677 11.074219 0 10.240886 0 9.375 Z M 17.5 9.375 C 17.5 8.626303 17.403971 7.905273 17.211914 7.211914 C 17.019855 6.518556 16.746418 5.87077 16.391602 5.268555 C 16.036783 4.666342 15.613606 4.119467 15.12207 3.62793 C 14.630533 3.136395 14.083658 2.713217 13.481445 2.358398 C 12.879231 2.003582 12.231445 1.730145 11.538086 1.538086 C 10.844727 1.346029 10.123697 1.25 9.375 1.25 C 8.626302 1.25 7.905273 1.346029 7.211914 1.538086 C 6.518555 1.730145 5.870768 2.003582 5.268555 2.358398 C 4.666341 2.713217 4.119466 3.136395 3.62793 3.62793 C 3.136393 4.119467 2.713216 4.666342 2.358398 5.268555 C 2.003581 5.87077 1.730143 6.518556 1.538086 7.211914 C 1.346029 7.905273 1.25 8.626303 1.25 9.375 C 1.25 10.123698 1.346029 10.844727 1.538086 11.538086 C 1.730143 12.231445 2.001953 12.879232 2.353516 13.481445 C 2.705078 14.083659 3.128255 14.632162 3.623047 15.126953 C 4.117838 15.621745 4.666341 16.044922 5.268555 16.396484 C 5.870768 16.748047 6.518555 17.019857 7.211914 17.211914 C 7.905273 17.403971 8.626302 17.5 9.375 17.5 C 10.123697 17.5 10.844727 17.403971 11.538086 17.211914 C 12.231445 17.019857 12.879231 16.748047 13.481445 16.396484 C 14.083658 16.044922 14.63216 15.621745 15.126953 15.126953 C 15.621744 14.632162 16.044922 14.083659 16.396484 13.481445 C 16.748047 12.879232 17.019855 12.231445 17.211914 11.538086 C 17.403971 10.844727 17.5 10.123698 17.5 9.375 Z M 9.375 10 C 9.205729 10 9.059244 9.938151 8.935547 9.814453 C 8.811849 9.690756 8.75 9.544271 8.75 9.375 L 8.75 4.375 C 8.75 4.20573 8.811849 4.059246 8.935547 3.935547 C 9.059244 3.81185 9.205729 3.75 9.375 3.75 C 9.544271 3.75 9.690755 3.81185 9.814453 3.935547 C 9.93815 4.059246 10 4.20573 10 4.375 L 10 8.75 L 13.125 8.75 C 13.294271 8.75 13.440755 8.81185 13.564453 8.935547 C 13.68815 9.059245 13.75 9.205729 13.75 9.375 C 13.75 9.544271 13.68815 9.690756 13.564453 9.814453 C 13.440755 9.938151 13.294271 10 13.125 10 Z " />
|
||||
</controls:SettingsCard.HeaderIcon>
|
||||
</controls:SettingsCard>
|
||||
<controls:SettingsCard Description="{x:Bind domain:Translator.SettingsAppPreferences_StoreUpdateNotifications_Description}" Header="{x:Bind domain:Translator.SettingsAppPreferences_StoreUpdateNotifications_Title}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.PreferencesService.IsStoreUpdateNotificationsEnabled, Mode=TwoWay}" />
|
||||
<controls:SettingsCard.HeaderIcon>
|
||||
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" />
|
||||
</controls:SettingsCard.HeaderIcon>
|
||||
</controls:SettingsCard>
|
||||
</StackPanel>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
@@ -64,3 +70,6 @@
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</ScrollViewer>
|
||||
</abstract:AppPreferencesPageAbstract>
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user