feat: create cloud-free Wino Mail fork
This commit is contained in:
@@ -1,632 +0,0 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Accounts;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
using Wino.Core.ViewModels.Data;
|
||||
using Wino.Mail.Api.Contracts.Common;
|
||||
using Wino.Messaging.UI;
|
||||
|
||||
namespace Wino.Core.ViewModels;
|
||||
|
||||
public partial class WinoAccountManagementPageViewModel : CoreBaseViewModel,
|
||||
IRecipient<WinoAccountProfileUpdatedMessage>,
|
||||
IRecipient<WinoAccountProfileDeletedMessage>,
|
||||
IRecipient<WinoAccountAddOnPurchasedMessage>
|
||||
{
|
||||
private readonly IWinoAccountProfileService _profileService;
|
||||
private readonly IWinoAccountDataSyncService _syncService;
|
||||
private readonly IMailDialogService _dialogService;
|
||||
private readonly IStoreManagementService _storeManagementService;
|
||||
private readonly WinoAddOnItemViewModel _aiPackAddOn;
|
||||
private readonly WinoAddOnItemViewModel _unlimitedAccountsAddOn;
|
||||
|
||||
public ObservableCollection<WinoAddOnItemViewModel> AddOns { get; } = [];
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsBusy { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(IsSignedOut))]
|
||||
public partial bool IsSignedIn { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string AccountEmail { get; set; } = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string AccountStatusText { get; set; } = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyCanExecuteChangedFor(nameof(PurchaseAddOnCommand))]
|
||||
public partial bool IsCheckoutInProgress { get; set; }
|
||||
|
||||
public bool IsSignedOut => !IsSignedIn;
|
||||
|
||||
public WinoAccountManagementPageViewModel(IWinoAccountProfileService profileService,
|
||||
IWinoAccountDataSyncService syncService,
|
||||
IMailDialogService dialogService,
|
||||
IStoreManagementService storeManagementService)
|
||||
{
|
||||
_profileService = profileService;
|
||||
_syncService = syncService;
|
||||
_dialogService = dialogService;
|
||||
_storeManagementService = storeManagementService;
|
||||
|
||||
_aiPackAddOn = CreateAddOnItem(WinoAddOnProductType.AI_PACK);
|
||||
_unlimitedAccountsAddOn = CreateAddOnItem(WinoAddOnProductType.UNLIMITED_ACCOUNTS);
|
||||
AddOns.Add(_aiPackAddOn);
|
||||
AddOns.Add(_unlimitedAccountsAddOn);
|
||||
}
|
||||
|
||||
public override void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
_ = InitializeAsync();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task RegisterAsync()
|
||||
{
|
||||
var account = await _dialogService.ShowWinoAccountRegistrationDialogAsync();
|
||||
if (account == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Info,
|
||||
string.Format(Translator.WinoAccount_RegisterSuccessMessage, account.Email),
|
||||
InfoBarMessageType.Success);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SignInAsync()
|
||||
{
|
||||
var account = await _dialogService.ShowWinoAccountLoginDialogAsync();
|
||||
if (account == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Info,
|
||||
string.Format(Translator.WinoAccount_LoginSuccessMessage, account.Email),
|
||||
InfoBarMessageType.Success);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SignOutAsync()
|
||||
{
|
||||
var account = await _profileService.GetActiveAccountAsync().ConfigureAwait(false);
|
||||
if (account == null)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Warning,
|
||||
Translator.WinoAccount_SignOut_NoAccountMessage,
|
||||
InfoBarMessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
await _profileService.SignOutAsync().ConfigureAwait(false);
|
||||
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Info,
|
||||
string.Format(Translator.WinoAccount_SignOut_SuccessMessage, account.Email),
|
||||
InfoBarMessageType.Success);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ChangePasswordAsync()
|
||||
{
|
||||
var account = await _profileService.GetActiveAccountAsync();
|
||||
if (account == null)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Warning,
|
||||
Translator.WinoAccount_SignOut_NoAccountMessage,
|
||||
InfoBarMessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var shouldContinue = await _dialogService.ShowConfirmationDialogAsync(
|
||||
string.Format(Translator.WinoAccount_ChangePassword_ConfirmationMessage, account.Email),
|
||||
Translator.WinoAccount_ChangePassword_Title,
|
||||
Translator.WinoAccount_ChangePassword_Action);
|
||||
|
||||
if (!shouldContinue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var response = await _profileService.ForgotPasswordAsync(account.Email);
|
||||
if (!response.IsSuccess)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Error,
|
||||
TranslateForgotPasswordError(response.ErrorCode),
|
||||
InfoBarMessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Info,
|
||||
string.Format(Translator.WinoAccount_ForgotPasswordDialog_SuccessMessage, account.Email),
|
||||
InfoBarMessageType.Success);
|
||||
}
|
||||
|
||||
private static string TranslateForgotPasswordError(string? errorCode)
|
||||
=> errorCode switch
|
||||
{
|
||||
ApiErrorCodes.EmailNotRegistered => Translator.WinoAccount_Error_EmailNotRegistered,
|
||||
ApiErrorCodes.ValidationFailed => Translator.WinoAccount_Error_ValidationFailed,
|
||||
_ when string.IsNullOrWhiteSpace(errorCode) => Translator.GeneralTitle_Error,
|
||||
_ => errorCode!
|
||||
};
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanPurchaseAddOn))]
|
||||
private async Task PurchaseAddOnAsync(WinoAddOnItemViewModel? addOn)
|
||||
{
|
||||
if (addOn == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
IsCheckoutInProgress = true;
|
||||
addOn.IsPurchaseInProgress = true;
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
var purchaseResult = await _storeManagementService.PurchaseAsync(addOn.ProductType);
|
||||
|
||||
if (purchaseResult == StorePurchaseResult.NotPurchased)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Error,
|
||||
Translator.WinoAccount_Management_PurchaseStartFailed,
|
||||
InfoBarMessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var syncResult = await _profileService.SyncStoreEntitlementsAsync().ConfigureAwait(false);
|
||||
if (!syncResult.IsSuccess && !string.Equals(syncResult.ErrorCode, "MissingAccessToken", StringComparison.Ordinal))
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Error,
|
||||
TranslateStoreSyncError(syncResult.ErrorCode),
|
||||
InfoBarMessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
await HandleAddOnPurchasedAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Error,
|
||||
Translator.WinoAccount_Management_PurchaseStartFailed,
|
||||
InfoBarMessageType.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
IsCheckoutInProgress = false;
|
||||
addOn.IsPurchaseInProgress = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanPurchaseAddOn(WinoAddOnItemViewModel? addOn)
|
||||
=> addOn != null && !addOn.IsPurchased && !addOn.IsLoading && !IsCheckoutInProgress;
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ExportSettingsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _dialogService.ShowWinoAccountExportDialogAsync().ConfigureAwait(false);
|
||||
if (result == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_dialogService.InfoBarMessage(
|
||||
Translator.GeneralTitle_Info,
|
||||
BuildExportSuccessMessage(result),
|
||||
InfoBarMessageType.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_dialogService.InfoBarMessage(
|
||||
Translator.GeneralTitle_Error,
|
||||
ex.Message,
|
||||
InfoBarMessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ImportSettingsAsync()
|
||||
{
|
||||
await ExecuteUIThread(() => IsBusy = true);
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _syncService.ImportAsync(new WinoAccountSyncSelection());
|
||||
|
||||
if (!result.HasAnyRemoteData)
|
||||
{
|
||||
_dialogService.InfoBarMessage(
|
||||
Translator.GeneralTitle_Info,
|
||||
Translator.WinoAccount_Management_NoRemoteSettings,
|
||||
InfoBarMessageType.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
var messageType = result.FailedPreferenceCount > 0
|
||||
? InfoBarMessageType.Warning
|
||||
: InfoBarMessageType.Success;
|
||||
|
||||
_dialogService.InfoBarMessage(
|
||||
result.FailedPreferenceCount > 0 ? Translator.GeneralTitle_Warning : Translator.GeneralTitle_Info,
|
||||
BuildImportMessage(result),
|
||||
messageType);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_dialogService.InfoBarMessage(
|
||||
Translator.GeneralTitle_Error,
|
||||
ex.Message,
|
||||
InfoBarMessageType.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await ExecuteUIThread(() => IsBusy = false);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RegisterRecipients()
|
||||
{
|
||||
base.RegisterRecipients();
|
||||
|
||||
Messenger.Register<WinoAccountProfileUpdatedMessage>(this);
|
||||
Messenger.Register<WinoAccountProfileDeletedMessage>(this);
|
||||
Messenger.Register<WinoAccountAddOnPurchasedMessage>(this);
|
||||
}
|
||||
|
||||
protected override void UnregisterRecipients()
|
||||
{
|
||||
base.UnregisterRecipients();
|
||||
|
||||
Messenger.Unregister<WinoAccountProfileUpdatedMessage>(this);
|
||||
Messenger.Unregister<WinoAccountProfileDeletedMessage>(this);
|
||||
Messenger.Unregister<WinoAccountAddOnPurchasedMessage>(this);
|
||||
}
|
||||
|
||||
public void Receive(WinoAccountProfileUpdatedMessage message)
|
||||
=> _ = LoadAsync();
|
||||
|
||||
public void Receive(WinoAccountProfileDeletedMessage message)
|
||||
=> _ = LoadAsync();
|
||||
|
||||
public void Receive(WinoAccountAddOnPurchasedMessage message)
|
||||
=> _ = HandleAddOnPurchasedAsync();
|
||||
|
||||
private async Task InitializeAsync()
|
||||
{
|
||||
await LoadAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task LoadAsync()
|
||||
{
|
||||
WinoAccount? cachedAccount = null;
|
||||
|
||||
try
|
||||
{
|
||||
cachedAccount = await _profileService.GetActiveAccountAsync().ConfigureAwait(false);
|
||||
|
||||
if (cachedAccount != null)
|
||||
{
|
||||
await ApplyAccountStateAsync(cachedAccount).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await ExecuteUIThread(() => IsBusy = true);
|
||||
await ResetAddOnStatesAsync().ConfigureAwait(false);
|
||||
var loadAiPackTask = LoadAiPackAddOnAsync();
|
||||
var loadUnlimitedAccountsTask = LoadUnlimitedAccountsAddOnAsync();
|
||||
|
||||
var resolvedAccount = cachedAccount;
|
||||
|
||||
if (cachedAccount == null || IsAccessTokenExpired(cachedAccount))
|
||||
{
|
||||
try
|
||||
{
|
||||
var account = await _profileService.GetAuthenticatedAccountAsync().ConfigureAwait(false);
|
||||
if (account != null)
|
||||
{
|
||||
resolvedAccount = account;
|
||||
|
||||
var refreshedProfileResult = await _profileService.RefreshProfileAsync().ConfigureAwait(false);
|
||||
if (refreshedProfileResult.IsSuccess && refreshedProfileResult.Account != null)
|
||||
{
|
||||
resolvedAccount = refreshedProfileResult.Account;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
resolvedAccount ??= cachedAccount;
|
||||
}
|
||||
}
|
||||
|
||||
await ApplyAccountStateAsync(resolvedAccount).ConfigureAwait(false);
|
||||
await Task.WhenAll(loadAiPackTask, loadUnlimitedAccountsTask).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (cachedAccount == null)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Error,
|
||||
Translator.WinoAccount_Management_LoadFailed,
|
||||
InfoBarMessageType.Error);
|
||||
await ResetStateAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
await ExecuteUIThread(() => IsBusy = false);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ApplyAccountStateAsync(Wino.Core.Domain.Entities.Shared.WinoAccount? account)
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
IsSignedIn = account != null;
|
||||
AccountEmail = account?.Email ?? string.Empty;
|
||||
AccountStatusText = account == null
|
||||
? string.Empty
|
||||
: string.Format(Translator.WinoAccount_Management_StatusLabel, account.AccountStatus);
|
||||
});
|
||||
}
|
||||
|
||||
private async Task HandleAddOnPurchasedAsync()
|
||||
{
|
||||
await LoadAsync().ConfigureAwait(false);
|
||||
|
||||
_dialogService.InfoBarMessage(Translator.Info_PurchaseThankYouTitle,
|
||||
Translator.Info_PurchaseThankYouMessage,
|
||||
InfoBarMessageType.Success);
|
||||
}
|
||||
|
||||
private async Task ResetStateAsync()
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
IsSignedIn = false;
|
||||
AccountEmail = string.Empty;
|
||||
AccountStatusText = string.Empty;
|
||||
IsCheckoutInProgress = false;
|
||||
PurchaseAddOnCommand.NotifyCanExecuteChanged();
|
||||
});
|
||||
|
||||
await ResetAddOnStatesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private WinoAddOnItemViewModel CreateAddOnItem(WinoAddOnProductType productType)
|
||||
{
|
||||
return new WinoAddOnItemViewModel(productType)
|
||||
{
|
||||
PurchaseCommand = PurchaseAddOnCommand,
|
||||
UsageLimit = 1
|
||||
};
|
||||
}
|
||||
|
||||
private async Task ResetAddOnStatesAsync()
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
ResetAddOnItem(_aiPackAddOn);
|
||||
ResetAddOnItem(_unlimitedAccountsAddOn);
|
||||
PurchaseAddOnCommand.NotifyCanExecuteChanged();
|
||||
});
|
||||
}
|
||||
|
||||
private static void ResetAddOnItem(WinoAddOnItemViewModel addOn)
|
||||
{
|
||||
addOn.IsLoading = true;
|
||||
addOn.IsPurchased = false;
|
||||
addOn.IsPurchaseInProgress = false;
|
||||
addOn.HasUsageData = false;
|
||||
addOn.ErrorText = string.Empty;
|
||||
addOn.UsageCount = 0;
|
||||
addOn.UsageLimit = 1;
|
||||
addOn.UsagePercentage = 0;
|
||||
addOn.RenewalText = string.Empty;
|
||||
addOn.UsageResetText = string.Empty;
|
||||
}
|
||||
|
||||
private static string TranslateStoreSyncError(string? errorCode)
|
||||
=> errorCode switch
|
||||
{
|
||||
_ => Translator.WinoAccount_Management_StoreSyncFailed
|
||||
};
|
||||
|
||||
private static string BuildExportSuccessMessage(WinoAccountSyncExportResult result)
|
||||
{
|
||||
var parts = new Collection<string>();
|
||||
|
||||
if (result.IncludedPreferences)
|
||||
{
|
||||
parts.Add(Translator.WinoAccount_Management_ExportPreferencesSucceeded);
|
||||
}
|
||||
|
||||
if (result.IncludedAccounts)
|
||||
{
|
||||
parts.Add(string.Format(Translator.WinoAccount_Management_ExportAccountsSucceeded, result.ExportedMailboxCount));
|
||||
}
|
||||
|
||||
if (parts.Count == 0)
|
||||
{
|
||||
parts.Add(Translator.WinoAccount_Management_ExportSucceeded);
|
||||
}
|
||||
|
||||
return string.Join(" ", parts);
|
||||
}
|
||||
|
||||
private static string BuildImportMessage(WinoAccountSyncImportResult result)
|
||||
{
|
||||
var parts = new Collection<string>();
|
||||
|
||||
if (result.HadRemotePreferences)
|
||||
{
|
||||
parts.Add(result.FailedPreferenceCount > 0
|
||||
? string.Format(Translator.WinoAccount_Management_ImportPartial, result.AppliedPreferenceCount, result.FailedPreferenceCount)
|
||||
: string.Format(Translator.WinoAccount_Management_ImportPreferencesSucceeded, result.AppliedPreferenceCount));
|
||||
}
|
||||
|
||||
if (result.ImportedMailboxCount > 0)
|
||||
{
|
||||
parts.Add(string.Format(Translator.WinoAccount_Management_ImportAccountsSucceeded, result.ImportedMailboxCount));
|
||||
}
|
||||
|
||||
if (result.SkippedDuplicateMailboxCount > 0)
|
||||
{
|
||||
parts.Add(string.Format(Translator.WinoAccount_Management_ImportDuplicateAccountsSkipped, result.SkippedDuplicateMailboxCount));
|
||||
}
|
||||
|
||||
if (parts.Count == 0)
|
||||
{
|
||||
parts.Add(Translator.WinoAccount_Management_ImportEmpty);
|
||||
}
|
||||
|
||||
if (result.ImportedMailboxCount > 0)
|
||||
{
|
||||
parts.Add(Translator.WinoAccount_Management_ImportReloginReminder);
|
||||
}
|
||||
|
||||
return string.Join(" ", parts);
|
||||
}
|
||||
|
||||
private static bool IsAccessTokenExpired(WinoAccount account)
|
||||
=> string.IsNullOrWhiteSpace(account.AccessToken) || account.AccessTokenExpiresAtUtc <= DateTime.UtcNow;
|
||||
|
||||
private async Task LoadUnlimitedAccountsAddOnAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var hasUnlimitedAccounts = await _storeManagementService.HasProductAsync(WinoAddOnProductType.UNLIMITED_ACCOUNTS).ConfigureAwait(false);
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
_unlimitedAccountsAddOn.IsPurchased = hasUnlimitedAccounts;
|
||||
_unlimitedAccountsAddOn.ErrorText = string.Empty;
|
||||
});
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
_unlimitedAccountsAddOn.ErrorText = Translator.WinoAccount_Management_AddOnLoadFailed;
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
_unlimitedAccountsAddOn.IsLoading = false;
|
||||
PurchaseAddOnCommand.NotifyCanExecuteChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadAiPackAddOnAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var hasAiPack = await _storeManagementService.HasProductAsync(WinoAddOnProductType.AI_PACK).ConfigureAwait(false);
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
_aiPackAddOn.IsPurchased = hasAiPack;
|
||||
_aiPackAddOn.ErrorText = string.Empty;
|
||||
});
|
||||
|
||||
if (!hasAiPack)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var aiStatusResponse = await _profileService.GetAiStatusAsync().ConfigureAwait(false);
|
||||
if (!aiStatusResponse.IsSuccess || aiStatusResponse.Result == null)
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
_aiPackAddOn.HasUsageData = false;
|
||||
_aiPackAddOn.ErrorText = Translator.WinoAccount_Management_AiPackUsageLoadFailed;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var aiStatus = aiStatusResponse.Result;
|
||||
if (IsExpiredAiEntitlement(aiStatus.EntitlementStatus))
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
_aiPackAddOn.IsPurchased = false;
|
||||
_aiPackAddOn.HasUsageData = false;
|
||||
_aiPackAddOn.ErrorText = string.Empty;
|
||||
_aiPackAddOn.RenewalText = string.Empty;
|
||||
_aiPackAddOn.UsageResetText = string.Empty;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (aiStatus.MonthlyLimit is not int usageLimit || usageLimit <= 0 || aiStatus.Used is not int usageCount)
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
_aiPackAddOn.HasUsageData = false;
|
||||
_aiPackAddOn.ErrorText = Translator.WinoAccount_Management_AiPackUsageLoadFailed;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
_aiPackAddOn.HasUsageData = true;
|
||||
_aiPackAddOn.ErrorText = string.Empty;
|
||||
_aiPackAddOn.UsageCount = usageCount;
|
||||
_aiPackAddOn.UsageLimit = usageLimit;
|
||||
_aiPackAddOn.UsagePercentage = usageLimit > 0 ? (double)usageCount / usageLimit * 100 : 0;
|
||||
_aiPackAddOn.RenewalText = aiStatus.CurrentPeriodEndUtc is DateTimeOffset renewalDateUtc
|
||||
? string.Format(Translator.WinoAccount_Management_AiPackRenews, renewalDateUtc.LocalDateTime)
|
||||
: string.Empty;
|
||||
_aiPackAddOn.UsageResetText = aiStatus.CurrentPeriodEndUtc is DateTimeOffset resetDateUtc
|
||||
? string.Format(Translator.WinoAccount_Management_AiPackResets, resetDateUtc.LocalDateTime)
|
||||
: string.Empty;
|
||||
});
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
_aiPackAddOn.HasUsageData = false;
|
||||
_aiPackAddOn.ErrorText = Translator.WinoAccount_Management_AddOnLoadFailed;
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
_aiPackAddOn.IsLoading = false;
|
||||
PurchaseAddOnCommand.NotifyCanExecuteChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsExpiredAiEntitlement(string? entitlementStatus)
|
||||
=> string.Equals(entitlementStatus, "Expired", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
Reference in New Issue
Block a user