From bac291587d71e2c6c1290849f44f074dd0a7a6fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Wed, 18 Mar 2026 10:25:07 +0100 Subject: [PATCH] New management page. --- .../Translations/en_US/resources.json | 28 +- .../SettingOptionsPageViewModel.cs | 1 - .../WinoAccountManagementPageViewModel.cs | 71 ++++ .../Settings/WinoAccountManagementPage.xaml | 328 +++++++++++++++--- 4 files changed, 368 insertions(+), 60 deletions(-) diff --git a/Wino.Core.Domain/Translations/en_US/resources.json b/Wino.Core.Domain/Translations/en_US/resources.json index 39815cac..3ecca58b 100644 --- a/Wino.Core.Domain/Translations/en_US/resources.json +++ b/Wino.Core.Domain/Translations/en_US/resources.json @@ -857,11 +857,15 @@ "SettingsSearch_KeyboardShortcuts_Keywords": "shortcut;shortcuts;hotkey;hotkeys;keyboard;keys", "SettingsSearch_WinoAccount_Keywords": "winoaccount;account;accounts;wino", "WinoAccount_Management_Description": "Manage your Wino Account, AI Pack access, and synchronized settings.", - "WinoAccount_Management_SignedOutTitle": "Sign in to Wino Account", - "WinoAccount_Management_SignedOutDescription": "Sign in or create a Wino Account to sync your settings and manage Wino add-ons.", + "WinoAccount_Management_SignedOutTitle": "Sign in to Wino Mail", + "WinoAccount_Management_SignedOutDescription": "Sign in or create an account to sync your email, access AI features, and manage your settings across devices.", + "WinoAccount_Management_ProfileSectionHeader": "Profile", + "WinoAccount_Management_AiPackSectionHeader": "AI Pack", + "WinoAccount_Management_DataSectionHeader": "Data", + "WinoAccount_Management_AccountActionsSectionHeader": "Account actions", "WinoAccount_Management_AccountCardTitle": "Account", "WinoAccount_Management_AccountCardDescription": "Your Wino Account email address and current account state.", - "WinoAccount_Management_AiPackCardTitle": "Wino AI Pack", + "WinoAccount_Management_AiPackCardTitle": "AI Pack", "WinoAccount_Management_AiPackCardDescription": "See whether Wino AI Pack is active and how much usage is left.", "WinoAccount_Management_AiPackActive": "AI Pack is active", "WinoAccount_Management_AiPackInactive": "AI Pack is not active", @@ -869,6 +873,24 @@ "WinoAccount_Management_AiPackBillingPeriod": "Billing period: {0:d} - {1:d}", "WinoAccount_Management_AiPackUnknownUsage": "Usage details are not available yet.", "WinoAccount_Management_AiPackBuyDescription": "Buy Wino AI Pack to translate, rewrite or summarize emails with AI.", + "WinoAccount_Management_AiPackPromoTitle": "Unlock AI Pack", + "WinoAccount_Management_AiPackPromoDescription": "Supercharge your email workflow with AI-powered tools. Translate messages into 50+ languages, rewrite for clarity and tone, and get instant summaries of long threads.", + "WinoAccount_Management_AiPackPromoPrice": "$4.99 / mo", + "WinoAccount_Management_AiPackPromoRequests": "1,000 requests", + "WinoAccount_Management_AiPackGetButton": "Get AI Pack", + "WinoAccount_Management_AiPackSubscriptionActive": "Your subscription is active", + "WinoAccount_Management_AiPackRenews": "Renews {0:d}", + "WinoAccount_Management_AiPackRequestsUsed": "Requests used this month", + "WinoAccount_Management_AiPackResets": "Resets {0:d}", + "WinoAccount_Management_AiPackFeatureTranslate": "Translate", + "WinoAccount_Management_AiPackFeatureRewrite": "Rewrite", + "WinoAccount_Management_AiPackFeatureSummarize": "Summarize", + "WinoAccount_Management_ImportSettingsTitle": "Import settings", + "WinoAccount_Management_ImportSettingsDescription": "Restore your preferences from cloud.", + "WinoAccount_Management_ExportSettingsTitle": "Export settings", + "WinoAccount_Management_ExportSettingsDescription": "Save your preferences to cloud.", + "WinoAccount_Management_SignOutTitle": "Sign out", + "WinoAccount_Management_SignOutDescription": "Sign out of your account on this device", "WinoAccount_Management_SettingsCardTitle": "Settings sync", "WinoAccount_Management_SettingsCardDescription": "Export your current settings to your Wino Account or import them back on this device.", "WinoAccount_Management_StatusLabel": "Status: {0}", diff --git a/Wino.Core.ViewModels/SettingOptionsPageViewModel.cs b/Wino.Core.ViewModels/SettingOptionsPageViewModel.cs index 66df4d7a..bbc86ce8 100644 --- a/Wino.Core.ViewModels/SettingOptionsPageViewModel.cs +++ b/Wino.Core.ViewModels/SettingOptionsPageViewModel.cs @@ -108,7 +108,6 @@ public partial class SettingOptionsPageViewModel : CoreBaseViewModel, public partial string WinoAccountStatusText { get; set; } = string.Empty; [ObservableProperty] - [NotifyPropertyChangedFor(nameof(CanShowAiUsage))] [NotifyPropertyChangedFor(nameof(CanShowBuyAiPack))] public partial bool HasAiPack { get; set; } diff --git a/Wino.Core.ViewModels/WinoAccountManagementPageViewModel.cs b/Wino.Core.ViewModels/WinoAccountManagementPageViewModel.cs index 9964e48d..7cadb3aa 100644 --- a/Wino.Core.ViewModels/WinoAccountManagementPageViewModel.cs +++ b/Wino.Core.ViewModels/WinoAccountManagementPageViewModel.cs @@ -23,6 +23,7 @@ namespace Wino.Core.ViewModels; public partial class WinoAccountManagementPageViewModel : CoreBaseViewModel { private const string BuyAiPackUrl = "https://example.com/wino-ai-pack"; + private const string ManageAiPackUrl = "https://example.com/wino-ai-pack/manage"; private readonly IWinoAccountProfileService _profileService; private readonly IWinoAccountApiClient _apiClient; @@ -38,6 +39,12 @@ public partial class WinoAccountManagementPageViewModel : CoreBaseViewModel [NotifyPropertyChangedFor(nameof(CanShowBuyAiPack))] public partial bool IsSignedIn { get; set; } + [ObservableProperty] + public partial string AccountDisplayName { get; set; } = string.Empty; + + [ObservableProperty] + public partial string AccountInitials { get; set; } = string.Empty; + [ObservableProperty] public partial string AccountEmail { get; set; } = string.Empty; @@ -58,6 +65,21 @@ public partial class WinoAccountManagementPageViewModel : CoreBaseViewModel [ObservableProperty] public partial string AiBillingPeriodSummary { get; set; } = string.Empty; + [ObservableProperty] + public partial string AiPackRenewalText { get; set; } = string.Empty; + + [ObservableProperty] + public partial int AiUsageCount { get; set; } + + [ObservableProperty] + public partial int AiUsageLimit { get; set; } = 1; + + [ObservableProperty] + public partial double AiUsagePercentage { get; set; } + + [ObservableProperty] + public partial string AiUsageResetText { get; set; } = string.Empty; + public bool IsSignedOut => !IsSignedIn; public bool CanShowAiUsage => HasAiPack; public bool CanShowBuyAiPack => IsSignedIn && !HasAiPack; @@ -137,6 +159,9 @@ public partial class WinoAccountManagementPageViewModel : CoreBaseViewModel [RelayCommand] private async Task OpenBuyPageAsync() => await _nativeAppService.LaunchUriAsync(new Uri(BuyAiPackUrl)); + [RelayCommand] + private async Task ManageAiPackAsync() => await _nativeAppService.LaunchUriAsync(new Uri(ManageAiPackUrl)); + [RelayCommand] private async Task ExportSettingsAsync() { @@ -289,6 +314,8 @@ public partial class WinoAccountManagementPageViewModel : CoreBaseViewModel { IsSignedIn = true; AccountEmail = resolvedUser.Email; + AccountDisplayName = ExtractDisplayName(resolvedUser.Email); + AccountInitials = ExtractInitials(resolvedUser.Email); AccountStatusText = string.Format(Translator.WinoAccount_Management_StatusLabel, resolvedUser.AccountStatus); }); @@ -319,12 +346,19 @@ public partial class WinoAccountManagementPageViewModel : CoreBaseViewModel await ExecuteUIThread(() => { IsSignedIn = false; + AccountDisplayName = string.Empty; + AccountInitials = string.Empty; AccountEmail = string.Empty; AccountStatusText = string.Empty; HasAiPack = false; AiPackStateText = Translator.WinoAccount_Management_AiPackInactive; AiUsageSummary = string.Empty; AiBillingPeriodSummary = string.Empty; + AiPackRenewalText = string.Empty; + AiUsageCount = 0; + AiUsageLimit = 1; + AiUsagePercentage = 0; + AiUsageResetText = string.Empty; }); } @@ -358,15 +392,25 @@ public partial class WinoAccountManagementPageViewModel : CoreBaseViewModel var hasAiPack = aiStatus?.HasAiPack == true; var usageText = Translator.WinoAccount_Management_AiPackUnknownUsage; var billingText = string.Empty; + var renewalText = string.Empty; + var usageCount = 0; + var usageLimit = 1; + var usagePercentage = 0d; + var resetText = string.Empty; if (hasAiPack && aiStatus?.Used is int used && aiStatus.MonthlyLimit is int limit && aiStatus.Remaining is int remaining) { usageText = string.Format(Translator.WinoAccount_Management_AiPackUsage, used, limit, remaining); + usageCount = used; + usageLimit = limit > 0 ? limit : 1; + usagePercentage = (double)used / usageLimit * 100; } if (hasAiPack && aiStatus?.CurrentPeriodStartUtc is DateTimeOffset periodStart && aiStatus.CurrentPeriodEndUtc is DateTimeOffset periodEnd) { billingText = string.Format(Translator.WinoAccount_Management_AiPackBillingPeriod, periodStart.LocalDateTime, periodEnd.LocalDateTime); + renewalText = string.Format(Translator.WinoAccount_Management_AiPackRenews, periodEnd.LocalDateTime); + resetText = string.Format(Translator.WinoAccount_Management_AiPackResets, periodEnd.LocalDateTime); } _ = ExecuteUIThread(() => @@ -377,9 +421,36 @@ public partial class WinoAccountManagementPageViewModel : CoreBaseViewModel : Translator.WinoAccount_Management_AiPackInactive; AiUsageSummary = hasAiPack ? usageText : string.Empty; AiBillingPeriodSummary = hasAiPack ? billingText : string.Empty; + AiPackRenewalText = hasAiPack ? renewalText : string.Empty; + AiUsageCount = usageCount; + AiUsageLimit = usageLimit; + AiUsagePercentage = usagePercentage; + AiUsageResetText = hasAiPack ? resetText : string.Empty; }); } + private static string ExtractDisplayName(string email) + { + if (string.IsNullOrWhiteSpace(email)) + return string.Empty; + + var atIndex = email.IndexOf('@'); + var localPart = atIndex > 0 ? email[..atIndex] : email; + + if (localPart.Length == 0) + return string.Empty; + + return char.ToUpper(localPart[0], CultureInfo.CurrentCulture) + localPart[1..]; + } + + private static string ExtractInitials(string email) + { + var displayName = ExtractDisplayName(email); + return displayName.Length > 0 + ? displayName[..1].ToUpper(CultureInfo.CurrentCulture) + : string.Empty; + } + private Dictionary CollectPreferencesSnapshot() { var settings = new Dictionary(StringComparer.Ordinal); diff --git a/Wino.Mail.WinUI/Views/Settings/WinoAccountManagementPage.xaml b/Wino.Mail.WinUI/Views/Settings/WinoAccountManagementPage.xaml index 57c8f649..cfeedc0f 100644 --- a/Wino.Mail.WinUI/Views/Settings/WinoAccountManagementPage.xaml +++ b/Wino.Mail.WinUI/Views/Settings/WinoAccountManagementPage.xaml @@ -12,12 +12,8 @@ - + + + + - - - - - + + + + + + + + + + + + + + +