Settings home refactoring.
This commit is contained in:
@@ -74,4 +74,5 @@ public interface IStatePersistanceService : INotifyPropertyChanged
|
||||
/// Setting: Calendar display count for the day view.
|
||||
/// </summary>
|
||||
int DayDisplayCount { get; set; }
|
||||
|
||||
}
|
||||
|
||||
@@ -7,11 +7,16 @@ 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.Navigation;
|
||||
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.ViewModels.Data;
|
||||
using Wino.Messaging.Client.Navigation;
|
||||
|
||||
namespace Wino.Core.ViewModels;
|
||||
@@ -22,6 +27,12 @@ public partial class SettingOptionsPageViewModel : CoreBaseViewModel
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly IMimeStorageService _mimeStorageService;
|
||||
private readonly IStoreRatingService _storeRatingService;
|
||||
private readonly ITranslationService _translationService;
|
||||
private readonly INewThemeService _newThemeService;
|
||||
private readonly IPreferencesService _preferencesService;
|
||||
private readonly IProviderService _providerService;
|
||||
private bool _isInitializingSettings;
|
||||
private bool _isAppearanceSelectionPaused;
|
||||
|
||||
public string GitHubUrl => AppUrls.GitHub;
|
||||
public string PaypalUrl => AppUrls.Paypal;
|
||||
@@ -29,6 +40,17 @@ public partial class SettingOptionsPageViewModel : CoreBaseViewModel
|
||||
public string PrivacyPolicyUrl => AppUrls.PrivacyPolicy;
|
||||
|
||||
public ObservableCollection<SettingsNavigationItemInfo> SearchSuggestions { get; } = [];
|
||||
public ObservableCollection<IAccountProviderDetailViewModel> Accounts { get; } = [];
|
||||
public ObservableCollection<AppColorViewModel> Colors { get; } = [];
|
||||
|
||||
public List<ElementThemeContainer> ElementThemes { get; } =
|
||||
[
|
||||
new(ApplicationElementTheme.Light, Translator.ElementTheme_Light),
|
||||
new(ApplicationElementTheme.Dark, Translator.ElementTheme_Dark),
|
||||
new(ApplicationElementTheme.Default, Translator.ElementTheme_Default),
|
||||
];
|
||||
|
||||
public bool HasAccounts => Accounts.Count > 0;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string VersionText { get; set; } = string.Empty;
|
||||
@@ -45,15 +67,38 @@ public partial class SettingOptionsPageViewModel : CoreBaseViewModel
|
||||
[ObservableProperty]
|
||||
public partial string SearchQuery { get; set; } = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial List<AppLanguageModel> AvailableLanguages { get; set; } = [];
|
||||
|
||||
[ObservableProperty]
|
||||
public partial AppLanguageModel SelectedLanguage { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ElementThemeContainer SelectedElementTheme { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial AppColorViewModel SelectedAppColor { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool UseAccentColor { get; set; }
|
||||
|
||||
public SettingOptionsPageViewModel(INativeAppService nativeAppService,
|
||||
IAccountService accountService,
|
||||
IMimeStorageService mimeStorageService,
|
||||
IStoreRatingService storeRatingService)
|
||||
IAccountService accountService,
|
||||
IMimeStorageService mimeStorageService,
|
||||
IStoreRatingService storeRatingService,
|
||||
ITranslationService translationService,
|
||||
INewThemeService newThemeService,
|
||||
IPreferencesService preferencesService,
|
||||
IProviderService providerService)
|
||||
{
|
||||
_nativeAppService = nativeAppService;
|
||||
_accountService = accountService;
|
||||
_mimeStorageService = mimeStorageService;
|
||||
_storeRatingService = storeRatingService;
|
||||
_translationService = translationService;
|
||||
_newThemeService = newThemeService;
|
||||
_preferencesService = preferencesService;
|
||||
_providerService = providerService;
|
||||
}
|
||||
|
||||
public override void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
@@ -64,6 +109,7 @@ public partial class SettingOptionsPageViewModel : CoreBaseViewModel
|
||||
SearchQuery = string.Empty;
|
||||
SearchSuggestions.Clear();
|
||||
StorageSummaryText = Translator.SettingsHome_StorageLoading;
|
||||
InitializeQuickSettings();
|
||||
|
||||
_ = LoadDashboardAsync();
|
||||
}
|
||||
@@ -91,19 +137,54 @@ public partial class SettingOptionsPageViewModel : CoreBaseViewModel
|
||||
}
|
||||
}
|
||||
|
||||
public void NavigateToAccount(IAccountProviderDetailViewModel account)
|
||||
{
|
||||
if (account == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Messenger.Send(new BreadcrumbNavigationRequested(Translator.SettingsManageAccountSettings_Title, WinoPage.ManageAccountsPage));
|
||||
|
||||
switch (account)
|
||||
{
|
||||
case AccountProviderDetailViewModel accountDetails:
|
||||
Messenger.Send(new BreadcrumbNavigationRequested(accountDetails.Account.Name, WinoPage.AccountDetailsPage, accountDetails.Account.Id));
|
||||
break;
|
||||
case MergedAccountProviderDetailViewModel mergedAccount:
|
||||
Messenger.Send(new BreadcrumbNavigationRequested(mergedAccount.MergedInbox.Name, WinoPage.MergedAccountDetailsPage, mergedAccount));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void NavigateToAddAccount()
|
||||
{
|
||||
Messenger.Send(new BreadcrumbNavigationRequested(Translator.SettingsManageAccountSettings_Title, WinoPage.ManageAccountsPage));
|
||||
Messenger.Send(new BreadcrumbNavigationRequested(Translator.WelcomeWizard_Step2Title, WinoPage.ProviderSelectionPage));
|
||||
}
|
||||
|
||||
private async Task LoadDashboardAsync()
|
||||
{
|
||||
var accounts = await _accountService.GetAccountsAsync().ConfigureAwait(false) ?? [];
|
||||
var accounts = (await _accountService.GetAccountsAsync().ConfigureAwait(false) ?? []).ToList();
|
||||
var count = accounts.Count;
|
||||
Dictionary<Guid, long> storageSizeMap = count == 0
|
||||
? []
|
||||
: await _mimeStorageService.GetAccountsMimeStorageSizesAsync(accounts.Select(account => account.Id)).ConfigureAwait(false);
|
||||
var totalStorageBytes = storageSizeMap.Values.Sum();
|
||||
var groupedAccountItems = CreateAccountItems(accounts);
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
AccountCount = count;
|
||||
AccountSummaryText = string.Format(Translator.SettingsOptions_AccountsSummary, count);
|
||||
Accounts.Clear();
|
||||
|
||||
foreach (var account in groupedAccountItems)
|
||||
{
|
||||
Accounts.Add(account);
|
||||
}
|
||||
|
||||
OnPropertyChanged(nameof(HasAccounts));
|
||||
StorageSummaryText = totalStorageBytes == 0
|
||||
? Translator.SettingsHome_StorageEmptySummary
|
||||
: string.Format(Translator.SettingsStorage_TotalUsage, totalStorageBytes.GetBytesReadable());
|
||||
@@ -115,6 +196,175 @@ public partial class SettingOptionsPageViewModel : CoreBaseViewModel
|
||||
});
|
||||
}
|
||||
|
||||
private void InitializeQuickSettings()
|
||||
{
|
||||
_isInitializingSettings = true;
|
||||
InitializeColors();
|
||||
InitializeLanguageOptions();
|
||||
InitializeAppearanceOptions();
|
||||
_isInitializingSettings = false;
|
||||
}
|
||||
|
||||
private void InitializeLanguageOptions()
|
||||
{
|
||||
AvailableLanguages = _translationService.GetAvailableLanguages();
|
||||
SelectedLanguage = AvailableLanguages.FirstOrDefault(language => language.Language == _preferencesService.CurrentLanguage)
|
||||
?? AvailableLanguages.FirstOrDefault();
|
||||
}
|
||||
|
||||
private void InitializeColors()
|
||||
{
|
||||
Colors.Clear();
|
||||
|
||||
foreach (var color in _newThemeService.GetAvailableAccountColors().Distinct(StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
Colors.Add(new AppColorViewModel(color));
|
||||
}
|
||||
|
||||
var systemAccentColor = _newThemeService.GetSystemAccentColorHex();
|
||||
|
||||
if (Colors.All(color => !string.Equals(color.Hex, systemAccentColor, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
Colors.Add(new AppColorViewModel(systemAccentColor, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
var matchingAccentColor = Colors.First(color => string.Equals(color.Hex, systemAccentColor, StringComparison.OrdinalIgnoreCase));
|
||||
Colors.Remove(matchingAccentColor);
|
||||
Colors.Add(new AppColorViewModel(systemAccentColor, true));
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeAppearanceOptions()
|
||||
{
|
||||
_isAppearanceSelectionPaused = true;
|
||||
|
||||
var currentAccentColor = _newThemeService.AccentColor;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(currentAccentColor) &&
|
||||
Colors.All(color => !string.Equals(color.Hex, currentAccentColor, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
Colors.Insert(0, new AppColorViewModel(currentAccentColor));
|
||||
}
|
||||
|
||||
SelectedElementTheme = ElementThemes.FirstOrDefault(theme => theme.NativeTheme == _newThemeService.RootTheme)
|
||||
?? ElementThemes.LastOrDefault();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(currentAccentColor))
|
||||
{
|
||||
SelectedAppColor = Colors.LastOrDefault(color => color.IsAccentColor) ?? Colors.LastOrDefault();
|
||||
UseAccentColor = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedAppColor = Colors.FirstOrDefault(color => string.Equals(color.Hex, currentAccentColor, StringComparison.OrdinalIgnoreCase))
|
||||
?? Colors.FirstOrDefault();
|
||||
UseAccentColor = SelectedAppColor?.IsAccentColor == true;
|
||||
}
|
||||
|
||||
_isAppearanceSelectionPaused = false;
|
||||
}
|
||||
|
||||
private List<IAccountProviderDetailViewModel> CreateAccountItems(List<MailAccount> accounts)
|
||||
{
|
||||
var groupedAccounts = accounts
|
||||
.OrderBy(account => account.MergedInboxId == null ? 1 : 0)
|
||||
.ThenBy(account => account.Order)
|
||||
.ThenBy(account => account.Name)
|
||||
.GroupBy(account => account.MergedInboxId);
|
||||
var accountItems = new List<IAccountProviderDetailViewModel>();
|
||||
|
||||
foreach (var accountGroup in groupedAccounts)
|
||||
{
|
||||
if (accountGroup.Key == null)
|
||||
{
|
||||
accountItems.AddRange(accountGroup.Select(CreateAccountProviderDetails));
|
||||
continue;
|
||||
}
|
||||
|
||||
var mergedInbox = accountGroup.First().MergedInbox;
|
||||
var holdingAccounts = accountGroup
|
||||
.Select(CreateAccountProviderDetails)
|
||||
.ToList();
|
||||
var mergedAccount = new MergedAccountProviderDetailViewModel(mergedInbox, holdingAccounts)
|
||||
{
|
||||
ProviderDetail = holdingAccounts.FirstOrDefault()?.ProviderDetail
|
||||
};
|
||||
|
||||
accountItems.Add(mergedAccount);
|
||||
}
|
||||
|
||||
return accountItems;
|
||||
}
|
||||
|
||||
private AccountProviderDetailViewModel CreateAccountProviderDetails(MailAccount account)
|
||||
{
|
||||
var provider = _providerService.GetProviderDetail(account.ProviderType);
|
||||
return new AccountProviderDetailViewModel(provider, account);
|
||||
}
|
||||
|
||||
partial void OnSelectedLanguageChanged(AppLanguageModel value)
|
||||
{
|
||||
if (_isInitializingSettings || value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_ = ApplyLanguageAsync(value);
|
||||
}
|
||||
|
||||
partial void OnSelectedElementThemeChanged(ElementThemeContainer value)
|
||||
{
|
||||
if (_isInitializingSettings || value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_newThemeService.RootTheme = value.NativeTheme;
|
||||
}
|
||||
|
||||
partial void OnSelectedAppColorChanged(AppColorViewModel value)
|
||||
{
|
||||
if (_isInitializingSettings || _isAppearanceSelectionPaused || value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isAppearanceSelectionPaused = true;
|
||||
UseAccentColor = value.IsAccentColor;
|
||||
_isAppearanceSelectionPaused = false;
|
||||
|
||||
_newThemeService.AccentColor = value.Hex;
|
||||
}
|
||||
|
||||
partial void OnUseAccentColorChanged(bool value)
|
||||
{
|
||||
if (_isInitializingSettings || _isAppearanceSelectionPaused || Colors.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var accentColor = Colors.LastOrDefault(color => color.IsAccentColor);
|
||||
var fallbackColor = Colors.FirstOrDefault(color => !color.IsAccentColor) ?? Colors.FirstOrDefault();
|
||||
var targetColor = value ? accentColor : SelectedAppColor?.IsAccentColor == true ? fallbackColor : SelectedAppColor;
|
||||
|
||||
if (targetColor == null || ReferenceEquals(targetColor, SelectedAppColor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isAppearanceSelectionPaused = true;
|
||||
SelectedAppColor = targetColor;
|
||||
_isAppearanceSelectionPaused = false;
|
||||
|
||||
_newThemeService.AccentColor = targetColor.Hex;
|
||||
}
|
||||
|
||||
private async Task ApplyLanguageAsync(AppLanguageModel language)
|
||||
{
|
||||
await _translationService.InitializeLanguageAsync(language.Language);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
public void NavigateSubDetail(object type)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using Wino.Core.Domain.Entities.Mail;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Mail.ViewModels.Collections;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
@@ -112,6 +113,48 @@ public class WinoMailCollectionTests
|
||||
threadItems.Should().Contain(item => item.ThreadId == "thread-c" && item.EmailCount == 2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateMailCopy_ShouldMergeExistingSingles_WhenThreadIdChangesToMatch()
|
||||
{
|
||||
var sut = CreateCollection();
|
||||
var first = CreateMailCopy(threadId: "shared-thread", creationDate: DateTime.UtcNow.AddMinutes(-1));
|
||||
var second = CreateMailCopy(threadId: string.Empty, creationDate: DateTime.UtcNow);
|
||||
|
||||
await sut.AddAsync(first);
|
||||
await sut.AddAsync(second);
|
||||
|
||||
var updatedSecond = CloneMailCopy(second);
|
||||
updatedSecond.ThreadId = "shared-thread";
|
||||
|
||||
await sut.UpdateMailCopy(updatedSecond, MailUpdateSource.Server, MailCopyChangeFlags.ThreadId);
|
||||
|
||||
var items = FlattenItems(sut);
|
||||
var threadItem = items.Should().ContainSingle().Which.Should().BeOfType<ThreadMailItemViewModel>().Subject;
|
||||
threadItem.EmailCount.Should().Be(2);
|
||||
threadItem.GetContainingIds().Should().BeEquivalentTo([first.UniqueId, second.UniqueId]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddAsync_ShouldThreadWithUpdatedItem_WhenThreadIdWasSetByPriorUpdate()
|
||||
{
|
||||
var sut = CreateCollection();
|
||||
var existing = CreateMailCopy(threadId: string.Empty, creationDate: DateTime.UtcNow.AddMinutes(-1));
|
||||
var incoming = CreateMailCopy(threadId: "shared-thread", creationDate: DateTime.UtcNow);
|
||||
|
||||
await sut.AddAsync(existing);
|
||||
|
||||
var updatedExisting = CloneMailCopy(existing);
|
||||
updatedExisting.ThreadId = "shared-thread";
|
||||
|
||||
await sut.UpdateMailCopy(updatedExisting, MailUpdateSource.Server, MailCopyChangeFlags.ThreadId);
|
||||
await sut.AddAsync(incoming);
|
||||
|
||||
var items = FlattenItems(sut);
|
||||
var threadItem = items.Should().ContainSingle().Which.Should().BeOfType<ThreadMailItemViewModel>().Subject;
|
||||
threadItem.EmailCount.Should().Be(2);
|
||||
threadItem.GetContainingIds().Should().BeEquivalentTo([existing.UniqueId, incoming.UniqueId]);
|
||||
}
|
||||
|
||||
private static WinoMailCollection CreateCollection() => new()
|
||||
{
|
||||
CoreDispatcher = new ImmediateDispatcher()
|
||||
@@ -146,6 +189,35 @@ public class WinoMailCollectionTests
|
||||
FolderId = Guid.NewGuid()
|
||||
};
|
||||
|
||||
private static MailCopy CloneMailCopy(MailCopy source)
|
||||
=> new()
|
||||
{
|
||||
UniqueId = source.UniqueId,
|
||||
Id = source.Id,
|
||||
FolderId = source.FolderId,
|
||||
ThreadId = source.ThreadId,
|
||||
MessageId = source.MessageId,
|
||||
References = source.References,
|
||||
InReplyTo = source.InReplyTo,
|
||||
FromName = source.FromName,
|
||||
FromAddress = source.FromAddress,
|
||||
Subject = source.Subject,
|
||||
PreviewText = source.PreviewText,
|
||||
CreationDate = source.CreationDate,
|
||||
Importance = source.Importance,
|
||||
IsRead = source.IsRead,
|
||||
IsFlagged = source.IsFlagged,
|
||||
IsFocused = source.IsFocused,
|
||||
HasAttachments = source.HasAttachments,
|
||||
ItemType = source.ItemType,
|
||||
DraftId = source.DraftId,
|
||||
IsDraft = source.IsDraft,
|
||||
FileId = source.FileId,
|
||||
SenderContact = source.SenderContact,
|
||||
AssignedAccount = source.AssignedAccount,
|
||||
AssignedFolder = source.AssignedFolder
|
||||
};
|
||||
|
||||
private sealed class ImmediateDispatcher : IDispatcher
|
||||
{
|
||||
public Task ExecuteOnUIThread(Action action)
|
||||
|
||||
@@ -213,14 +213,37 @@ public class WinoMailCollection : ObservableRecipient, IRecipient<SelectedItemsC
|
||||
}
|
||||
}
|
||||
|
||||
private IMailListItem FindThreadableItem(string threadId)
|
||||
private IMailListItem FindThreadableItem(string threadId, Guid? excludedUniqueId = null, IMailListItem excludedItem = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(threadId) || !_threadIdToItemsMap.TryGetValue(threadId, out var items))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return items.FirstOrDefault();
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (ReferenceEquals(item, excludedItem))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (excludedUniqueId.HasValue)
|
||||
{
|
||||
if (item is MailItemViewModel mailItem && mailItem.MailCopy.UniqueId == excludedUniqueId.Value)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item is ThreadMailItemViewModel threadItem && threadItem.HasUniqueId(excludedUniqueId.Value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -380,7 +403,8 @@ public class WinoMailCollection : ObservableRecipient, IRecipient<SelectedItemsC
|
||||
{
|
||||
if (item.MailCopy.UniqueId == addedItem.UniqueId)
|
||||
{
|
||||
await UpdateExistingItemAsync(item, addedItem);
|
||||
var existingItemContainer = GetMailItemContainer(addedItem.UniqueId);
|
||||
await UpdateExistingItemAsync(existingItemContainer, addedItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -419,7 +443,7 @@ public class WinoMailCollection : ObservableRecipient, IRecipient<SelectedItemsC
|
||||
var existingItemContainer = GetMailItemContainer(addedItem.UniqueId);
|
||||
if (existingItemContainer?.ItemViewModel != null)
|
||||
{
|
||||
await UpdateExistingItemAsync(existingItemContainer.ItemViewModel, addedItem);
|
||||
await UpdateExistingItemAsync(existingItemContainer, addedItem);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -478,16 +502,83 @@ public class WinoMailCollection : ObservableRecipient, IRecipient<SelectedItemsC
|
||||
await InsertItemInternalAsync(groupKey, newMailItem);
|
||||
}
|
||||
|
||||
private async Task UpdateExistingItemAsync(MailItemViewModel existingItem, MailCopy updatedItem)
|
||||
private async Task ReinsertUpdatedItemAsync(MailCopy updatedItem, bool isSelected, bool isBusy)
|
||||
{
|
||||
UpdateUniqueIdHashes(existingItem, false);
|
||||
await RemoveAsync(updatedItem);
|
||||
await AddAsync(updatedItem);
|
||||
|
||||
var updatedContainer = GetMailItemContainer(updatedItem.UniqueId);
|
||||
if (updatedContainer?.ItemViewModel == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
existingItem.UpdateFrom(updatedItem);
|
||||
updatedContainer.ItemViewModel.IsSelected = isSelected;
|
||||
updatedContainer.ItemViewModel.IsBusy = isBusy;
|
||||
});
|
||||
}
|
||||
|
||||
private async Task UpdateExistingItemAsync(MailItemContainer itemContainer,
|
||||
MailCopy updatedItem,
|
||||
MailUpdateSource mailUpdateSource = MailUpdateSource.Server,
|
||||
MailCopyChangeFlags changeHint = MailCopyChangeFlags.None)
|
||||
{
|
||||
if (itemContainer?.ItemViewModel == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var existingItem = itemContainer.ItemViewModel;
|
||||
var threadOwner = itemContainer.ThreadViewModel as IMailListItem ?? existingItem;
|
||||
var wasSelected = existingItem.IsSelected;
|
||||
MailCopyChangeFlags appliedChanges = MailCopyChangeFlags.None;
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
UpdateUniqueIdHashes(existingItem, false);
|
||||
UpdateThreadIdCache(threadOwner, false);
|
||||
|
||||
itemContainer.ThreadViewModel?.SuspendChildPropertyNotifications();
|
||||
|
||||
try
|
||||
{
|
||||
appliedChanges = existingItem.UpdateFrom(updatedItem, changeHint);
|
||||
}
|
||||
finally
|
||||
{
|
||||
itemContainer.ThreadViewModel?.ResumeChildPropertyNotifications();
|
||||
}
|
||||
|
||||
existingItem.IsBusy = mailUpdateSource == MailUpdateSource.ClientUpdated;
|
||||
|
||||
UpdateUniqueIdHashes(existingItem, true);
|
||||
UpdateThreadIdCache(threadOwner, true);
|
||||
|
||||
if (itemContainer.ThreadViewModel != null)
|
||||
{
|
||||
_uniqueIdToThreadMap[existingItem.MailCopy.UniqueId] = itemContainer.ThreadViewModel;
|
||||
}
|
||||
else
|
||||
{
|
||||
_uniqueIdToThreadMap.TryRemove(existingItem.MailCopy.UniqueId, out _);
|
||||
}
|
||||
});
|
||||
|
||||
UpdateUniqueIdHashes(existingItem, true);
|
||||
if ((appliedChanges & MailCopyChangeFlags.ThreadId) != 0)
|
||||
{
|
||||
await ReinsertUpdatedItemAsync(updatedItem, wasSelected, existingItem.IsBusy);
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemContainer.ThreadViewModel != null && appliedChanges != MailCopyChangeFlags.None)
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
itemContainer.ThreadViewModel.NotifyMailItemUpdated(existingItem, appliedChanges);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -755,45 +846,14 @@ public class WinoMailCollection : ObservableRecipient, IRecipient<SelectedItemsC
|
||||
/// <returns></returns>
|
||||
public Task UpdateMailCopy(MailCopy updatedMailCopy, MailUpdateSource mailUpdateSource, MailCopyChangeFlags changedProperties = MailCopyChangeFlags.None)
|
||||
{
|
||||
return ExecuteUIThread(() =>
|
||||
var itemContainer = GetMailItemContainer(updatedMailCopy.UniqueId);
|
||||
|
||||
if (itemContainer?.ItemViewModel == null)
|
||||
{
|
||||
var itemContainer = GetMailItemContainer(updatedMailCopy.UniqueId);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (itemContainer == null) return;
|
||||
MailCopyChangeFlags appliedChanges = MailCopyChangeFlags.None;
|
||||
|
||||
if (itemContainer.ItemViewModel != null)
|
||||
{
|
||||
UpdateUniqueIdHashes(itemContainer.ItemViewModel, false);
|
||||
|
||||
itemContainer.ThreadViewModel?.SuspendChildPropertyNotifications();
|
||||
|
||||
try
|
||||
{
|
||||
appliedChanges = itemContainer.ItemViewModel.UpdateFrom(updatedMailCopy, changedProperties);
|
||||
}
|
||||
finally
|
||||
{
|
||||
itemContainer.ThreadViewModel?.ResumeChildPropertyNotifications();
|
||||
}
|
||||
|
||||
// Mark the item view model as busy until the network operation is completed.
|
||||
itemContainer.ItemViewModel.IsBusy = mailUpdateSource == MailUpdateSource.ClientUpdated;
|
||||
|
||||
UpdateUniqueIdHashes(itemContainer.ItemViewModel, true);
|
||||
|
||||
// Keep thread membership cache in sync for items rendered inside thread containers.
|
||||
if (itemContainer.ThreadViewModel != null)
|
||||
{
|
||||
_uniqueIdToThreadMap[itemContainer.ItemViewModel.MailCopy.UniqueId] = itemContainer.ThreadViewModel;
|
||||
}
|
||||
}
|
||||
|
||||
if (itemContainer.ThreadViewModel != null && appliedChanges != MailCopyChangeFlags.None)
|
||||
{
|
||||
itemContainer.ThreadViewModel.NotifyMailItemUpdated(itemContainer.ItemViewModel, appliedChanges);
|
||||
}
|
||||
});
|
||||
return UpdateExistingItemAsync(itemContainer, updatedMailCopy, mailUpdateSource, changedProperties);
|
||||
}
|
||||
|
||||
public MailItemViewModel GetFirst() => AllItems.ElementAtOrDefault(0);
|
||||
|
||||
@@ -12,22 +12,8 @@ public partial class MessageListPageViewModel : MailBaseViewModel
|
||||
{
|
||||
public IPreferencesService PreferencesService { get; }
|
||||
private readonly IThumbnailService _thumbnailService;
|
||||
|
||||
private int selectedMarkAsOptionIndex;
|
||||
public int SelectedMarkAsOptionIndex
|
||||
{
|
||||
get => selectedMarkAsOptionIndex;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref selectedMarkAsOptionIndex, value))
|
||||
{
|
||||
if (value >= 0)
|
||||
{
|
||||
PreferencesService.MarkAsPreference = (MailMarkAsOption)Enum.GetValues<MailMarkAsOption>().GetValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private readonly IStatePersistanceService _statePersistenceService;
|
||||
private readonly IDialogServiceBase _dialogService;
|
||||
|
||||
private readonly List<MailOperation> availableHoverActions =
|
||||
[
|
||||
@@ -38,6 +24,13 @@ public partial class MessageListPageViewModel : MailBaseViewModel
|
||||
MailOperation.MoveToJunk
|
||||
];
|
||||
|
||||
private readonly List<MailListDisplayMode> availableMailSpacingOptions =
|
||||
[
|
||||
MailListDisplayMode.Compact,
|
||||
MailListDisplayMode.Medium,
|
||||
MailListDisplayMode.Spacious
|
||||
];
|
||||
|
||||
public List<string> AvailableHoverActionsTranslations { get; set; } =
|
||||
[
|
||||
Translator.HoverActionOption_Archive,
|
||||
@@ -47,6 +40,32 @@ public partial class MessageListPageViewModel : MailBaseViewModel
|
||||
Translator.HoverActionOption_MoveJunk
|
||||
];
|
||||
|
||||
private int selectedMarkAsOptionIndex;
|
||||
public int SelectedMarkAsOptionIndex
|
||||
{
|
||||
get => selectedMarkAsOptionIndex;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref selectedMarkAsOptionIndex, value) && value >= 0)
|
||||
{
|
||||
PreferencesService.MarkAsPreference = (MailMarkAsOption)Enum.GetValues<MailMarkAsOption>().GetValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int selectedMailSpacingIndex;
|
||||
public int SelectedMailSpacingIndex
|
||||
{
|
||||
get => selectedMailSpacingIndex;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref selectedMailSpacingIndex, value) && value >= 0 && value < availableMailSpacingOptions.Count)
|
||||
{
|
||||
PreferencesService.MailItemDisplayMode = availableMailSpacingOptions[value];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Properties
|
||||
private int leftHoverActionIndex;
|
||||
public int LeftHoverActionIndex
|
||||
@@ -88,13 +107,19 @@ public partial class MessageListPageViewModel : MailBaseViewModel
|
||||
}
|
||||
#endregion
|
||||
|
||||
public MessageListPageViewModel(IPreferencesService preferencesService, IThumbnailService thumbnailService)
|
||||
public MessageListPageViewModel(IPreferencesService preferencesService,
|
||||
IThumbnailService thumbnailService,
|
||||
IStatePersistanceService statePersistenceService,
|
||||
IDialogServiceBase dialogService)
|
||||
{
|
||||
PreferencesService = preferencesService;
|
||||
_thumbnailService = thumbnailService;
|
||||
_statePersistenceService = statePersistenceService;
|
||||
_dialogService = dialogService;
|
||||
leftHoverActionIndex = availableHoverActions.IndexOf(PreferencesService.LeftHoverAction);
|
||||
centerHoverActionIndex = availableHoverActions.IndexOf(PreferencesService.CenterHoverAction);
|
||||
rightHoverActionIndex = availableHoverActions.IndexOf(PreferencesService.RightHoverAction);
|
||||
selectedMailSpacingIndex = availableMailSpacingOptions.IndexOf(PreferencesService.MailItemDisplayMode);
|
||||
SelectedMarkAsOptionIndex = Array.IndexOf(Enum.GetValues<MailMarkAsOption>(), PreferencesService.MarkAsPreference);
|
||||
}
|
||||
|
||||
@@ -103,4 +128,11 @@ public partial class MessageListPageViewModel : MailBaseViewModel
|
||||
{
|
||||
await _thumbnailService.ClearCache();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ResetMailListPaneLength()
|
||||
{
|
||||
_statePersistenceService.MailListPaneLength = 420;
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Info, Translator.Info_MailListSizeResetSuccessMessage, InfoBarMessageType.Success);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,7 +1,10 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using CommunityToolkit.WinUI.Controls;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Settings;
|
||||
using Wino.Core.ViewModels.Data;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Views.Abstract;
|
||||
|
||||
namespace Wino.Views.Settings;
|
||||
@@ -17,7 +20,7 @@ public sealed partial class SettingOptionsPage : SettingOptionsPageAbstract
|
||||
{
|
||||
WinoPage? page = sender switch
|
||||
{
|
||||
Button button when button.Tag is WinoPage p => p,
|
||||
FrameworkElement element when element.Tag is WinoPage p => p,
|
||||
_ => null
|
||||
};
|
||||
|
||||
@@ -27,6 +30,27 @@ public sealed partial class SettingOptionsPage : SettingOptionsPageAbstract
|
||||
}
|
||||
}
|
||||
|
||||
private void AccountSettingClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is SettingsCard settingsCard && settingsCard.CommandParameter is AccountProviderDetailViewModel account)
|
||||
{
|
||||
ViewModel.NavigateToAccount(account);
|
||||
}
|
||||
}
|
||||
|
||||
private void MergedAccountSettingClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is SettingsCard settingsCard && settingsCard.CommandParameter is MergedAccountProviderDetailViewModel account)
|
||||
{
|
||||
ViewModel.NavigateToAccount(account);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAccountSettingClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.NavigateToAddAccount();
|
||||
}
|
||||
|
||||
private void SettingsSearchTextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
|
||||
{
|
||||
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput || string.IsNullOrWhiteSpace(sender.Text))
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -184,37 +184,6 @@
|
||||
Foreground="{ThemeResource InfoBarWarningSeverityIconBackground}"
|
||||
Text="{x:Bind domain:Translator.SettingsWindowBackdrop_Disabled}" />
|
||||
|
||||
<!-- Mail spacing. -->
|
||||
<controls:SettingsExpander Description="{x:Bind domain:Translator.SettingsMailSpacing_Description}" Header="{x:Bind domain:Translator.SettingsMailSpacing_Title}">
|
||||
<controls:SettingsExpander.HeaderIcon>
|
||||
<PathIcon Data="F1 M 17.5 9.375 C 17.832031 9.375 18.149414 9.440104 18.452148 9.570312 C 18.754883 9.700521 19.020182 9.876303 19.248047 10.097656 C 19.47591 10.319011 19.658203 10.579428 19.794922 10.878906 C 19.931641 11.178386 20 11.494141 20 11.826172 L 20 15.048828 C 20 15.37435 19.933268 15.685222 19.799805 15.981445 C 19.66634 16.27767 19.487305 16.538086 19.262695 16.762695 C 19.038086 16.987305 18.777668 17.166342 18.481445 17.299805 C 18.185221 17.433268 17.874348 17.5 17.548828 17.5 L 2.451172 17.5 C 2.125651 17.5 1.814779 17.433268 1.518555 17.299805 C 1.222331 17.166342 0.961914 16.987305 0.737305 16.762695 C 0.512695 16.538086 0.333659 16.27767 0.200195 15.981445 C 0.066732 15.685222 0 15.37435 0 15.048828 L 0 4.951172 C 0 4.625651 0.066732 4.314779 0.200195 4.018555 C 0.333659 3.722332 0.512695 3.461914 0.737305 3.237305 C 0.961914 3.012695 1.222331 2.83366 1.518555 2.700195 C 1.814779 2.566732 2.125651 2.5 2.451172 2.5 L 12.548828 2.5 C 12.887369 2.5 13.203125 2.565105 13.496094 2.695312 C 13.789062 2.825521 14.044596 3.00293 14.262695 3.227539 C 14.480793 3.452148 14.654947 3.712566 14.785156 4.008789 C 14.915364 4.305014 14.986979 4.619141 15 4.951172 C 15.00651 5.022787 15.009766 5.094402 15.009766 5.166016 C 15.009766 5.237631 15.009766 5.309245 15.009766 5.380859 C 15.009766 5.5306 15.008138 5.677084 15.004883 5.820312 C 15.001627 5.963542 14.999999 6.106771 15 6.25 C 15.33203 6.25 15.649413 6.315104 15.952148 6.445312 C 16.254883 6.575521 16.520182 6.751303 16.748047 6.972656 C 16.97591 7.194011 17.156574 7.4528 17.290039 7.749023 C 17.423502 8.045248 17.493488 8.362631 17.5 8.701172 Z M 1.25 15 C 1.25 15.175781 1.282552 15.34017 1.347656 15.493164 C 1.41276 15.646159 1.500651 15.777995 1.611328 15.888672 C 1.722005 15.99935 1.853841 16.08724 2.006836 16.152344 C 2.159831 16.217449 2.324219 16.25 2.5 16.25 L 6.582031 16.25 C 6.360677 15.859375 6.25 15.442709 6.25 15 L 6.25 8.701172 C 6.25 8.362631 6.318359 8.045248 6.455078 7.749023 C 6.591797 7.4528 6.774088 7.194011 7.001953 6.972656 C 7.229817 6.751303 7.495117 6.575521 7.797852 6.445312 C 8.100586 6.315104 8.417969 6.25 8.75 6.25 L 13.75 6.25 L 13.75 5 C 13.75 4.830729 13.717447 4.669597 13.652344 4.516602 C 13.587239 4.363607 13.497721 4.230144 13.383789 4.116211 C 13.269855 4.002279 13.136393 3.912762 12.983398 3.847656 C 12.830403 3.782553 12.669271 3.75 12.5 3.75 L 2.5 3.75 C 2.324219 3.75 2.161458 3.782553 2.011719 3.847656 C 1.861979 3.912762 1.730143 4.002279 1.616211 4.116211 C 1.502279 4.230144 1.41276 4.361979 1.347656 4.511719 C 1.282552 4.661459 1.25 4.82422 1.25 5 Z M 12.207031 16.25 C 11.985677 15.859375 11.875 15.442709 11.875 15 L 11.875 11.826172 C 11.875 11.487631 11.943359 11.170248 12.080078 10.874023 C 12.216797 10.5778 12.399088 10.319011 12.626953 10.097656 C 12.854817 9.876303 13.120117 9.700521 13.422852 9.570312 C 13.725586 9.440104 14.042969 9.375 14.375 9.375 L 16.25 9.375 L 16.25 8.75 C 16.25 8.580729 16.217447 8.419597 16.152344 8.266602 C 16.087238 8.113607 15.997721 7.980144 15.883789 7.866211 C 15.769856 7.752279 15.636393 7.662762 15.483398 7.597656 C 15.330403 7.532553 15.169271 7.5 15 7.5 L 8.75 7.5 C 8.574219 7.5 8.411458 7.532553 8.261719 7.597656 C 8.111979 7.662762 7.980143 7.752279 7.866211 7.866211 C 7.752278 7.980144 7.66276 8.111979 7.597656 8.261719 C 7.532552 8.411459 7.5 8.574219 7.5 8.75 L 7.5 15 C 7.5 15.175781 7.532552 15.34017 7.597656 15.493164 C 7.66276 15.646159 7.750651 15.777995 7.861328 15.888672 C 7.972005 15.99935 8.103841 16.08724 8.256836 16.152344 C 8.40983 16.217449 8.574219 16.25 8.75 16.25 Z M 18.75 11.875 C 18.75 11.705729 18.717447 11.544597 18.652344 11.391602 C 18.587238 11.238607 18.497721 11.105144 18.383789 10.991211 C 18.269855 10.877279 18.136393 10.787761 17.983398 10.722656 C 17.830402 10.657553 17.66927 10.625 17.5 10.625 L 14.375 10.625 C 14.199219 10.625 14.036458 10.657553 13.886719 10.722656 C 13.736979 10.787761 13.605143 10.877279 13.491211 10.991211 C 13.377278 11.105144 13.28776 11.236979 13.222656 11.386719 C 13.157551 11.536459 13.124999 11.699219 13.125 11.875 L 13.125 15 C 13.124999 15.175781 13.157551 15.34017 13.222656 15.493164 C 13.28776 15.646159 13.37565 15.777995 13.486328 15.888672 C 13.597004 15.99935 13.72884 16.08724 13.881836 16.152344 C 14.03483 16.217449 14.199219 16.25 14.375 16.25 L 17.5 16.25 C 17.675781 16.25 17.838541 16.217449 17.988281 16.152344 C 18.13802 16.08724 18.269855 15.997722 18.383789 15.883789 C 18.497721 15.769857 18.587238 15.638021 18.652344 15.488281 C 18.717447 15.338542 18.75 15.175781 18.75 15 Z " />
|
||||
</controls:SettingsExpander.HeaderIcon>
|
||||
|
||||
<controls:SettingsExpander.Items>
|
||||
<controls:SettingsCard
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
ContentAlignment="Vertical">
|
||||
<ListView
|
||||
Margin="-18,0"
|
||||
toolkitExt:ListViewExtensions.ItemContainerStretchDirection="Horizontal"
|
||||
ItemTemplateSelector="{StaticResource MailItemDisplayModePreviewTemplateSelector}"
|
||||
ItemsSource="{x:Bind ViewModel.InformationDisplayModes}"
|
||||
SelectedItem="{x:Bind ViewModel.SelectedInfoDisplayMode, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
</controls:SettingsExpander.Items>
|
||||
</controls:SettingsExpander>
|
||||
|
||||
<!-- Pane Length Reset -->
|
||||
<controls:SettingsCard Description="{x:Bind domain:Translator.SettingsPaneLengthReset_Description}" Header="{x:Bind domain:Translator.SettingsPaneLengthReset_Title}">
|
||||
<controls:SettingsCard.HeaderIcon>
|
||||
<PathIcon Data="F1 M 3.056641 17.5 C 2.646484 17.5 2.255859 17.416992 1.884766 17.250977 C 1.513672 17.084961 1.189779 16.863607 0.913086 16.586914 C 0.636393 16.310221 0.415039 15.986328 0.249023 15.615234 C 0.083008 15.244141 0 14.853516 0 14.443359 L 0 5.556641 C 0 5.146485 0.083008 4.75586 0.249023 4.384766 C 0.415039 4.013673 0.636393 3.689779 0.913086 3.413086 C 1.189779 3.136395 1.513672 2.915039 1.884766 2.749023 C 2.255859 2.583008 2.646484 2.5 3.056641 2.5 L 16.943359 2.5 C 17.353516 2.5 17.744141 2.583008 18.115234 2.749023 C 18.486328 2.915039 18.810221 3.136395 19.086914 3.413086 C 19.363605 3.689779 19.584961 4.013673 19.750977 4.384766 C 19.916992 4.75586 20 5.146485 20 5.556641 L 20 14.443359 C 20 14.853516 19.916992 15.244141 19.750977 15.615234 C 19.584961 15.986328 19.363605 16.310221 19.086914 16.586914 C 18.810221 16.863607 18.486328 17.084961 18.115234 17.250977 C 17.744141 17.416992 17.353516 17.5 16.943359 17.5 Z M 12.5 16.25 L 12.5 3.75 L 3.125 3.75 C 2.871094 3.75 2.630208 3.798828 2.402344 3.896484 C 2.174479 3.994141 1.974284 4.129232 1.801758 4.301758 C 1.629232 4.474285 1.494141 4.67448 1.396484 4.902344 C 1.298828 5.130209 1.25 5.371094 1.25 5.625 L 1.25 14.375 C 1.25 14.628906 1.298828 14.869792 1.396484 15.097656 C 1.494141 15.325521 1.629232 15.525717 1.801758 15.698242 C 1.974284 15.870769 2.174479 16.005859 2.402344 16.103516 C 2.630208 16.201172 2.871094 16.25 3.125 16.25 Z M 16.875 16.25 C 17.128906 16.25 17.369791 16.201172 17.597656 16.103516 C 17.82552 16.005859 18.025715 15.870769 18.198242 15.698242 C 18.370768 15.525717 18.505859 15.325521 18.603516 15.097656 C 18.701172 14.869792 18.75 14.628906 18.75 14.375 L 18.75 5.625 C 18.75 5.371094 18.701172 5.130209 18.603516 4.902344 C 18.505859 4.67448 18.370768 4.474285 18.198242 4.301758 C 18.025715 4.129232 17.82552 3.994141 17.597656 3.896484 C 17.369791 3.798828 17.128906 3.75 16.875 3.75 L 13.75 3.75 L 13.75 16.25 Z M 7.5 11.875 C 7.5 11.705729 7.561849 11.559245 7.685547 11.435547 L 9.121094 10 L 3.125 10 C 2.955729 10 2.809245 9.938151 2.685547 9.814453 C 2.561849 9.690756 2.5 9.544271 2.5 9.375 C 2.5 9.205729 2.561849 9.059245 2.685547 8.935547 C 2.809245 8.81185 2.955729 8.75 3.125 8.75 L 9.121094 8.75 L 7.685547 7.314453 C 7.561849 7.190756 7.5 7.044271 7.5 6.875 C 7.5 6.705729 7.561849 6.559245 7.685547 6.435547 C 7.809245 6.31185 7.955729 6.25 8.125 6.25 C 8.294271 6.25 8.440755 6.31185 8.564453 6.435547 L 11.064453 8.935547 C 11.18815 9.059245 11.25 9.205729 11.25 9.375 C 11.25 9.544271 11.18815 9.690756 11.064453 9.814453 L 8.564453 12.314453 C 8.440755 12.438151 8.294271 12.5 8.125 12.5 C 7.955729 12.5 7.809245 12.438151 7.685547 12.314453 C 7.561849 12.190756 7.5 12.044271 7.5 11.875 Z " />
|
||||
</controls:SettingsCard.HeaderIcon>
|
||||
|
||||
<controls:SettingsCard.Content>
|
||||
<Button Command="{x:Bind ViewModel.ResetMailListPaneLengthCommand}" Content="{x:Bind domain:Translator.Buttons_Reset}" />
|
||||
</controls:SettingsCard.Content>
|
||||
</controls:SettingsCard>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</abstract:PersonalizationPageAbstract>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
MaxWidth="900"
|
||||
Padding="20"
|
||||
HorizontalAlignment="Stretch"
|
||||
RowSpacing="20">
|
||||
RowSpacing="4">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
|
||||
Reference in New Issue
Block a user