From eb8cd7651d00f2f3c6cdb3f9429bb5293c90b6e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Fri, 20 Mar 2026 13:26:16 +0100 Subject: [PATCH] Calendar buttons etc. --- .../CalendarPageViewModel.cs | 36 ++- .../Translations/en_US/resources.json | 94 +------ .../SettingOptionsPageViewModel.cs | 261 +----------------- .../Calendar/CustomCalendarFlipView.cs | 55 ++-- Wino.Mail.WinUI/ShellWindow.xaml | 29 +- .../Views/Calendar/CalendarAppShell.xaml | 83 +----- Wino.Mail.WinUI/Views/SettingOptionsPage.xaml | 116 +------- Wino.Mail.WinUI/Views/WinoAppShell.xaml | 6 +- 8 files changed, 94 insertions(+), 586 deletions(-) diff --git a/Wino.Calendar.ViewModels/CalendarPageViewModel.cs b/Wino.Calendar.ViewModels/CalendarPageViewModel.cs index d75a1f31..10e4b1ee 100644 --- a/Wino.Calendar.ViewModels/CalendarPageViewModel.cs +++ b/Wino.Calendar.ViewModels/CalendarPageViewModel.cs @@ -910,19 +910,37 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel, } } - // Create day ranges for each flip item until we reach the total days to load. - int totalFlipItemCount = (int)Math.Ceiling((double)flipLoadRange.TotalDays / eachFlipItemCount); + List flipItemRanges = []; - List renderModels = new(); - - for (int i = 0; i < totalFlipItemCount; i++) + if (calendarLoadDirection == CalendarLoadDirection.Replace && + StatePersistanceService.CalendarDisplayType == CalendarDisplayType.Month) { - var startDate = flipLoadRange.StartDate.AddDays(i * eachFlipItemCount); - var endDate = startDate.AddDays(eachFlipItemCount); + var previousRange = strategy.GetPreviousDateRange(flipLoadRange, StatePersistanceService.DayDisplayCount); + var nextRange = strategy.GetNextDateRange(flipLoadRange, StatePersistanceService.DayDisplayCount); - var range = new DateRange(startDate, endDate); + flipItemRanges.Add(previousRange); + flipItemRanges.Add(flipLoadRange); + flipItemRanges.Add(nextRange); + } + else + { + // Create day ranges for each flip item until we reach the total days to load. + int totalFlipItemCount = (int)Math.Ceiling((double)flipLoadRange.TotalDays / eachFlipItemCount); + + for (int i = 0; i < totalFlipItemCount; i++) + { + var startDate = flipLoadRange.StartDate.AddDays(i * eachFlipItemCount); + var endDate = startDate.AddDays(eachFlipItemCount); + + flipItemRanges.Add(new DateRange(startDate, endDate)); + } + } + + List renderModels = []; + + foreach (var range in flipItemRanges) + { var renderOptions = new CalendarRenderOptions(range, CurrentSettings); - var dayRangeHeaderModel = new DayRangeRenderModel(renderOptions); renderModels.Add(dayRangeHeaderModel); } diff --git a/Wino.Core.Domain/Translations/en_US/resources.json b/Wino.Core.Domain/Translations/en_US/resources.json index 08f0d2ba..aa9843e4 100644 --- a/Wino.Core.Domain/Translations/en_US/resources.json +++ b/Wino.Core.Domain/Translations/en_US/resources.json @@ -84,7 +84,6 @@ "Buttons_SyncAliases": "Synchronize Aliases", "Buttons_TryAgain": "Try Again", "Buttons_Yes": "Yes", - "Purchased": "Purchased", "Sync_SynchronizingFolder": "Synchronizing {0} {1}%", "Sync_DownloadedMessages": "Downloaded {0} messages from {1}", "SyncAction_Archiving": "Archiving {0} mail(s)", @@ -830,12 +829,6 @@ "SettingsHome_StorageCard_Description": "See how much local MIME content Wino keeps on this device and clean it up when needed.", "SettingsHome_StorageEmptySummary": "No cached MIME content detected yet.", "SettingsHome_StorageLoading": "Checking local MIME usage...", - "SettingsHome_WinoAccount_Title": "Wino Account", - "SettingsHome_WinoAccount_SignedOutDescription": "Sign in to sync settings across devices and access add-ons like Wino AI Pack.", - "SettingsHome_WinoAccount_ManageLink": "Manage Wino Account", - "SettingsHome_AiPack_Title": "Wino AI Pack", - "SettingsHome_SettingsSync_Title": "Settings sync", - "SettingsHome_SettingsSync_Description": "Export or import your preferences to keep them in sync across devices.", "SettingsHome_Tips_Title": "Tips & tricks", "SettingsHome_Tips_Description": "A few small changes can make Wino feel much more personal.", "SettingsHome_Tip_Theme": "Want dark mode or accent changes? Open Personalization.", @@ -856,58 +849,6 @@ "SettingsSearch_Personalization_Keywords": "theme;dark;light;appearance;accent;color;colour;mode;layout;density", "SettingsSearch_About_Keywords": "about;version;website;privacy;github;donate;store;support", "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 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_AddOnsSectionHeader": "Wino Add-Ons", - "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": "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", - "WinoAccount_Management_AiPackUsage": "{0} of {1} uses consumed. {2} remaining.", - "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_PurchaseRequiresSignIn": "Sign in with your Wino Account to complete this purchase.", - "WinoAccount_Management_PurchaseStartFailed": "Wino could not start the checkout session for this add-on.", - "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_SyncPreferencesTitle": "Synchronize Preferences", - "WinoAccount_Management_SyncPreferencesDescription": "Import or export your preferences to cloud. Import them across devices.", - "WinoAccount_Management_SignOutTitle": "Sign out", - "WinoAccount_Management_SignOutDescription": "Sign out of your account on this device", - "WinoAccount_Management_StatusLabel": "Status: {0}", - "WinoAccount_Management_NoRemoteSettings": "There are no synchronized settings stored for this account yet.", - "WinoAccount_Management_ExportSucceeded": "Your settings were exported to your Wino Account.", - "WinoAccount_Management_ImportSucceeded": "Imported {0} settings from your Wino Account.", - "WinoAccount_Management_ImportPartial": "Imported {0} settings. {1} settings could not be restored.", - "WinoAccount_Management_SerializeFailed": "Wino could not serialize your current preferences.", - "WinoAccount_Management_EmptyExport": "There are no preference values to export.", - "WinoAccount_Management_ImportEmpty": "The synchronized settings payload does not contain any values to restore.", - "WinoAccount_Management_LoadFailed": "Wino could not load the latest Wino Account information.", - "WinoAccount_Management_ActionFailed": "The Wino Account request could not be completed.", - "WinoAddOn_AI_PACK_Name": "AI Pack", - "WinoAddOn_AI_PACK_Description": "Translate, rewrite, and summarize emails with Wino AI.", - "WinoAddOn_AI_PACK_Keywords": "Translate · Rewrite · Summarize", - "WinoAddOn_UNLIMITED_ACCOUNTS_Name": "Unlimited Accounts", - "WinoAddOn_UNLIMITED_ACCOUNTS_Description": "Remove the 3-account limit and add as many accounts as you need.", - "WinoAddOn_UNLIMITED_ACCOUNTS_Keywords": "More accounts · No limit · One-time purchase", "SettingsSearch_MessageList_Keywords": "message;messages;list;threading;threads;avatar;preview;sender", "SettingsSearch_ReadComposePane_Keywords": "reader;compose;composer;font;fonts;external content;display;reading", "SettingsSearch_SignatureAndEncryption_Keywords": "signature;signatures;encryption;certificate;certificates;s mime;smime;security", @@ -1041,9 +982,8 @@ "SystemFolderConfigDialogValidation_InboxSelected": "You can't assign Inbox folder to any other system folder.", "SystemFolderConfigSetupSuccess_Message": "System folders are successfully configured.", "SystemFolderConfigSetupSuccess_Title": "System Folders Setup", - "SystemTrayMenu_Open": "Open", - "SystemTrayMenu_ShowWino": "Open Mail", - "SystemTrayMenu_ShowWinoCalendar": "Open Calendar", + "SystemTrayMenu_ShowWino": "Open Wino Mail", + "SystemTrayMenu_ShowWinoCalendar": "Open Wino Calendar", "SystemTrayMenu_ExitWino": "Exit", "TestingImapConnectionMessage": "Testing server connection...", "TitleBarServerDisconnectedButton_Description": "Wino is disconnected from the network. Click reconnect to restore connection.", @@ -1228,10 +1168,6 @@ "WinoAccount_RegisterDialog_DifferenceTitle": "Wino Account is separate from your mail accounts", "WinoAccount_RegisterDialog_DifferenceDescription": "Your Outlook, Gmail, IMAP, or other email accounts stay exactly as they are. A Wino Account only manages Wino-specific features and account-based add-ons.", "WinoAccount_RegisterDialog_PrimaryButton": "Register", - "WinoAccount_RegisterDialog_PrivacyTitle": "Privacy and API processing", - "WinoAccount_RegisterDialog_PrivacyDescription": "Optional add-ons such as Wino AI Pack may send selected email HTML content to the Wino API service only when you use those features.", - "WinoAccount_RegisterDialog_PrivacyLinkText": "Read the privacy policy", - "WinoAccount_RegisterDialog_PrivacyCheckbox": "I agree to the privacy policy.", "WinoAccount_LoginDialog_Title": "Sign In to Wino Account", "WinoAccount_LoginDialog_Description": "Sign in to your Wino Account to sync your Wino setup and access account-based features.", "WinoAccount_LoginDialog_HeroTitle": "Welcome back", @@ -1239,28 +1175,17 @@ "WinoAccount_LoginDialog_BenefitsDescription": "Use your Wino Account to continue syncing settings across devices and to access paid add-ons such as Wino AI Pack.", "WinoAccount_LoginDialog_DifferenceTitle": "This is not your email mailbox sign-in", "WinoAccount_LoginDialog_DifferenceDescription": "Signing in here does not add or replace your Outlook, Gmail, or IMAP accounts in Wino. It only signs you in to Wino-specific services.", - "WinoAccount_LoginDialog_ForgotPasswordLink": "Forgot password?", "WinoAccount_EmailLabel": "Email", "WinoAccount_EmailPlaceholder": "name@example.com", "WinoAccount_PasswordLabel": "Password", "WinoAccount_ConfirmPasswordLabel": "Confirm password", - "WinoAccount_ForgotPasswordDialog_Title": "Reset your password", - "WinoAccount_ForgotPasswordDialog_PrimaryButton": "Send reset email", - "WinoAccount_ForgotPasswordDialog_BackToSignIn": "Back to sign in", - "WinoAccount_ForgotPasswordDialog_Description": "Enter your Wino Account email address and we will send you a password reset link if the address is registered.", "WinoAccount_Validation_EmailRequired": "Email is required.", "WinoAccount_Validation_PasswordRequired": "Password is required.", "WinoAccount_Validation_PasswordMismatch": "Passwords do not match.", - "WinoAccount_Validation_PrivacyConsentRequired": "You must accept the privacy policy before creating a Wino Account.", "WinoAccount_Error_InvalidCredentials": "The email address or password is incorrect.", "WinoAccount_Error_AccountLocked": "This account is temporarily locked.", "WinoAccount_Error_AccountBanned": "This account has been banned.", "WinoAccount_Error_AccountSuspended": "This account has been suspended.", - "WinoAccount_Error_EmailNotConfirmed": "Please confirm your email address before signing in.", - "WinoAccount_Error_EmailConfirmationRequired": "Please confirm your email address before signing in.", - "WinoAccount_Error_EmailConfirmationResendNotAvailable": "A new confirmation email is not available yet.", - "WinoAccount_Error_EmailConfirmationResendInvalid": "This confirmation request is no longer valid. Please try signing in again.", - "WinoAccount_Error_EmailNotRegistered": "This email address is not registered.", "WinoAccount_Error_RefreshTokenInvalid": "Your session is no longer valid. Please sign in again.", "WinoAccount_Error_EmailAlreadyRegistered": "This email address is already registered.", "WinoAccount_Error_ExternalLoginEmailRequired": "An email address is required to complete external sign-in.", @@ -1271,21 +1196,6 @@ "WinoAccount_Error_ValidationFailed": "The request is invalid. Please review the entered values.", "WinoAccount_RegisterSuccessMessage": "Wino Account registration completed for {0}.", "WinoAccount_LoginSuccessMessage": "Signed in to Wino Account as {0}.", - "WinoAccount_EmailConfirmationSentDialog_Title": "Confirm your email address", - "WinoAccount_EmailConfirmationSentDialog_Message": "We sent an email confirmation to {0}. Please confirm it and try signing in again.", - "WinoAccount_EmailConfirmationPendingDialog_Title": "Email confirmation required", - "WinoAccount_EmailConfirmationPendingDialog_Message": "We are still waiting for you to confirm {0}.", - "WinoAccount_EmailConfirmationPendingDialog_ResendButton": "Resend confirmation email", - "WinoAccount_EmailConfirmationPendingDialog_Countdown": "You can resend the confirmation email in {0}.", - "WinoAccount_EmailConfirmationPendingDialog_ReadyToResend": "You can resend the confirmation email now.", - "WinoAccount_EmailConfirmationResentDialog_Title": "Confirmation email resent", - "WinoAccount_EmailConfirmationResentDialog_Message": "We sent another confirmation email to {0}. Please confirm it and try signing in again.", - "WinoAccount_ForgotPasswordDialog_SuccessTitle": "Password reset email sent", - "WinoAccount_ForgotPasswordDialog_SuccessMessage": "We sent a password reset email to {0}. Open that message to choose a new password.", - "WinoAccount_ChangePassword_Title": "Change password", - "WinoAccount_ChangePassword_Description": "Send a password reset email to this Wino Account.", - "WinoAccount_ChangePassword_Action": "Send reset email", - "WinoAccount_ChangePassword_ConfirmationMessage": "Do you want Wino to send a password reset email to {0}?", "WinoAccount_SignOut_SuccessMessage": "Signed out from Wino Account {0}.", "WinoAccount_SignOut_NoAccountMessage": "There is no active Wino Account to sign out.", "WinoAccount_Titlebar_SignedOutTitle": "Wino Account", diff --git a/Wino.Core.ViewModels/SettingOptionsPageViewModel.cs b/Wino.Core.ViewModels/SettingOptionsPageViewModel.cs index 8d15aad2..e49f98fe 100644 --- a/Wino.Core.ViewModels/SettingOptionsPageViewModel.cs +++ b/Wino.Core.ViewModels/SettingOptionsPageViewModel.cs @@ -11,26 +11,18 @@ using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Enums; using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Models.Navigation; -using Wino.Core.Domain.Models.Accounts; using Wino.Core.Domain.Models.Personalization; using Wino.Core.Domain.Models.Settings; using Wino.Core.Domain.Models.Translations; using Wino.Core.Extensions; using Wino.Core.ViewModels.Data; -using Wino.Mail.Api.Contracts.Ai; using Wino.Mail.ViewModels.Data; using Wino.Messaging.Client.Navigation; -using Wino.Messaging.UI; namespace Wino.Core.ViewModels; -public partial class SettingOptionsPageViewModel : CoreBaseViewModel, - IRecipient, - IRecipient, - IRecipient +public partial class SettingOptionsPageViewModel : CoreBaseViewModel { - private const string BuyAiPackUrl = "https://example.com/wino-ai-pack"; - private readonly INativeAppService _nativeAppService; private readonly IAccountService _accountService; private readonly IMimeStorageService _mimeStorageService; @@ -39,8 +31,6 @@ public partial class SettingOptionsPageViewModel : CoreBaseViewModel, private readonly INewThemeService _newThemeService; private readonly IPreferencesService _preferencesService; private readonly IProviderService _providerService; - private readonly IWinoAccountProfileService _profileService; - private readonly IMailDialogService _dialogService; private bool _isInitializingSettings; private bool _isAppearanceSelectionPaused; @@ -92,42 +82,6 @@ public partial class SettingOptionsPageViewModel : CoreBaseViewModel, [ObservableProperty] public partial bool UseAccentColor { get; set; } - // Wino Account hero card properties - - [ObservableProperty] - public partial bool IsWinoAccountBusy { get; set; } - - [ObservableProperty] - [NotifyPropertyChangedFor(nameof(IsWinoAccountSignedOut))] - [NotifyPropertyChangedFor(nameof(CanShowBuyAiPack))] - public partial bool IsWinoAccountSignedIn { get; set; } - - [ObservableProperty] - public partial string WinoAccountEmail { get; set; } = string.Empty; - - [ObservableProperty] - public partial string WinoAccountStatusText { get; set; } = string.Empty; - - [ObservableProperty] - [NotifyPropertyChangedFor(nameof(CanShowBuyAiPack))] - public partial bool HasAiPack { get; set; } - - [ObservableProperty] - public partial string AiPackStateText { get; set; } = Translator.WinoAccount_Management_AiPackInactive; - - [ObservableProperty] - public partial string AiUsageSummary { get; set; } = string.Empty; - - [ObservableProperty] - public partial string AiBillingPeriodSummary { get; set; } = string.Empty; - - [ObservableProperty] - public partial double AiUsagePercent { get; set; } - - public bool IsWinoAccountSignedOut => !IsWinoAccountSignedIn; - public bool CanShowAiUsage => HasAiPack; - public bool CanShowBuyAiPack => IsWinoAccountSignedIn && !HasAiPack; - public SettingOptionsPageViewModel(INativeAppService nativeAppService, IAccountService accountService, IMimeStorageService mimeStorageService, @@ -135,9 +89,7 @@ public partial class SettingOptionsPageViewModel : CoreBaseViewModel, ITranslationService translationService, INewThemeService newThemeService, IPreferencesService preferencesService, - IProviderService providerService, - IWinoAccountProfileService profileService, - IMailDialogService dialogService) + IProviderService providerService) { _nativeAppService = nativeAppService; _accountService = accountService; @@ -147,8 +99,6 @@ public partial class SettingOptionsPageViewModel : CoreBaseViewModel, _newThemeService = newThemeService; _preferencesService = preferencesService; _providerService = providerService; - _profileService = profileService; - _dialogService = dialogService; } public override void OnNavigatedTo(NavigationMode mode, object parameters) @@ -162,7 +112,6 @@ public partial class SettingOptionsPageViewModel : CoreBaseViewModel, InitializeQuickSettings(); _ = LoadDashboardAsync(); - _ = LoadWinoAccountAsync(); } public void UpdateSearchSuggestions(string query) @@ -416,212 +365,6 @@ public partial class SettingOptionsPageViewModel : CoreBaseViewModel, await _translationService.InitializeLanguageAsync(language.Language); } - // Wino Account message recipients - - protected override void RegisterRecipients() - { - base.RegisterRecipients(); - - Messenger.Register(this); - Messenger.Register(this); - Messenger.Register(this); - } - - protected override void UnregisterRecipients() - { - base.UnregisterRecipients(); - - Messenger.Unregister(this); - Messenger.Unregister(this); - Messenger.Unregister(this); - } - - public void Receive(WinoAccountProfileUpdatedMessage message) - => _ = LoadWinoAccountAsync(); - - public void Receive(WinoAccountProfileDeletedMessage message) - => _ = ResetWinoAccountStateAsync(); - - public void Receive(WinoAccountAddOnPurchasedMessage message) - => _ = LoadWinoAccountAsync(); - - // Wino Account hero card commands and helpers - - [RelayCommand] - private async Task WinoAccountRegisterAsync() - { - var account = await _dialogService.ShowWinoAccountRegistrationDialogAsync(); - if (account == null) return; - - _dialogService.InfoBarMessage(Translator.GeneralTitle_Info, - string.Format(Translator.WinoAccount_RegisterSuccessMessage, account.Email), - InfoBarMessageType.Success); - await LoadWinoAccountAsync(); - } - - [RelayCommand] - private async Task WinoAccountSignInAsync() - { - var account = await _dialogService.ShowWinoAccountLoginDialogAsync(); - if (account == null) return; - - _dialogService.InfoBarMessage(Translator.GeneralTitle_Info, - string.Format(Translator.WinoAccount_LoginSuccessMessage, account.Email), - InfoBarMessageType.Success); - await LoadWinoAccountAsync(); - } - - [RelayCommand] - private async Task WinoAccountSignOutAsync() - { - var account = await _profileService.GetActiveAccountAsync().ConfigureAwait(false); - if (account == null) return; - - await _profileService.SignOutAsync().ConfigureAwait(false); - - _dialogService.InfoBarMessage(Translator.GeneralTitle_Info, - string.Format(Translator.WinoAccount_SignOut_SuccessMessage, account.Email), - InfoBarMessageType.Success); - await ResetWinoAccountStateAsync(); - } - - [RelayCommand] - private async Task OpenBuyAiPackPageAsync() => await _nativeAppService.LaunchUriAsync(new Uri(BuyAiPackUrl)); - - [RelayCommand] - private void NavigateToWinoAccountManagement() - => NavigateSubDetail(WinoPage.WinoAccountManagementPage); - - private async Task LoadWinoAccountAsync() - { - try - { - var cachedAccount = await _profileService.GetActiveAccountAsync().ConfigureAwait(false); - var cachedAddOns = await _profileService.GetCachedAddOnSnapshotAsync().ConfigureAwait(false); - - if (cachedAccount != null) - { - await ApplyWinoAccountStateAsync(cachedAccount.Email, cachedAccount.AccountStatus).ConfigureAwait(false); - UpdateAiPackState(cachedAddOns); - } - - await ExecuteUIThread(() => IsWinoAccountBusy = cachedAccount == null); - - var account = await _profileService.GetAuthenticatedAccountAsync().ConfigureAwait(false); - if (account == null) - { - await ResetWinoAccountStateAsync(); - return; - } - - var aiStatusResponse = await _profileService.GetAiStatusAsync().ConfigureAwait(false); - var profileRefreshResult = await _profileService.RefreshProfileAsync().ConfigureAwait(false); - - var resolvedAccount = profileRefreshResult.IsSuccess && profileRefreshResult.Account != null - ? profileRefreshResult.Account - : account; - - await ApplyWinoAccountStateAsync(resolvedAccount.Email, resolvedAccount.AccountStatus).ConfigureAwait(false); - - if (aiStatusResponse.IsSuccess) - { - UpdateAiPackState(aiStatusResponse.Result); - } - else - { - UpdateAiPackState(cachedAddOns); - } - } - catch - { - await ResetWinoAccountStateAsync(); - } - finally - { - await ExecuteUIThread(() => IsWinoAccountBusy = false); - } - } - - private async Task ApplyWinoAccountStateAsync(string email, string status) - { - await ExecuteUIThread(() => - { - IsWinoAccountSignedIn = true; - WinoAccountEmail = email; - WinoAccountStatusText = string.Format(Translator.WinoAccount_Management_StatusLabel, status); - }); - } - - private async Task ResetWinoAccountStateAsync() - { - await ExecuteUIThread(() => - { - IsWinoAccountSignedIn = false; - WinoAccountEmail = string.Empty; - WinoAccountStatusText = string.Empty; - HasAiPack = false; - AiPackStateText = Translator.WinoAccount_Management_AiPackInactive; - AiUsageSummary = string.Empty; - AiBillingPeriodSummary = string.Empty; - AiUsagePercent = 0; - }); - } - - private void UpdateAiPackState(AiStatusResultDto aiStatus) - { - UpdateAiPackState( - aiStatus?.HasAiPack == true, - aiStatus?.Used, - aiStatus?.MonthlyLimit, - aiStatus?.Remaining, - aiStatus?.CurrentPeriodStartUtc, - aiStatus?.CurrentPeriodEndUtc); - } - - private void UpdateAiPackState(WinoAccountAddOnSnapshot addOnSnapshot) - { - var remaining = addOnSnapshot?.HasAiPack == true && addOnSnapshot.UsageLimit is int limit && addOnSnapshot.UsageCount is int used - ? limit - used - : (int?)null; - - UpdateAiPackState( - addOnSnapshot?.HasAiPack == true, - addOnSnapshot?.UsageCount, - addOnSnapshot?.UsageLimit, - remaining, - addOnSnapshot?.BillingPeriodStartUtc, - addOnSnapshot?.BillingPeriodEndUtc); - } - - private void UpdateAiPackState(bool hasAiPack, int? used, int? limit, int? remaining, DateTimeOffset? periodStart, DateTimeOffset? periodEnd) - { - var usageText = Translator.WinoAccount_Management_AiPackUnknownUsage; - var billingText = string.Empty; - var usagePercent = 0d; - - if (hasAiPack && used is int usageCount && limit is int usageLimit && remaining is int remainingCount) - { - usageText = string.Format(Translator.WinoAccount_Management_AiPackUsage, usageCount, usageLimit, remainingCount); - usagePercent = usageLimit > 0 ? (double)usageCount / usageLimit * 100 : 0; - } - - if (hasAiPack && periodStart is DateTimeOffset billingStart && periodEnd is DateTimeOffset billingEnd) - { - billingText = string.Format(Translator.WinoAccount_Management_AiPackBillingPeriod, billingStart.LocalDateTime, billingEnd.LocalDateTime); - } - - _ = ExecuteUIThread(() => - { - HasAiPack = hasAiPack; - AiPackStateText = hasAiPack - ? Translator.WinoAccount_Management_AiPackActive - : Translator.WinoAccount_Management_AiPackInactive; - AiUsageSummary = hasAiPack ? usageText : string.Empty; - AiBillingPeriodSummary = hasAiPack ? billingText : string.Empty; - AiUsagePercent = usagePercent; - }); - } - [RelayCommand] public void NavigateSubDetail(object type) { diff --git a/Wino.Mail.WinUI/Controls/Calendar/CustomCalendarFlipView.cs b/Wino.Mail.WinUI/Controls/Calendar/CustomCalendarFlipView.cs index 4e3a812c..dfd56773 100644 --- a/Wino.Mail.WinUI/Controls/Calendar/CustomCalendarFlipView.cs +++ b/Wino.Mail.WinUI/Controls/Calendar/CustomCalendarFlipView.cs @@ -38,10 +38,7 @@ public partial class CustomCalendarFlipView : FlipView { base.OnApplyTemplate(); - PreviousButtonHorizontal = GetTemplateChild(PART_PreviousButtonHorizontal) as Button; - NextButtonHorizontal = GetTemplateChild(PART_NextButtonHorizontal) as Button; - PreviousButtonVertical = GetTemplateChild(PART_PreviousButtonVertical) as Button; - NextButtonVertical = GetTemplateChild(PART_NextButtonVertical) as Button; + RefreshNavigationButtons(); // Hide navigation buttons HideButton(PreviousButtonHorizontal); @@ -78,25 +75,47 @@ public partial class CustomCalendarFlipView : FlipView public void GoPreviousFlip() { - var previousButton = DisplayType == CalendarDisplayType.Month - ? PreviousButtonVertical ?? PreviousButtonHorizontal - : PreviousButtonHorizontal ?? PreviousButtonVertical; - - if (previousButton == null) return; - - var backPeer = new ButtonAutomationPeer(previousButton); - backPeer.Invoke(); + InvokeNavigationButton(PreviousButtonHorizontal, PreviousButtonVertical); } public void GoNextFlip() { - var nextButton = DisplayType == CalendarDisplayType.Month - ? NextButtonVertical ?? NextButtonHorizontal - : NextButtonHorizontal ?? NextButtonVertical; + InvokeNavigationButton(NextButtonHorizontal, NextButtonVertical); + } - if (nextButton == null) return; + private void RefreshNavigationButtons() + { + PreviousButtonHorizontal = GetTemplateChild(PART_PreviousButtonHorizontal) as Button; + NextButtonHorizontal = GetTemplateChild(PART_NextButtonHorizontal) as Button; + PreviousButtonVertical = GetTemplateChild(PART_PreviousButtonVertical) as Button; + NextButtonVertical = GetTemplateChild(PART_NextButtonVertical) as Button; + } - var nextPeer = new ButtonAutomationPeer(nextButton); - nextPeer.Invoke(); + private void InvokeNavigationButton(Button? primaryButton, Button? secondaryButton) + { + if (Items == null || Items.Count == 0) + return; + + RefreshNavigationButtons(); + + var previousIndex = SelectedIndex; + + if (TryInvokeNavigationButton(primaryButton, previousIndex)) + { + return; + } + + TryInvokeNavigationButton(secondaryButton, previousIndex); + } + + private bool TryInvokeNavigationButton(Button? navigationButton, int previousIndex) + { + if (navigationButton == null) + return false; + + var peer = new ButtonAutomationPeer(navigationButton); + peer.Invoke(); + + return SelectedIndex != previousIndex; } } diff --git a/Wino.Mail.WinUI/ShellWindow.xaml b/Wino.Mail.WinUI/ShellWindow.xaml index da45b6b4..4a909fb0 100644 --- a/Wino.Mail.WinUI/ShellWindow.xaml +++ b/Wino.Mail.WinUI/ShellWindow.xaml @@ -92,27 +92,17 @@ BorderBrush="Transparent" Visibility="{x:Bind helpers:XamlHelpers.ReverseBoolToVisibilityConverter(PreferencesService.IsWinoAccountButtonHidden), Mode=OneWay}"> - + - - + + - diff --git a/Wino.Mail.WinUI/Views/Calendar/CalendarAppShell.xaml b/Wino.Mail.WinUI/Views/Calendar/CalendarAppShell.xaml index 06105cb0..b6d548ab 100644 --- a/Wino.Mail.WinUI/Views/Calendar/CalendarAppShell.xaml +++ b/Wino.Mail.WinUI/Views/Calendar/CalendarAppShell.xaml @@ -63,87 +63,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Wino.Mail.WinUI/Views/SettingOptionsPage.xaml b/Wino.Mail.WinUI/Views/SettingOptionsPage.xaml index de5709dd..26b09479 100644 --- a/Wino.Mail.WinUI/Views/SettingOptionsPage.xaml +++ b/Wino.Mail.WinUI/Views/SettingOptionsPage.xaml @@ -80,18 +80,22 @@ - + - + + + + + - + @@ -130,6 +138,7 @@ Stretch="Uniform" /> + +