From 55ae6e1f3a0bfd08a378dda59d7c2bcc61e77766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Sat, 25 Apr 2026 16:12:49 +0200 Subject: [PATCH] Initial v2 launch notification. --- .../Interfaces/INotificationBuilder.cs | 5 ++ .../Translations/en_US/resources.json | 2 + Wino.Mail.WinUI/Package.appxmanifest | 2 +- .../Services/NotificationBuilder.cs | 11 +++++ .../ReleaseLocalAccountDataCleanupService.cs | 48 +++++++++++++++---- 5 files changed, 58 insertions(+), 10 deletions(-) diff --git a/Wino.Core.Domain/Interfaces/INotificationBuilder.cs b/Wino.Core.Domain/Interfaces/INotificationBuilder.cs index ad3fdea2..b2be4c35 100644 --- a/Wino.Core.Domain/Interfaces/INotificationBuilder.cs +++ b/Wino.Core.Domain/Interfaces/INotificationBuilder.cs @@ -51,6 +51,11 @@ public interface INotificationBuilder /// void CreateStoreUpdateNotification(); + /// + /// Shows the one-time release migration notification. + /// + void CreateReleaseMigrationNotification(); + /// /// Creates a calendar reminder toast for the specified calendar item. /// diff --git a/Wino.Core.Domain/Translations/en_US/resources.json b/Wino.Core.Domain/Translations/en_US/resources.json index 247b881b..65f628ab 100644 --- a/Wino.Core.Domain/Translations/en_US/resources.json +++ b/Wino.Core.Domain/Translations/en_US/resources.json @@ -704,6 +704,8 @@ "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.", + "Notifications_ReleaseMigrationTitle": "New Wino Mail & Calendar", + "Notifications_ReleaseMigrationMessage": "Wino Mail got updated to the next version. Please re-create your accounts and start using the next version including calendar, mail templates, shortcuts, and a bunch of other improvements.", "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.", diff --git a/Wino.Mail.WinUI/Package.appxmanifest b/Wino.Mail.WinUI/Package.appxmanifest index 087d4689..48c4df34 100644 --- a/Wino.Mail.WinUI/Package.appxmanifest +++ b/Wino.Mail.WinUI/Package.appxmanifest @@ -23,7 +23,7 @@ + Version="2.0.7.0" /> diff --git a/Wino.Mail.WinUI/Services/NotificationBuilder.cs b/Wino.Mail.WinUI/Services/NotificationBuilder.cs index 5361d5e1..bb52ecf4 100644 --- a/Wino.Mail.WinUI/Services/NotificationBuilder.cs +++ b/Wino.Mail.WinUI/Services/NotificationBuilder.cs @@ -233,6 +233,17 @@ public class NotificationBuilder : INotificationBuilder ShowNotification(builder, "store-update-available"); } + public void CreateReleaseMigrationNotification() + { + var builder = CreateBuilder(); + builder.AddText(Translator.Notifications_ReleaseMigrationTitle); + builder.AddText(Translator.Notifications_ReleaseMigrationMessage); + builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail); + builder.AddButton(CreateDismissButton()); + + ShowNotification(builder, "release-migration-v2"); + } + public Task CreateCalendarReminderNotificationAsync(CalendarItem calendarItem, long reminderDurationInSeconds) { if (calendarItem == null) diff --git a/Wino.Mail.WinUI/Services/ReleaseLocalAccountDataCleanupService.cs b/Wino.Mail.WinUI/Services/ReleaseLocalAccountDataCleanupService.cs index 44edc737..fea775ff 100644 --- a/Wino.Mail.WinUI/Services/ReleaseLocalAccountDataCleanupService.cs +++ b/Wino.Mail.WinUI/Services/ReleaseLocalAccountDataCleanupService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading.Tasks; using Serilog; using Wino.Core.Domain.Interfaces; @@ -14,13 +15,16 @@ public sealed class ReleaseLocalAccountDataCleanupService private readonly IConfigurationService _configurationService; private readonly IApplicationConfiguration _applicationConfiguration; + private readonly INotificationBuilder _notificationBuilder; private readonly ILogger _logger = Log.ForContext(); public ReleaseLocalAccountDataCleanupService(IConfigurationService configurationService, - IApplicationConfiguration applicationConfiguration) + IApplicationConfiguration applicationConfiguration, + INotificationBuilder notificationBuilder) { _configurationService = configurationService; _applicationConfiguration = applicationConfiguration; + _notificationBuilder = notificationBuilder; } public async Task RunIfNeededAsync() @@ -45,44 +49,70 @@ public sealed class ReleaseLocalAccountDataCleanupService Path.Combine(publisherPath, LegacyDatabaseFileName) }; + var hadLegacyData = false; + foreach (var targetPath in cleanupTargets) { - await DeletePathIfExistsAsync(localFolderPath, targetPath).ConfigureAwait(false); + hadLegacyData |= await DeletePathIfExistsAsync(targetPath, localFolderPath, publisherPath).ConfigureAwait(false); } _configurationService.Set(CleanupCompletedSettingKey, true); + + if (hadLegacyData) + { + _notificationBuilder.CreateReleaseMigrationNotification(); + } + _logger.Information("Completed one-time local account data cleanup for release migration."); } - private async Task DeletePathIfExistsAsync(string localFolderPath, string targetPath) + private async Task DeletePathIfExistsAsync(string targetPath, params string[] allowedRootPaths) { try { var fullTargetPath = Path.GetFullPath(targetPath); - var fullLocalFolderPath = Path.GetFullPath(localFolderPath); - - if (!fullTargetPath.StartsWith(fullLocalFolderPath, StringComparison.OrdinalIgnoreCase)) + if (!allowedRootPaths.Any(rootPath => IsPathUnderAllowedRoot(fullTargetPath, rootPath))) { - _logger.Warning("Skipped startup cleanup for path outside local folder: {TargetPath}", fullTargetPath); - return; + _logger.Warning("Skipped startup cleanup for path outside allowed roots: {TargetPath}", fullTargetPath); + return false; } + var targetExists = Directory.Exists(fullTargetPath) || File.Exists(fullTargetPath); + if (Directory.Exists(fullTargetPath)) { await Task.Run(() => Directory.Delete(fullTargetPath, recursive: true)).ConfigureAwait(false); _logger.Information("Deleted legacy startup cleanup directory {TargetPath}", fullTargetPath); - return; + return true; } if (File.Exists(fullTargetPath)) { File.Delete(fullTargetPath); _logger.Information("Deleted legacy startup cleanup file {TargetPath}", fullTargetPath); + return true; } + + return targetExists; } catch (Exception ex) { _logger.Warning(ex, "Failed to delete legacy startup cleanup path {TargetPath}", targetPath); } + + return false; + } + + private static bool IsPathUnderAllowedRoot(string fullTargetPath, string rootPath) + { + if (string.IsNullOrWhiteSpace(rootPath)) + return false; + + var fullRootPath = Path.GetFullPath(rootPath); + var relativePath = Path.GetRelativePath(fullRootPath, fullTargetPath); + + return relativePath != "." && + !relativePath.StartsWith("..", StringComparison.Ordinal) && + !Path.IsPathRooted(relativePath); } }