diff --git a/Wino.Core.Domain/CalendarRecurrenceSummaryFormatter.cs b/Wino.Core.Domain/CalendarRecurrenceSummaryFormatter.cs
index 7cb50fcb..3092b0f8 100644
--- a/Wino.Core.Domain/CalendarRecurrenceSummaryFormatter.cs
+++ b/Wino.Core.Domain/CalendarRecurrenceSummaryFormatter.cs
@@ -77,7 +77,7 @@ public static class CalendarRecurrenceSummaryFormatter
return string.Format(
culture,
- Translator.GetTranslatedString("CalendarEventCompose_RecurringSummarySmart"),
+ Translator.CalendarEventCompose_RecurringSummarySmart,
cadenceSummary,
weekdaySummary,
timeSummary,
diff --git a/Wino.Core.Domain/Interfaces/IStatePersistenceService.cs b/Wino.Core.Domain/Interfaces/IStatePersistenceService.cs
index 55653d6e..2c544e59 100644
--- a/Wino.Core.Domain/Interfaces/IStatePersistenceService.cs
+++ b/Wino.Core.Domain/Interfaces/IStatePersistenceService.cs
@@ -18,6 +18,11 @@ public interface IStatePersistanceService : INotifyPropertyChanged
///
string CoreWindowTitle { get; set; }
+ ///
+ /// App mode title shown in the title bar.
+ ///
+ string AppModeTitle { get; set; }
+
///
/// When only reader page is visible in small sized window.
///
diff --git a/Wino.Core.Domain/Misc/CalendarColorPalette.cs b/Wino.Core.Domain/Misc/CalendarColorPalette.cs
new file mode 100644
index 00000000..593cdbc6
--- /dev/null
+++ b/Wino.Core.Domain/Misc/CalendarColorPalette.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+
+namespace Wino.Core.Domain.Misc;
+
+public static class CalendarColorPalette
+{
+ private static readonly string[] FlatUiColorPalette =
+ [
+ "#E53935", "#D81B60", "#8E24AA", "#5E35B1", "#3949AB", "#1E88E5", "#039BE5", "#00ACC1", "#00897B", "#43A047",
+ "#7CB342", "#C0CA33", "#FDD835", "#FFB300", "#FB8C00", "#F4511E", "#6D4C41", "#757575", "#546E7A", "#C62828",
+ "#AD1457", "#6A1B9A", "#4527A0", "#283593", "#1565C0", "#0277BD", "#00838F", "#00695C", "#2E7D32", "#558B2F",
+ "#9E9D24", "#F9A825", "#FF8F00", "#EF6C00", "#D84315", "#4E342E", "#616161", "#455A64", "#EF5350", "#EC407A",
+ "#AB47BC", "#7E57C2", "#5C6BC0", "#42A5F5", "#29B6F6", "#26C6DA", "#26A69A", "#66BB6A", "#9CCC65", "#D4E157",
+ "#FFEE58", "#FFCA28", "#FFA726", "#FF7043", "#8D6E63", "#BDBDBD", "#78909C", "#F06292", "#BA68C8", "#9575CD"
+ ];
+
+ public static IReadOnlyList GetColors() => FlatUiColorPalette;
+
+ public static string GetDistinctColor(IEnumerable usedColors)
+ {
+ var used = new HashSet(StringComparer.OrdinalIgnoreCase);
+
+ if (usedColors != null)
+ {
+ foreach (var color in usedColors)
+ {
+ if (TryNormalizeHexColor(color, out var normalized))
+ {
+ used.Add(normalized);
+ }
+ }
+ }
+
+ foreach (var color in FlatUiColorPalette)
+ {
+ if (!used.Contains(color))
+ {
+ return color;
+ }
+ }
+
+ return FlatUiColorPalette[0];
+ }
+
+ private static bool TryNormalizeHexColor(string value, out string normalized)
+ {
+ normalized = null;
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ return false;
+ }
+
+ var color = value.Trim();
+ if (color.StartsWith('#'))
+ {
+ color = color[1..];
+ }
+
+ if (color.Length != 6)
+ {
+ return false;
+ }
+
+ if (!int.TryParse(color, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out _))
+ {
+ return false;
+ }
+
+ normalized = $"#{color.ToUpperInvariant()}";
+ return true;
+ }
+}
diff --git a/Wino.Core.Domain/Translations/en_US/resources.json b/Wino.Core.Domain/Translations/en_US/resources.json
index ea3c6d13..8d87609c 100644
--- a/Wino.Core.Domain/Translations/en_US/resources.json
+++ b/Wino.Core.Domain/Translations/en_US/resources.json
@@ -150,6 +150,7 @@
"CalendarEventCompose_PickCalendarTitle": "Pick a calendar",
"CalendarEventCompose_Recurring": "Recurring",
"CalendarEventCompose_RecurringSummary": "Occurs every {0} {1}{2} {3} effective {4}{5}",
+ "CalendarEventCompose_RecurringSummarySmart": "Occurs {0}{1} {2} effective {3}{4}",
"CalendarEventCompose_RepeatEvery": "Repeat every",
"CalendarEventCompose_SelectCalendar": "Select calendar",
"CalendarEventCompose_SingleOccurrenceSummary": "Occurs on {0} {1}",
diff --git a/Wino.Core.Tests/Services/AccountServiceTests.cs b/Wino.Core.Tests/Services/AccountServiceTests.cs
index 28f9dcb4..69c69dfd 100644
--- a/Wino.Core.Tests/Services/AccountServiceTests.cs
+++ b/Wino.Core.Tests/Services/AccountServiceTests.cs
@@ -1,3 +1,5 @@
+using System;
+using System.Linq;
using FluentAssertions;
using Moq;
using Wino.Core.Domain;
@@ -5,6 +7,7 @@ using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
+using Wino.Core.Misc;
using Wino.Core.Tests.Helpers;
using Wino.Services;
using Xunit;
@@ -49,6 +52,7 @@ public class AccountServiceTests : IAsyncLifetime
calendars.Should().HaveCount(1);
calendars[0].IsPrimary.Should().BeTrue();
calendars[0].Name.Should().Be(Translator.AccountDetailsPage_TabCalendar);
+ ColorHelpers.GetFlatColorPalette().Should().Contain(calendars[0].BackgroundColorHex);
}
[Fact]
@@ -72,6 +76,49 @@ public class AccountServiceTests : IAsyncLifetime
calendars.Should().BeEmpty();
}
+ [Fact]
+ public async Task CreateAccountAsync_ImapLocalOnly_AssignsDistinctCalendarColorsAcrossAccounts()
+ {
+ var firstAccountId = Guid.NewGuid();
+ var secondAccountId = Guid.NewGuid();
+
+ await _accountService.CreateAccountAsync(
+ CreateImapAccount(firstAccountId),
+ new CustomServerInformation
+ {
+ Id = Guid.NewGuid(),
+ AccountId = firstAccountId,
+ CalendarSupportMode = ImapCalendarSupportMode.LocalOnly
+ });
+
+ await _accountService.CreateAccountAsync(
+ CreateImapAccount(secondAccountId),
+ new CustomServerInformation
+ {
+ Id = Guid.NewGuid(),
+ AccountId = secondAccountId,
+ CalendarSupportMode = ImapCalendarSupportMode.LocalOnly
+ });
+
+ var calendars = await _databaseService.Connection.Table()
+ .OrderBy(a => a.AccountId)
+ .ToListAsync();
+
+ calendars.Should().HaveCount(2);
+ calendars.Select(a => a.BackgroundColorHex).Should().OnlyHaveUniqueItems();
+ calendars.Should().OnlyContain(a => ColorHelpers.GetFlatColorPalette().Contains(a.BackgroundColorHex));
+ }
+
+ [Fact]
+ public void FlatCalendarPalette_ProvidesAtLeastFiftyDistinctColors()
+ {
+ ColorHelpers.GetFlatColorPalette()
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .Count()
+ .Should()
+ .BeGreaterThanOrEqualTo(50);
+ }
+
private static MailAccount CreateImapAccount(Guid accountId)
{
return new MailAccount
diff --git a/Wino.Core.ViewModels/SettingOptionsPageViewModel.cs b/Wino.Core.ViewModels/SettingOptionsPageViewModel.cs
index 9f55c8b2..dc6ed40c 100644
--- a/Wino.Core.ViewModels/SettingOptionsPageViewModel.cs
+++ b/Wino.Core.ViewModels/SettingOptionsPageViewModel.cs
@@ -15,8 +15,9 @@ public partial class SettingOptionsPageViewModel : CoreBaseViewModel
{
private readonly INativeAppService _nativeAppService;
private readonly IAccountService _accountService;
+ private readonly IStoreRatingService _storeRatingService;
- public string WebsiteUrl => AppUrls.Website;
+ public string GitHubUrl => AppUrls.GitHub;
public string PaypalUrl => AppUrls.Paypal;
[ObservableProperty]
@@ -28,10 +29,13 @@ public partial class SettingOptionsPageViewModel : CoreBaseViewModel
[ObservableProperty]
public partial int AccountCount { get; set; }
- public SettingOptionsPageViewModel(INativeAppService nativeAppService, IAccountService accountService)
+ public SettingOptionsPageViewModel(INativeAppService nativeAppService,
+ IAccountService accountService,
+ IStoreRatingService storeRatingService)
{
_nativeAppService = nativeAppService;
_accountService = accountService;
+ _storeRatingService = storeRatingService;
}
public override void OnNavigatedTo(NavigationMode mode, object parameters)
@@ -86,4 +90,19 @@ public partial class SettingOptionsPageViewModel : CoreBaseViewModel
Messenger.Send(new BreadcrumbNavigationRequested(pageTitle, pageType));
}
}
+
+ [RelayCommand]
+ private async Task NavigateExternalAsync(object target)
+ {
+ if (target is not string stringTarget || string.IsNullOrWhiteSpace(stringTarget))
+ return;
+
+ if (stringTarget == "Store")
+ {
+ await _storeRatingService.LaunchStorePageForReviewAsync();
+ return;
+ }
+
+ await _nativeAppService.LaunchUriAsync(new Uri(stringTarget));
+ }
}
diff --git a/Wino.Core/Misc/ColorHelpers.cs b/Wino.Core/Misc/ColorHelpers.cs
index 752a0bd6..d917cab3 100644
--- a/Wino.Core/Misc/ColorHelpers.cs
+++ b/Wino.Core/Misc/ColorHelpers.cs
@@ -1,105 +1,34 @@
using System;
using System.Collections.Generic;
using System.Drawing;
-using System.Globalization;
+using System.Linq;
+using Wino.Core.Domain.Misc;
namespace Wino.Core.Misc;
public static class ColorHelpers
{
- private static readonly string[] FlatUiColorPalette =
- [
- "#B91C1C", "#C2410C", "#B45309", "#A16207", "#4D7C0F", "#15803D", "#047857", "#0F766E", "#0E7490", "#0369A1",
- "#1D4ED8", "#4338CA", "#6D28D9", "#7E22CE", "#A21CAF", "#BE185D", "#E11D48", "#DC2626", "#EA580C", "#D97706",
- "#CA8A04", "#65A30D", "#16A34A", "#059669", "#0D9488", "#0891B2", "#0284C7", "#2563EB", "#4F46E5", "#7C3AED",
- "#9333EA", "#C026D3", "#DB2777", "#F43F5E", "#EF4444", "#F97316", "#F59E0B", "#EAB308", "#84CC16", "#22C55E",
- "#10B981", "#14B8A6", "#06B6D4", "#0EA5E9", "#3B82F6", "#6366F1", "#8B5CF6", "#A855F7", "#D946EF", "#EC4899",
- "#FB7185", "#F87171", "#FB923C", "#FBBF24", "#FACC15", "#A3E635", "#4ADE80", "#34D399", "#2DD4BF", "#22D3EE",
- "#38BDF8", "#60A5FA", "#818CF8", "#A78BFA", "#C084FC", "#E879F9", "#F472B6", "#FDA4AF", "#FCA5A5", "#FDBA74",
- "#FCD34D", "#FDE047", "#BEF264", "#86EFAC", "#6EE7B7", "#5EEAD4", "#67E8F9", "#7DD3FC", "#93C5FD", "#A5B4FC",
- "#C4B5FD", "#D8B4FE", "#F0ABFC", "#F9A8D4", "#A16207", "#9A3412", "#7C2D12", "#6F1D1B", "#7F1D1D", "#881337",
- "#831843", "#701A75", "#581C87", "#312E81", "#1E3A8A", "#1D4ED8", "#155E75", "#134E4A", "#14532D", "#3F6212",
- "#365314", "#3F3F46", "#52525B", "#57534E", "#44403C", "#78716C", "#6B7280", "#4B5563", "#374151", "#1F2937",
- "#A16207", "#B45309", "#C2410C", "#9F1239", "#BE123C", "#C026D3", "#7E22CE", "#6D28D9", "#4338CA", "#1D4ED8"
- ];
-
- public static IReadOnlyList GetFlatColorPalette() => FlatUiColorPalette;
+ public static IReadOnlyList GetFlatColorPalette() => CalendarColorPalette.GetColors();
public static string GenerateFlatColorHex() => GetDistinctFlatColorHex(Array.Empty());
public static string GetDistinctFlatColorHex(IEnumerable usedColors)
{
- var used = new HashSet(StringComparer.OrdinalIgnoreCase);
-
- if (usedColors != null)
+ var palette = CalendarColorPalette.GetColors();
+ var distinctColor = CalendarColorPalette.GetDistinctColor(usedColors);
+ if (palette.Contains(distinctColor))
{
- foreach (var color in usedColors)
- {
- if (TryNormalizeHexColor(color, out var normalized))
- {
- used.Add(normalized);
- }
- }
+ return distinctColor;
}
- foreach (var color in FlatUiColorPalette)
- {
- if (!used.Contains(color))
- {
- return color;
- }
- }
+ var candidate = AdjustColor(palette[0], 1);
- var attempt = 0;
- while (attempt < 500)
- {
- var baseColor = FlatUiColorPalette[attempt % FlatUiColorPalette.Length];
- var cycle = (attempt / FlatUiColorPalette.Length) + 1;
- var candidate = AdjustColor(baseColor, cycle);
-
- if (!used.Contains(candidate))
- {
- return candidate;
- }
-
- attempt++;
- }
-
- return "#5C7A8A";
+ return candidate;
}
public static string ToHexString(this Color c) => $"#{c.R:X2}{c.G:X2}{c.B:X2}";
public static string ToRgbString(this Color c) => $"RGB({c.R}, {c.G}, {c.B})";
-
- private static bool TryNormalizeHexColor(string value, out string normalized)
- {
- normalized = null;
- if (string.IsNullOrWhiteSpace(value))
- {
- return false;
- }
-
- var color = value.Trim();
- if (color.StartsWith('#'))
- {
- color = color[1..];
- }
-
- if (color.Length != 6)
- {
- return false;
- }
-
- if (!int.TryParse(color, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out _))
- {
- return false;
- }
-
- normalized = $"#{color.ToUpperInvariant()}";
- return true;
- }
-
private static string AdjustColor(string hexColor, int cycle)
{
var color = ColorTranslator.FromHtml(hexColor);
diff --git a/Wino.Mail.WinUI/Controls/ImagePreviewControl.cs b/Wino.Mail.WinUI/Controls/ImagePreviewControl.cs
index badbc77f..7bd51b87 100644
--- a/Wino.Mail.WinUI/Controls/ImagePreviewControl.cs
+++ b/Wino.Mail.WinUI/Controls/ImagePreviewControl.cs
@@ -331,6 +331,7 @@ public sealed partial class ImagePreviewControl : PersonPicture
if (!IsActiveRefresh(refreshVersion, cancellationToken))
return;
+ DisplayName = string.Empty;
Initials = string.Empty;
ProfilePicture = bitmapImage;
}).ConfigureAwait(false);
diff --git a/Wino.Mail.WinUI/Services/NavigationService.cs b/Wino.Mail.WinUI/Services/NavigationService.cs
index 1c183c65..819976e7 100644
--- a/Wino.Mail.WinUI/Services/NavigationService.cs
+++ b/Wino.Mail.WinUI/Services/NavigationService.cs
@@ -188,7 +188,7 @@ public class NavigationService : NavigationServiceBase, INavigationService
// Update the application mode in state persistence service
_statePersistanceService.ApplicationMode = mode;
- _statePersistanceService.CoreWindowTitle = mode == WinoApplicationMode.Calendar
+ _statePersistanceService.AppModeTitle = mode == WinoApplicationMode.Calendar
? "Wino Calendar"
: "Wino Mail";
diff --git a/Wino.Mail.WinUI/Services/StatePersistenceService.cs b/Wino.Mail.WinUI/Services/StatePersistenceService.cs
index de50a3ad..23ce4466 100644
--- a/Wino.Mail.WinUI/Services/StatePersistenceService.cs
+++ b/Wino.Mail.WinUI/Services/StatePersistenceService.cs
@@ -13,6 +13,7 @@ public class StatePersistenceService : ObservableObject, IStatePersistanceServic
private const string OpenPaneLengthKey = nameof(OpenPaneLengthKey);
private const string MailListPaneLengthKey = nameof(MailListPaneLengthKey);
+ private const string AppModeTitleKey = nameof(AppModeTitle);
private readonly IConfigurationService _configurationService;
@@ -22,6 +23,7 @@ public class StatePersistenceService : ObservableObject, IStatePersistanceServic
_openPaneLength = _configurationService.Get(OpenPaneLengthKey, 320d);
_mailListPaneLength = _configurationService.Get(MailListPaneLengthKey, 420d);
+ _appModeTitle = _configurationService.Get(AppModeTitleKey, "Wino Mail");
_calendarDisplayType = EnsureValidCalendarDisplayType(_configurationService.Get(nameof(CalendarDisplayType), CalendarDisplayType.Week));
_dayDisplayCount = _configurationService.Get(nameof(DayDisplayCount), 1);
@@ -144,6 +146,21 @@ public class StatePersistenceService : ObservableObject, IStatePersistanceServic
}
}
+ private string _appModeTitle;
+
+ public string AppModeTitle
+ {
+ get => _appModeTitle;
+ set
+ {
+ if (SetProperty(ref _appModeTitle, value))
+ {
+ _configurationService.Set(AppModeTitleKey, value);
+ UpdateAppWindowTitle();
+ }
+ }
+ }
+
private double _openPaneLength;
public double OpenPaneLength
{
@@ -199,10 +216,15 @@ public class StatePersistenceService : ObservableObject, IStatePersistanceServic
}
private void UpdateAppCoreWindowTitle()
+ {
+ OnPropertyChanged(nameof(CoreWindowTitle));
+ }
+
+ private void UpdateAppWindowTitle()
{
if (WinoApplication.MainWindow != null)
{
- WinoApplication.MainWindow.Title = CoreWindowTitle;
+ WinoApplication.MainWindow.Title = AppModeTitle;
}
}
diff --git a/Wino.Mail.WinUI/ShellWindow.xaml b/Wino.Mail.WinUI/ShellWindow.xaml
index 3dc761f2..1410cf3f 100644
--- a/Wino.Mail.WinUI/ShellWindow.xaml
+++ b/Wino.Mail.WinUI/ShellWindow.xaml
@@ -26,7 +26,7 @@
+ PaneToggleRequested="PaneButtonClicked"
+ Subtitle="{x:Bind StatePersistanceService.CoreWindowTitle, Mode=OneWay}">
diff --git a/Wino.Mail.WinUI/ShellWindow.xaml.cs b/Wino.Mail.WinUI/ShellWindow.xaml.cs
index 5fa3ecab..b458ce28 100644
--- a/Wino.Mail.WinUI/ShellWindow.xaml.cs
+++ b/Wino.Mail.WinUI/ShellWindow.xaml.cs
@@ -68,7 +68,7 @@ public sealed partial class ShellWindow : WindowEx, IWinoShellWindow,
ExitWinoCommand = new RelayCommand(ForceClose);
this.SetIcon("Assets/Wino_Icon.ico");
- Title = "Wino Mail";
+ Title = StatePersistanceService.AppModeTitle;
SystemTrayIcon.ForceCreate();
}
@@ -122,6 +122,7 @@ public sealed partial class ShellWindow : WindowEx, IWinoShellWindow,
AppModeSegmentedControl.SelectedIndex = targetMode == WinoApplicationMode.Mail ? 0 : 1;
_isApplyingActivationMode = false;
+ StatePersistanceService.AppModeTitle = GetAppModeTitle(targetMode);
NavigationService.ChangeApplicationMode(targetMode);
}
@@ -317,6 +318,7 @@ public sealed partial class ShellWindow : WindowEx, IWinoShellWindow,
AppModeSegmentedControl.SelectedIndex = mode == WinoApplicationMode.Mail ? 0 : 1;
_isApplyingActivationMode = false;
+ StatePersistanceService.AppModeTitle = GetAppModeTitle(mode);
NavigationService.ChangeApplicationMode(mode);
RestoreFromTray();
}
@@ -371,4 +373,9 @@ public sealed partial class ShellWindow : WindowEx, IWinoShellWindow,
_currentMode = selectedMode;
NavigationService.ChangeApplicationMode(selectedMode);
}
+
+ private static string GetAppModeTitle(WinoApplicationMode mode)
+ => mode == WinoApplicationMode.Calendar
+ ? "Wino Calendar"
+ : "Wino Mail";
}
diff --git a/Wino.Mail.WinUI/Views/Account/AccountDetailsPage.xaml b/Wino.Mail.WinUI/Views/Account/AccountDetailsPage.xaml
index 3b5ecaaa..8c7ba9c8 100644
--- a/Wino.Mail.WinUI/Views/Account/AccountDetailsPage.xaml
+++ b/Wino.Mail.WinUI/Views/Account/AccountDetailsPage.xaml
@@ -2,14 +2,14 @@
x:Class="Wino.Views.AccountDetailsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:accounts="using:Wino.Core.Domain.Models.Accounts"
xmlns:abstract="using:Wino.Views.Abstract"
+ xmlns:accounts="using:Wino.Core.Domain.Models.Accounts"
xmlns:calendar="using:Wino.Core.Domain.Entities.Calendar"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:controls1="using:Wino.Controls"
xmlns:coreControls="using:Wino.Mail.WinUI.Controls"
- xmlns:data="using:Wino.Core.ViewModels.Data"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:data="using:Wino.Core.ViewModels.Data"
xmlns:domain="using:Wino.Core.Domain"
xmlns:folders="using:Wino.Core.Domain.Models.Folders"
xmlns:helpers="using:Wino.Helpers"
@@ -81,13 +81,9 @@
-
+
-
+
@@ -132,6 +128,7 @@
@@ -310,6 +307,7 @@
diff --git a/Wino.Mail.WinUI/Views/Calendar/CalendarEventComposePage.xaml b/Wino.Mail.WinUI/Views/Calendar/CalendarEventComposePage.xaml
index 4a9b29f4..529bbb60 100644
--- a/Wino.Mail.WinUI/Views/Calendar/CalendarEventComposePage.xaml
+++ b/Wino.Mail.WinUI/Views/Calendar/CalendarEventComposePage.xaml
@@ -63,7 +63,9 @@
HorizontalContentAlignment="Left"
Style="{StaticResource DefaultButtonStyle}">
-
+
diff --git a/Wino.Mail.WinUI/Views/Calendar/CalendarEventComposePage.xaml.cs b/Wino.Mail.WinUI/Views/Calendar/CalendarEventComposePage.xaml.cs
index 8181869c..86c7ed28 100644
--- a/Wino.Mail.WinUI/Views/Calendar/CalendarEventComposePage.xaml.cs
+++ b/Wino.Mail.WinUI/Views/Calendar/CalendarEventComposePage.xaml.cs
@@ -155,6 +155,7 @@ public sealed partial class CalendarEventComposePage : CalendarEventComposePageA
if (e.ClickedItem is AccountCalendarViewModel calendar)
{
ViewModel.SelectedCalendar = calendar;
+ CalendarSelectionFlyout.Hide();
}
}
diff --git a/Wino.Mail.WinUI/Views/SettingOptionsPage.xaml b/Wino.Mail.WinUI/Views/SettingOptionsPage.xaml
index 17bb7d32..89010876 100644
--- a/Wino.Mail.WinUI/Views/SettingOptionsPage.xaml
+++ b/Wino.Mail.WinUI/Views/SettingOptionsPage.xaml
@@ -33,43 +33,92 @@
-
-
-
-
-
+ ColumnSpacing="12">
+
+
+
-
-
+ Spacing="12">
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
diff --git a/Wino.Services/AccountService.cs b/Wino.Services/AccountService.cs
index 55d6f6d9..658cc41a 100644
--- a/Wino.Services/AccountService.cs
+++ b/Wino.Services/AccountService.cs
@@ -12,6 +12,7 @@ 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.Misc;
using Wino.Messaging.Client.Calendar;
using Wino.Messaging.Client.Accounts;
using Wino.Messaging.UI;
@@ -20,22 +21,6 @@ namespace Wino.Services;
public class AccountService : BaseDatabaseService, IAccountService
{
- private static readonly string[] DefaultCalendarFlatColors =
- [
- "#B91C1C",
- "#15803D",
- "#0E7490",
- "#1D4ED8",
- "#7C3AED",
- "#C026D3",
- "#EC4899",
- "#F97316",
- "#EAB308",
- "#22C55E",
- "#06B6D4",
- "#60A5FA"
- ];
-
public IAuthenticator ExternalAuthenticationAuthenticator { get; set; }
private readonly ISignatureService _signatureService;
@@ -657,20 +642,20 @@ public class AccountService : BaseDatabaseService, IAccountService
IsExtended = true,
RemoteCalendarId = string.Empty,
TimeZone = string.Empty,
- BackgroundColorHex = GetDefaultCalendarFlatColor(accountId),
+ BackgroundColorHex = await GetNextDistinctCalendarColorAsync().ConfigureAwait(false),
TextColorHex = "#FFFFFF"
};
await Connection.InsertAsync(localCalendar, typeof(AccountCalendar)).ConfigureAwait(false);
}
- private static string GetDefaultCalendarFlatColor(Guid accountId)
+ private async Task GetNextDistinctCalendarColorAsync()
{
- var bytes = accountId.ToByteArray();
- var hash = BitConverter.ToUInt32(bytes, 0);
- var index = (int)(hash % (uint)DefaultCalendarFlatColors.Length);
+ var usedColors = await Connection.Table()
+ .ToListAsync()
+ .ConfigureAwait(false);
- return DefaultCalendarFlatColors[index];
+ return CalendarColorPalette.GetDistinctColor(usedColors.Select(a => a.BackgroundColorHex));
}
public async Task UpdateAccountOrdersAsync(Dictionary accountIdOrderPair)