diff --git a/Wino.Calendar.ViewModels/CalendarAppShellViewModel.cs b/Wino.Calendar.ViewModels/CalendarAppShellViewModel.cs index 43aec751..d43ca51d 100644 --- a/Wino.Calendar.ViewModels/CalendarAppShellViewModel.cs +++ b/Wino.Calendar.ViewModels/CalendarAppShellViewModel.cs @@ -402,7 +402,7 @@ public partial class CalendarAppShellViewModel : CalendarBaseViewModel, var pickingResult = await _dialogService.ShowSingleCalendarPickerDialogAsync(availableGroups); if (pickingResult.ShouldNavigateToCalendarSettings) { - NavigationService.Navigate(WinoPage.CalendarSettingsPage); + NavigationService.Navigate(WinoPage.CalendarPreferenceSettingsPage); return; } diff --git a/Wino.Calendar.ViewModels/CalendarNotificationSettingsPageViewModel.cs b/Wino.Calendar.ViewModels/CalendarNotificationSettingsPageViewModel.cs new file mode 100644 index 00000000..df4af29f --- /dev/null +++ b/Wino.Calendar.ViewModels/CalendarNotificationSettingsPageViewModel.cs @@ -0,0 +1,44 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Calendar.ViewModels; + +public partial class CalendarNotificationSettingsPageViewModel : CalendarSettingsSectionViewModelBase +{ + [ObservableProperty] + public partial int SelectedDefaultReminderIndex { get; set; } + + [ObservableProperty] + public partial int SelectedDefaultSnoozeIndex { get; set; } + + public CalendarNotificationSettingsPageViewModel( + IPreferencesService preferencesService, + ICalendarService calendarService, + IAccountService accountService) + : base(preferencesService, calendarService, accountService) + { + LoadReminderOptions(); + LoadSnoozeOptions(); + + SelectedDefaultReminderIndex = GetSelectedReminderIndex(); + SelectedDefaultSnoozeIndex = GetSelectedSnoozeIndex(); + + IsLoaded = true; + } + + partial void OnSelectedDefaultReminderIndexChanged(int value) + { + if (!IsLoaded) + return; + + SaveReminderIndex(value); + } + + partial void OnSelectedDefaultSnoozeIndexChanged(int value) + { + if (!IsLoaded) + return; + + SaveSnoozeIndex(value); + } +} diff --git a/Wino.Calendar.ViewModels/CalendarPreferenceSettingsPageViewModel.cs b/Wino.Calendar.ViewModels/CalendarPreferenceSettingsPageViewModel.cs new file mode 100644 index 00000000..c66b1d1d --- /dev/null +++ b/Wino.Calendar.ViewModels/CalendarPreferenceSettingsPageViewModel.cs @@ -0,0 +1,62 @@ +using System.Linq; +using CommunityToolkit.Mvvm.ComponentModel; +using Wino.Calendar.ViewModels.Data; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Calendar.ViewModels; + +public partial class CalendarPreferenceSettingsPageViewModel : CalendarSettingsSectionViewModelBase +{ + [ObservableProperty] + public partial CalendarNewEventBehaviorOption SelectedNewEventBehaviorOption { get; set; } + + [ObservableProperty] + public partial AccountCalendarViewModel SelectedNewEventCalendar { get; set; } + + public bool ShouldShowSpecificNewEventCalendar + => SelectedNewEventBehaviorOption?.Behavior == NewEventButtonBehavior.AlwaysUseSpecificCalendar; + + public CalendarPreferenceSettingsPageViewModel( + IPreferencesService preferencesService, + ICalendarService calendarService, + IAccountService accountService) + : base(preferencesService, calendarService, accountService) + { + LoadNewEventBehaviorOptions(); + SelectedNewEventBehaviorOption = GetSelectedNewEventBehaviorOption(); + + IsLoaded = true; + LoadCalendarsAsync(ApplyStoredNewEventCalendarPreference); + } + + partial void OnSelectedNewEventBehaviorOptionChanged(CalendarNewEventBehaviorOption value) + { + if (!IsLoaded) + return; + + OnPropertyChanged(nameof(ShouldShowSpecificNewEventCalendar)); + SaveNewEventBehavior(SelectedNewEventBehaviorOption, SelectedNewEventCalendar); + } + + partial void OnSelectedNewEventCalendarChanged(AccountCalendarViewModel value) + { + if (!IsLoaded) + return; + + SaveNewEventBehavior(SelectedNewEventBehaviorOption, value); + } + + private void ApplyStoredNewEventCalendarPreference() + { + var configuredCalendar = ResolveSelectedNewEventCalendar(); + if (PreferencesService.NewEventButtonBehavior == NewEventButtonBehavior.AlwaysUseSpecificCalendar && configuredCalendar == null) + { + SelectedNewEventBehaviorOption = NewEventBehaviorOptions.First(option => option.Behavior == NewEventButtonBehavior.AskEachTime); + SelectedNewEventCalendar = null; + return; + } + + SelectedNewEventCalendar = configuredCalendar ?? ResolveFallbackNewEventCalendar(); + } +} diff --git a/Wino.Calendar.ViewModels/CalendarRenderingSettingsPageViewModel.cs b/Wino.Calendar.ViewModels/CalendarRenderingSettingsPageViewModel.cs new file mode 100644 index 00000000..5a7fe685 --- /dev/null +++ b/Wino.Calendar.ViewModels/CalendarRenderingSettingsPageViewModel.cs @@ -0,0 +1,191 @@ +using System; +using System.Linq; +using CommunityToolkit.Mvvm.ComponentModel; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Calendar.ViewModels; + +public partial class CalendarRenderingSettingsPageViewModel : CalendarSettingsSectionViewModelBase +{ + [ObservableProperty] + public partial double CellHourHeight { get; set; } + + [ObservableProperty] + public partial int SelectedFirstDayOfWeekIndex { get; set; } + + [ObservableProperty] + public partial bool Is24HourHeaders { get; set; } + + [ObservableProperty] + public partial bool IsWorkingHoursEnabled { get; set; } + + [ObservableProperty] + public partial TimeSpan WorkingHourStart { get; set; } + + [ObservableProperty] + public partial TimeSpan WorkingHourEnd { get; set; } + + [ObservableProperty] + public partial int WorkingDayStartIndex { get; set; } + + [ObservableProperty] + public partial int WorkingDayEndIndex { get; set; } + + [ObservableProperty] + public partial string TimedDayHeaderDateFormat { get; set; } = "ddd dd"; + + [ObservableProperty] + public partial int SelectedTimedDayHeaderFormatPresetIndex { get; set; } = -1; + + public CalendarRenderingSettingsPageViewModel( + IPreferencesService preferencesService, + ICalendarService calendarService, + IAccountService accountService) + : base(preferencesService, calendarService, accountService) + { + SelectedFirstDayOfWeekIndex = DayNames.IndexOf(CalendarCulture.DateTimeFormat.GetDayName(preferencesService.FirstDayOfWeek)); + Is24HourHeaders = preferencesService.Prefer24HourTimeFormat; + IsWorkingHoursEnabled = preferencesService.IsWorkingHoursEnabled; + WorkingHourStart = preferencesService.WorkingHourStart; + WorkingHourEnd = preferencesService.WorkingHourEnd; + CellHourHeight = preferencesService.HourHeight; + WorkingDayStartIndex = DayNames.IndexOf(CalendarCulture.DateTimeFormat.GetDayName(preferencesService.WorkingDayStart)); + WorkingDayEndIndex = DayNames.IndexOf(CalendarCulture.DateTimeFormat.GetDayName(preferencesService.WorkingDayEnd)); + TimedDayHeaderDateFormat = preferencesService.CalendarTimedDayHeaderDateFormat; + SelectedTimedDayHeaderFormatPresetIndex = TimedDayHeaderFormatPresets.IndexOf(TimedDayHeaderDateFormat); + + IsLoaded = true; + } + + partial void OnCellHourHeightChanged(double oldValue, double newValue) => SaveSettings(); + + partial void OnIs24HourHeadersChanged(bool value) + { + OnPropertyChanged(nameof(TimedHourLabelPreview)); + SaveSettings(); + } + + partial void OnSelectedFirstDayOfWeekIndexChanged(int value) => SaveSettings(); + partial void OnIsWorkingHoursEnabledChanged(bool value) => SaveSettings(); + partial void OnWorkingHourStartChanged(TimeSpan value) => SaveSettings(); + partial void OnWorkingHourEndChanged(TimeSpan value) => SaveSettings(); + partial void OnWorkingDayStartIndexChanged(int value) => SaveSettings(); + partial void OnWorkingDayEndIndexChanged(int value) => SaveSettings(); + + partial void OnTimedDayHeaderDateFormatChanged(string value) + { + OnPropertyChanged(nameof(TimedDayHeaderFormatPreview)); + OnPropertyChanged(nameof(TimedHourLabelPreview)); + + var normalizedFormat = string.IsNullOrWhiteSpace(value) ? "ddd dd" : value.Trim(); + var matchingPresetIndex = TimedDayHeaderFormatPresets + .Select((format, index) => new { format, index }) + .Where(item => string.Equals(item.format, normalizedFormat, StringComparison.Ordinal)) + .Select(item => item.index) + .DefaultIfEmpty(-1) + .First(); + + if (SelectedTimedDayHeaderFormatPresetIndex != matchingPresetIndex) + { + SelectedTimedDayHeaderFormatPresetIndex = matchingPresetIndex; + } + + SaveSettings(); + } + + partial void OnSelectedTimedDayHeaderFormatPresetIndexChanged(int value) + { + if (value < 0 || value >= TimedDayHeaderFormatPresets.Count) + return; + + var selectedPreset = TimedDayHeaderFormatPresets[value]; + if (string.Equals(TimedDayHeaderDateFormat, selectedPreset, StringComparison.Ordinal)) + return; + + TimedDayHeaderDateFormat = selectedPreset; + } + + public string TimedDayHeaderFormatPreview + { + get + { + var format = string.IsNullOrWhiteSpace(TimedDayHeaderDateFormat) ? "ddd dd" : TimedDayHeaderDateFormat.Trim(); + var previewDates = new[] + { + new DateTime(2026, 3, 23), + new DateTime(2026, 3, 24), + new DateTime(2026, 3, 25) + }; + + try + { + return string.Join(" · ", previewDates.Select(date => date.ToString(format, CalendarCulture))); + } + catch (FormatException) + { + return string.Join(" · ", previewDates.Select(date => date.ToString("ddd dd", CalendarCulture))); + } + } + } + + public string TimedHourLabelPreview + => string.Join(" · ", new[] { 0, 9, 14, 24 }.Select(CurrentSettingsPreviewLabel)); + + private string CurrentSettingsPreviewLabel(int hour) + { + if (Is24HourHeaders) + return hour.ToString(CalendarCulture); + + var displayHour = hour % 24; + return DateTime.Today.AddHours(displayHour).ToString("h tt", CalendarCulture); + } + + private void SaveSettings() + { + if (!IsLoaded) + return; + + PreferencesService.FirstDayOfWeek = SelectedFirstDayOfWeekIndex switch + { + 0 => DayOfWeek.Sunday, + 1 => DayOfWeek.Monday, + 2 => DayOfWeek.Tuesday, + 3 => DayOfWeek.Wednesday, + 4 => DayOfWeek.Thursday, + 5 => DayOfWeek.Friday, + 6 => DayOfWeek.Saturday, + _ => throw new ArgumentOutOfRangeException() + }; + + PreferencesService.WorkingDayStart = WorkingDayStartIndex switch + { + 0 => DayOfWeek.Sunday, + 1 => DayOfWeek.Monday, + 2 => DayOfWeek.Tuesday, + 3 => DayOfWeek.Wednesday, + 4 => DayOfWeek.Thursday, + 5 => DayOfWeek.Friday, + 6 => DayOfWeek.Saturday, + _ => throw new ArgumentOutOfRangeException() + }; + + PreferencesService.WorkingDayEnd = WorkingDayEndIndex switch + { + 0 => DayOfWeek.Sunday, + 1 => DayOfWeek.Monday, + 2 => DayOfWeek.Tuesday, + 3 => DayOfWeek.Wednesday, + 4 => DayOfWeek.Thursday, + 5 => DayOfWeek.Friday, + 6 => DayOfWeek.Saturday, + _ => throw new ArgumentOutOfRangeException() + }; + + PreferencesService.Prefer24HourTimeFormat = Is24HourHeaders; + PreferencesService.IsWorkingHoursEnabled = IsWorkingHoursEnabled; + PreferencesService.WorkingHourStart = WorkingHourStart; + PreferencesService.WorkingHourEnd = WorkingHourEnd; + PreferencesService.HourHeight = CellHourHeight; + PreferencesService.CalendarTimedDayHeaderDateFormat = TimedDayHeaderDateFormat; + } +} diff --git a/Wino.Calendar.ViewModels/CalendarSettingsPageViewModel.cs b/Wino.Calendar.ViewModels/CalendarSettingsPageViewModel.cs deleted file mode 100644 index b0e4a41a..00000000 --- a/Wino.Calendar.ViewModels/CalendarSettingsPageViewModel.cs +++ /dev/null @@ -1,402 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Globalization; -using System.Linq; -using System.Threading.Tasks; -using CommunityToolkit.Mvvm.ComponentModel; -using Wino.Calendar.ViewModels.Data; -using Wino.Core.Domain; -using Wino.Core.Domain.Entities.Shared; -using Wino.Core.Domain.Enums; -using Wino.Core.Domain.Interfaces; -using Wino.Core.Domain.Translations; -using Wino.Core.ViewModels; - -namespace Wino.Calendar.ViewModels; - -public partial class CalendarSettingsPageViewModel : CalendarBaseViewModel -{ - [ObservableProperty] - public partial double CellHourHeight { get; set; } - - [ObservableProperty] - public partial int SelectedFirstDayOfWeekIndex { get; set; } - - [ObservableProperty] - public partial bool Is24HourHeaders { get; set; } - - [ObservableProperty] - public partial bool IsWorkingHoursEnabled { get; set; } - - [ObservableProperty] - public partial TimeSpan WorkingHourStart { get; set; } - - [ObservableProperty] - public partial TimeSpan WorkingHourEnd { get; set; } - - [ObservableProperty] - public partial List DayNames { get; set; } = []; - - [ObservableProperty] - public partial int WorkingDayStartIndex { get; set; } - - [ObservableProperty] - public partial int WorkingDayEndIndex { get; set; } - - [ObservableProperty] - public partial string TimedDayHeaderDateFormat { get; set; } = "ddd dd"; - - [ObservableProperty] - public partial int SelectedTimedDayHeaderFormatPresetIndex { get; set; } = -1; - - [ObservableProperty] - public partial List ReminderOptions { get; set; } = []; - - [ObservableProperty] - public partial int SelectedDefaultReminderIndex { get; set; } - - [ObservableProperty] - public partial List SnoozeOptions { get; set; } = []; - - [ObservableProperty] - public partial int SelectedDefaultSnoozeIndex { get; set; } - - public ObservableCollection Accounts { get; } = []; - public ObservableCollection NewEventBehaviorOptions { get; } = []; - public ObservableCollection AvailableNewEventCalendars { get; } = []; - public ObservableCollection TimedDayHeaderFormatPresets { get; } = - [ - "ddd dd", - "dddd dd", - "ddd d MMM", - "dd MMM ddd", - "M/d ddd" - ]; - - [ObservableProperty] - public partial CalendarNewEventBehaviorOption SelectedNewEventBehaviorOption { get; set; } - - [ObservableProperty] - public partial AccountCalendarViewModel SelectedNewEventCalendar { get; set; } - - public bool ShouldShowSpecificNewEventCalendar => SelectedNewEventBehaviorOption?.Behavior == NewEventButtonBehavior.AlwaysUseSpecificCalendar; - - public IPreferencesService PreferencesService { get; } - private readonly ICalendarService _calendarService; - private readonly IAccountService _accountService; - private readonly CultureInfo _calendarCulture; - private readonly bool _isLoaded = false; - - public CalendarSettingsPageViewModel(IPreferencesService preferencesService, ICalendarService calendarService, IAccountService accountService) - { - PreferencesService = preferencesService; - _calendarService = calendarService; - _accountService = accountService; - - var currentLanguageLanguageCode = WinoTranslationDictionary.GetLanguageFileNameRelativePath(preferencesService.CurrentLanguage); - var cultureInfo = new CultureInfo(currentLanguageLanguageCode); - _calendarCulture = cultureInfo; - - for (var i = 0; i < 7; i++) - { - DayNames.Add(cultureInfo.DateTimeFormat.DayNames[i]); - } - - var cultureFirstDayName = cultureInfo.DateTimeFormat.GetDayName(preferencesService.FirstDayOfWeek); - SelectedFirstDayOfWeekIndex = DayNames.IndexOf(cultureFirstDayName); - Is24HourHeaders = preferencesService.Prefer24HourTimeFormat; - IsWorkingHoursEnabled = preferencesService.IsWorkingHoursEnabled; - WorkingHourStart = preferencesService.WorkingHourStart; - WorkingHourEnd = preferencesService.WorkingHourEnd; - CellHourHeight = preferencesService.HourHeight; - WorkingDayStartIndex = DayNames.IndexOf(cultureInfo.DateTimeFormat.GetDayName(preferencesService.WorkingDayStart)); - WorkingDayEndIndex = DayNames.IndexOf(cultureInfo.DateTimeFormat.GetDayName(preferencesService.WorkingDayEnd)); - TimedDayHeaderDateFormat = preferencesService.CalendarTimedDayHeaderDateFormat; - SelectedTimedDayHeaderFormatPresetIndex = TimedDayHeaderFormatPresets.IndexOf(TimedDayHeaderDateFormat); - - var predefinedMinutes = _calendarService.GetPredefinedReminderMinutes(); - ReminderOptions.Add("None"); - foreach (var minutes in predefinedMinutes) - { - var displayText = minutes switch - { - >= 60 => $"{minutes / 60} Hour{(minutes / 60 > 1 ? "s" : "")}", - _ => $"{minutes} Minute{(minutes > 1 ? "s" : "")}" - }; - ReminderOptions.Add(displayText); - } - - if (preferencesService.DefaultReminderDurationInSeconds == 0) - { - SelectedDefaultReminderIndex = 0; - } - else - { - var minutes = (int)(preferencesService.DefaultReminderDurationInSeconds / 60); - var index = Array.IndexOf(predefinedMinutes, minutes); - SelectedDefaultReminderIndex = index >= 0 ? index + 1 : 0; - } - - var supportedSnoozeMinutes = CalendarReminderSnoozeOptions.GetSupportedSnoozeMinutes().ToArray(); - foreach (var snoozeMinutes in supportedSnoozeMinutes) - { - SnoozeOptions.Add(string.Format(Translator.CalendarReminder_SnoozeMinutesOption, snoozeMinutes)); - } - - var selectedSnoozeIndex = Array.IndexOf(supportedSnoozeMinutes, preferencesService.DefaultSnoozeDurationInMinutes); - SelectedDefaultSnoozeIndex = selectedSnoozeIndex >= 0 ? selectedSnoozeIndex : 0; - - NewEventBehaviorOptions.Add(new CalendarNewEventBehaviorOption(NewEventButtonBehavior.AskEachTime, Translator.CalendarSettings_NewEventBehavior_AskEachTime)); - NewEventBehaviorOptions.Add(new CalendarNewEventBehaviorOption(NewEventButtonBehavior.AlwaysUseSpecificCalendar, Translator.CalendarSettings_NewEventBehavior_AlwaysUseSpecificCalendar)); - SelectedNewEventBehaviorOption = NewEventBehaviorOptions.FirstOrDefault(option => option.Behavior == preferencesService.NewEventButtonBehavior) - ?? NewEventBehaviorOptions.First(); - - _isLoaded = true; - - LoadAccountsAsync(); - } - - private async void LoadAccountsAsync() - { - var accounts = await _accountService.GetAccountsAsync().ConfigureAwait(false); - var calendarsByAccount = new List<(MailAccount Account, List Calendars)>(); - - foreach (var account in accounts) - { - var calendars = await _calendarService.GetAccountCalendarsAsync(account.Id).ConfigureAwait(false); - calendarsByAccount.Add((account, calendars.Select(calendar => new AccountCalendarViewModel(account, calendar)).ToList())); - } - - await Dispatcher.ExecuteOnUIThread(() => - { - Accounts.Clear(); - AvailableNewEventCalendars.Clear(); - - foreach (var account in accounts) - { - Accounts.Add(account); - } - - foreach (var accountCalendars in calendarsByAccount) - { - foreach (var calendar in accountCalendars.Calendars) - { - AvailableNewEventCalendars.Add(calendar); - } - } - - ApplyStoredNewEventCalendarPreference(); - }); - } - - partial void OnCellHourHeightChanged(double oldValue, double newValue) => SaveSettings(); - partial void OnIs24HourHeadersChanged(bool value) - { - OnPropertyChanged(nameof(TimedHourLabelPreview)); - SaveSettings(); - } - partial void OnSelectedFirstDayOfWeekIndexChanged(int value) => SaveSettings(); - partial void OnIsWorkingHoursEnabledChanged(bool value) => SaveSettings(); - partial void OnWorkingHourStartChanged(TimeSpan value) => SaveSettings(); - partial void OnWorkingHourEndChanged(TimeSpan value) => SaveSettings(); - partial void OnWorkingDayStartIndexChanged(int value) => SaveSettings(); - partial void OnWorkingDayEndIndexChanged(int value) => SaveSettings(); - partial void OnTimedDayHeaderDateFormatChanged(string value) - { - OnPropertyChanged(nameof(TimedDayHeaderFormatPreview)); - OnPropertyChanged(nameof(TimedHourLabelPreview)); - - var normalizedFormat = string.IsNullOrWhiteSpace(value) ? "ddd dd" : value.Trim(); - var matchingPresetIndex = TimedDayHeaderFormatPresets - .Select((format, index) => new { format, index }) - .Where(item => string.Equals(item.format, normalizedFormat, StringComparison.Ordinal)) - .Select(item => item.index) - .DefaultIfEmpty(-1) - .First(); - - if (SelectedTimedDayHeaderFormatPresetIndex != matchingPresetIndex) - { - SelectedTimedDayHeaderFormatPresetIndex = matchingPresetIndex; - } - - SaveSettings(); - } - partial void OnSelectedTimedDayHeaderFormatPresetIndexChanged(int value) - { - if (value < 0 || value >= TimedDayHeaderFormatPresets.Count) - { - return; - } - - var selectedPreset = TimedDayHeaderFormatPresets[value]; - - if (string.Equals(TimedDayHeaderDateFormat, selectedPreset, StringComparison.Ordinal)) - { - return; - } - - TimedDayHeaderDateFormat = selectedPreset; - } - partial void OnSelectedDefaultReminderIndexChanged(int value) => SaveSettings(); - partial void OnSelectedDefaultSnoozeIndexChanged(int value) => SaveSettings(); - partial void OnSelectedNewEventBehaviorOptionChanged(CalendarNewEventBehaviorOption value) - { - OnPropertyChanged(nameof(ShouldShowSpecificNewEventCalendar)); - SaveSettings(); - } - partial void OnSelectedNewEventCalendarChanged(AccountCalendarViewModel value) => SaveSettings(); - - public string TimedDayHeaderFormatPreview - { - get - { - var format = string.IsNullOrWhiteSpace(TimedDayHeaderDateFormat) ? "ddd dd" : TimedDayHeaderDateFormat.Trim(); - var previewDates = new[] - { - new DateTime(2026, 3, 23), - new DateTime(2026, 3, 24), - new DateTime(2026, 3, 25) - }; - - try - { - return string.Join(" · ", previewDates.Select(date => date.ToString(format, _calendarCulture))); - } - catch (FormatException) - { - return string.Join(" · ", previewDates.Select(date => date.ToString("ddd dd", _calendarCulture))); - } - } - } - - public string TimedHourLabelPreview - { - get - { - var previewHours = new[] { 0, 9, 14, 24 }; - return string.Join(" · ", previewHours.Select(CurrentSettingsPreviewLabel)); - } - } - - private string CurrentSettingsPreviewLabel(int hour) - { - if (Is24HourHeaders) - { - return hour.ToString(_calendarCulture); - } - - var displayHour = hour % 24; - return DateTime.Today.AddHours(displayHour).ToString("h tt", _calendarCulture); - } - - public void SaveSettings() - { - if (!_isLoaded) - return; - - PreferencesService.FirstDayOfWeek = SelectedFirstDayOfWeekIndex switch - { - 0 => DayOfWeek.Sunday, - 1 => DayOfWeek.Monday, - 2 => DayOfWeek.Tuesday, - 3 => DayOfWeek.Wednesday, - 4 => DayOfWeek.Thursday, - 5 => DayOfWeek.Friday, - 6 => DayOfWeek.Saturday, - _ => throw new ArgumentOutOfRangeException() - }; - - PreferencesService.WorkingDayStart = WorkingDayStartIndex switch - { - 0 => DayOfWeek.Sunday, - 1 => DayOfWeek.Monday, - 2 => DayOfWeek.Tuesday, - 3 => DayOfWeek.Wednesday, - 4 => DayOfWeek.Thursday, - 5 => DayOfWeek.Friday, - 6 => DayOfWeek.Saturday, - _ => throw new ArgumentOutOfRangeException() - }; - - PreferencesService.WorkingDayEnd = WorkingDayEndIndex switch - { - 0 => DayOfWeek.Sunday, - 1 => DayOfWeek.Monday, - 2 => DayOfWeek.Tuesday, - 3 => DayOfWeek.Wednesday, - 4 => DayOfWeek.Thursday, - 5 => DayOfWeek.Friday, - 6 => DayOfWeek.Saturday, - _ => throw new ArgumentOutOfRangeException() - }; - - PreferencesService.Prefer24HourTimeFormat = Is24HourHeaders; - PreferencesService.IsWorkingHoursEnabled = IsWorkingHoursEnabled; - PreferencesService.WorkingHourStart = WorkingHourStart; - PreferencesService.WorkingHourEnd = WorkingHourEnd; - PreferencesService.HourHeight = CellHourHeight; - PreferencesService.CalendarTimedDayHeaderDateFormat = TimedDayHeaderDateFormat; - - if (SelectedDefaultReminderIndex == 0) - { - PreferencesService.DefaultReminderDurationInSeconds = 0; - } - else - { - var predefinedMinutes = _calendarService.GetPredefinedReminderMinutes(); - var minutes = predefinedMinutes[SelectedDefaultReminderIndex - 1]; - PreferencesService.DefaultReminderDurationInSeconds = minutes * 60; - } - - var supportedSnoozeMinutes = CalendarReminderSnoozeOptions.GetSupportedSnoozeMinutes(); - if (supportedSnoozeMinutes.Count > 0) - { - var selectedIndex = Math.Clamp(SelectedDefaultSnoozeIndex, 0, supportedSnoozeMinutes.Count - 1); - PreferencesService.DefaultSnoozeDurationInMinutes = supportedSnoozeMinutes[selectedIndex]; - } - - var newEventBehavior = SelectedNewEventBehaviorOption?.Behavior ?? NewEventButtonBehavior.AskEachTime; - if (newEventBehavior == NewEventButtonBehavior.AlwaysUseSpecificCalendar && SelectedNewEventCalendar != null) - { - PreferencesService.NewEventButtonBehavior = NewEventButtonBehavior.AlwaysUseSpecificCalendar; - PreferencesService.DefaultNewEventCalendarId = SelectedNewEventCalendar.Id; - } - else - { - PreferencesService.NewEventButtonBehavior = NewEventButtonBehavior.AskEachTime; - PreferencesService.DefaultNewEventCalendarId = null; - } - } - - private void ApplyStoredNewEventCalendarPreference() - { - var configuredCalendarId = PreferencesService.DefaultNewEventCalendarId; - var configuredCalendar = configuredCalendarId.HasValue - ? AvailableNewEventCalendars.FirstOrDefault(calendar => calendar.Id == configuredCalendarId.Value) - : null; - - if (PreferencesService.NewEventButtonBehavior == NewEventButtonBehavior.AlwaysUseSpecificCalendar && configuredCalendar == null) - { - SelectedNewEventBehaviorOption = NewEventBehaviorOptions.First(option => option.Behavior == NewEventButtonBehavior.AskEachTime); - SelectedNewEventCalendar = null; - return; - } - - SelectedNewEventCalendar = configuredCalendar - ?? AvailableNewEventCalendars.FirstOrDefault(calendar => calendar.IsPrimary) - ?? AvailableNewEventCalendars.FirstOrDefault(); - } -} - -public sealed class CalendarNewEventBehaviorOption -{ - public NewEventButtonBehavior Behavior { get; } - public string DisplayText { get; } - - public CalendarNewEventBehaviorOption(NewEventButtonBehavior behavior, string displayText) - { - Behavior = behavior; - DisplayText = displayText; - } -} diff --git a/Wino.Calendar.ViewModels/CalendarSettingsSectionViewModelBase.cs b/Wino.Calendar.ViewModels/CalendarSettingsSectionViewModelBase.cs new file mode 100644 index 00000000..3058479f --- /dev/null +++ b/Wino.Calendar.ViewModels/CalendarSettingsSectionViewModelBase.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; +using System.Linq; +using Wino.Calendar.ViewModels.Data; +using Wino.Core.Domain; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Translations; +using Wino.Core.ViewModels; + +namespace Wino.Calendar.ViewModels; + +public abstract class CalendarSettingsSectionViewModelBase : CalendarBaseViewModel +{ + protected CalendarSettingsSectionViewModelBase( + IPreferencesService preferencesService, + ICalendarService calendarService, + IAccountService accountService) + { + PreferencesService = preferencesService; + CalendarService = calendarService; + AccountService = accountService; + + var languageCode = WinoTranslationDictionary.GetLanguageFileNameRelativePath(preferencesService.CurrentLanguage); + CalendarCulture = new CultureInfo(languageCode); + + for (var index = 0; index < 7; index++) + { + DayNames.Add(CalendarCulture.DateTimeFormat.DayNames[index]); + } + } + + protected IPreferencesService PreferencesService { get; } + protected ICalendarService CalendarService { get; } + protected IAccountService AccountService { get; } + protected CultureInfo CalendarCulture { get; } + protected bool IsLoaded { get; set; } + + public ObservableCollection DayNames { get; } = []; + public ObservableCollection ReminderOptions { get; } = []; + public ObservableCollection SnoozeOptions { get; } = []; + public ObservableCollection NewEventBehaviorOptions { get; } = []; + public ObservableCollection AvailableNewEventCalendars { get; } = []; + public ObservableCollection TimedDayHeaderFormatPresets { get; } = + [ + "ddd dd", + "dddd dd", + "ddd d MMM", + "dd MMM ddd", + "M/d ddd" + ]; + + protected void LoadReminderOptions() + { + ReminderOptions.Clear(); + + var predefinedMinutes = CalendarService.GetPredefinedReminderMinutes(); + ReminderOptions.Add("None"); + + foreach (var minutes in predefinedMinutes) + { + var displayText = minutes switch + { + >= 60 => $"{minutes / 60} Hour{(minutes / 60 > 1 ? "s" : "")}", + _ => $"{minutes} Minute{(minutes > 1 ? "s" : "")}" + }; + + ReminderOptions.Add(displayText); + } + } + + protected int GetSelectedReminderIndex() + { + if (PreferencesService.DefaultReminderDurationInSeconds == 0) + return 0; + + var minutes = (int)(PreferencesService.DefaultReminderDurationInSeconds / 60); + var predefinedMinutes = CalendarService.GetPredefinedReminderMinutes(); + var index = Array.IndexOf(predefinedMinutes, minutes); + return index >= 0 ? index + 1 : 0; + } + + protected void SaveReminderIndex(int selectedDefaultReminderIndex) + { + if (selectedDefaultReminderIndex == 0) + { + PreferencesService.DefaultReminderDurationInSeconds = 0; + return; + } + + var predefinedMinutes = CalendarService.GetPredefinedReminderMinutes(); + var minutes = predefinedMinutes[selectedDefaultReminderIndex - 1]; + PreferencesService.DefaultReminderDurationInSeconds = minutes * 60; + } + + protected void LoadSnoozeOptions() + { + SnoozeOptions.Clear(); + + foreach (var snoozeMinutes in CalendarReminderSnoozeOptions.GetSupportedSnoozeMinutes()) + { + SnoozeOptions.Add(string.Format(Translator.CalendarReminder_SnoozeMinutesOption, snoozeMinutes)); + } + } + + protected int GetSelectedSnoozeIndex() + { + var supportedSnoozeMinutes = CalendarReminderSnoozeOptions.GetSupportedSnoozeMinutes().ToArray(); + var selectedIndex = Array.IndexOf(supportedSnoozeMinutes, PreferencesService.DefaultSnoozeDurationInMinutes); + return selectedIndex >= 0 ? selectedIndex : 0; + } + + protected void SaveSnoozeIndex(int selectedDefaultSnoozeIndex) + { + var supportedSnoozeMinutes = CalendarReminderSnoozeOptions.GetSupportedSnoozeMinutes(); + if (supportedSnoozeMinutes.Count == 0) + return; + + var selectedIndex = Math.Clamp(selectedDefaultSnoozeIndex, 0, supportedSnoozeMinutes.Count - 1); + PreferencesService.DefaultSnoozeDurationInMinutes = supportedSnoozeMinutes[selectedIndex]; + } + + protected void LoadNewEventBehaviorOptions() + { + NewEventBehaviorOptions.Clear(); + NewEventBehaviorOptions.Add(new CalendarNewEventBehaviorOption(NewEventButtonBehavior.AskEachTime, Translator.CalendarSettings_NewEventBehavior_AskEachTime)); + NewEventBehaviorOptions.Add(new CalendarNewEventBehaviorOption(NewEventButtonBehavior.AlwaysUseSpecificCalendar, Translator.CalendarSettings_NewEventBehavior_AlwaysUseSpecificCalendar)); + } + + protected CalendarNewEventBehaviorOption GetSelectedNewEventBehaviorOption() + => NewEventBehaviorOptions.FirstOrDefault(option => option.Behavior == PreferencesService.NewEventButtonBehavior) + ?? NewEventBehaviorOptions.First(); + + protected async void LoadCalendarsAsync(Action applySelection) + { + var accounts = await AccountService.GetAccountsAsync().ConfigureAwait(false); + var calendarsByAccount = new List(); + + foreach (var account in accounts) + { + var calendars = await CalendarService.GetAccountCalendarsAsync(account.Id).ConfigureAwait(false); + calendarsByAccount.AddRange(calendars.Select(calendar => new AccountCalendarViewModel(account, calendar))); + } + + await ExecuteUIThread(() => + { + AvailableNewEventCalendars.Clear(); + + foreach (var calendar in calendarsByAccount) + { + AvailableNewEventCalendars.Add(calendar); + } + + applySelection(); + }); + } + + protected AccountCalendarViewModel ResolveSelectedNewEventCalendar() + { + var configuredCalendarId = PreferencesService.DefaultNewEventCalendarId; + return configuredCalendarId.HasValue + ? AvailableNewEventCalendars.FirstOrDefault(calendar => calendar.Id == configuredCalendarId.Value) + : null; + } + + protected AccountCalendarViewModel ResolveFallbackNewEventCalendar() + => AvailableNewEventCalendars.FirstOrDefault(calendar => calendar.IsPrimary) + ?? AvailableNewEventCalendars.FirstOrDefault(); + + protected void SaveNewEventBehavior(CalendarNewEventBehaviorOption selectedBehaviorOption, AccountCalendarViewModel selectedCalendar) + { + var newEventBehavior = selectedBehaviorOption?.Behavior ?? NewEventButtonBehavior.AskEachTime; + if (newEventBehavior == NewEventButtonBehavior.AlwaysUseSpecificCalendar && selectedCalendar != null) + { + PreferencesService.NewEventButtonBehavior = NewEventButtonBehavior.AlwaysUseSpecificCalendar; + PreferencesService.DefaultNewEventCalendarId = selectedCalendar.Id; + return; + } + + PreferencesService.NewEventButtonBehavior = NewEventButtonBehavior.AskEachTime; + PreferencesService.DefaultNewEventCalendarId = null; + } +} + +public sealed class CalendarNewEventBehaviorOption +{ + public CalendarNewEventBehaviorOption(NewEventButtonBehavior behavior, string displayText) + { + Behavior = behavior; + DisplayText = displayText; + } + + public NewEventButtonBehavior Behavior { get; } + public string DisplayText { get; } +} diff --git a/Wino.Core.Domain/Enums/WinoPage.cs b/Wino.Core.Domain/Enums/WinoPage.cs index 10fbb392..eb78105a 100644 --- a/Wino.Core.Domain/Enums/WinoPage.cs +++ b/Wino.Core.Domain/Enums/WinoPage.cs @@ -21,7 +21,6 @@ public enum WinoPage MessageListPage, MailListPage, ReadComposePanePage, - LanguageTimePage, AppPreferencesPage, SettingOptionsPage, AliasManagementPage, @@ -29,6 +28,9 @@ public enum WinoPage KeyboardShortcutsPage, CalendarPage, CalendarSettingsPage, + CalendarRenderingSettingsPage, + CalendarNotificationSettingsPage, + CalendarPreferenceSettingsPage, CalendarAccountSettingsPage, EventDetailsPage, CalendarEventComposePage, diff --git a/Wino.Core.Domain/Models/Settings/SettingsNavigationItemInfo.cs b/Wino.Core.Domain/Models/Settings/SettingsNavigationItemInfo.cs index dbcbdc4c..af8ea957 100644 --- a/Wino.Core.Domain/Models/Settings/SettingsNavigationItemInfo.cs +++ b/Wino.Core.Domain/Models/Settings/SettingsNavigationItemInfo.cs @@ -47,11 +47,11 @@ public static class SettingsNavigationInfoProvider Translator.SettingsAppPreferences_Description, "\uE770", searchKeywords: Translator.SettingsSearch_AppPreferences_Keywords), - new(WinoPage.LanguageTimePage, - Translator.SettingsLanguageTime_Title, - Translator.SettingsLanguageTime_Description, - "\uE775", - searchKeywords: Translator.SettingsSearch_LanguageTime_Keywords), + new(WinoPage.KeyboardShortcutsPage, + Translator.Settings_KeyboardShortcuts_Title, + Translator.Settings_KeyboardShortcuts_Description, + "\uE765", + searchKeywords: Translator.SettingsSearch_KeyboardShortcuts_Keywords), new(WinoPage.PersonalizationPage, Translator.SettingsPersonalization_Title, Translator.SettingsPersonalization_Description, @@ -63,11 +63,6 @@ public static class SettingsNavigationInfoProvider "\uE946", searchKeywords: Translator.SettingsSearch_About_Keywords), new(null, Translator.SettingsOptions_MailSection, string.Empty, "\uE715", isSeparator: true), - new(WinoPage.KeyboardShortcutsPage, - Translator.Settings_KeyboardShortcuts_Title, - Translator.Settings_KeyboardShortcuts_Description, - "\uE765", - searchKeywords: Translator.SettingsSearch_KeyboardShortcuts_Keywords), new(WinoPage.MessageListPage, Translator.SettingsMessageList_Title, Translator.SettingsMessageList_Description, @@ -89,11 +84,22 @@ public static class SettingsNavigationInfoProvider "\uE81C", searchKeywords: Translator.SettingsSearch_Storage_Keywords), new(null, Translator.SettingsOptions_CalendarSection, string.Empty, "\uE787", isSeparator: true), - new(WinoPage.CalendarSettingsPage, - Translator.SettingsCalendarSettings_Title, - Translator.SettingsCalendarSettings_Description, + new(WinoPage.CalendarRenderingSettingsPage, + Translator.CalendarSettings_Rendering_Title, + Translator.CalendarSettings_Rendering_Description, "\uE787", searchKeywords: Translator.SettingsSearch_CalendarSettings_Keywords) + , + new(WinoPage.CalendarNotificationSettingsPage, + Translator.CalendarSettings_Notifications_Title, + Translator.CalendarSettings_Notifications_Description, + "\uE7F4", + searchKeywords: Translator.SettingsSearch_CalendarSettings_Keywords), + new(WinoPage.CalendarPreferenceSettingsPage, + Translator.CalendarSettings_Preferences_Title, + Translator.CalendarSettings_Preferences_Description, + "\uE713", + searchKeywords: Translator.SettingsSearch_CalendarSettings_Keywords) ]; } @@ -140,9 +146,11 @@ public static class SettingsNavigationInfoProvider WinoPage.AboutPage => Translator.SettingsAbout_Title, WinoPage.MessageListPage => Translator.SettingsMessageList_Title, WinoPage.ReadComposePanePage => Translator.SettingsReadComposePane_Title, - WinoPage.LanguageTimePage => Translator.SettingsLanguageTime_Title, WinoPage.AppPreferencesPage => Translator.SettingsAppPreferences_Title, - WinoPage.CalendarSettingsPage => Translator.SettingsCalendarSettings_Title, + WinoPage.CalendarSettingsPage => Translator.CalendarSettings_Preferences_Title, + WinoPage.CalendarRenderingSettingsPage => Translator.CalendarSettings_Rendering_Title, + WinoPage.CalendarNotificationSettingsPage => Translator.CalendarSettings_Notifications_Title, + WinoPage.CalendarPreferenceSettingsPage => Translator.CalendarSettings_Preferences_Title, WinoPage.SignatureAndEncryptionPage => Translator.SettingsSignatureAndEncryption_Title, WinoPage.KeyboardShortcutsPage => Translator.Settings_KeyboardShortcuts_Title, WinoPage.StoragePage => Translator.SettingsStorage_Title, @@ -162,7 +170,8 @@ public static class SettingsNavigationInfoProvider WinoPage.ImapCalDavSettingsPage => WinoPage.ManageAccountsPage, WinoPage.EmailTemplatesPage => WinoPage.ManageAccountsPage, WinoPage.CreateEmailTemplatePage => WinoPage.ManageAccountsPage, - WinoPage.CalendarAccountSettingsPage => WinoPage.CalendarSettingsPage, + WinoPage.CalendarSettingsPage => WinoPage.CalendarPreferenceSettingsPage, + WinoPage.CalendarAccountSettingsPage => WinoPage.CalendarPreferenceSettingsPage, _ => pageType }; diff --git a/Wino.Core.Domain/Translations/en_US/resources.json b/Wino.Core.Domain/Translations/en_US/resources.json index 57db9af5..5ad559a5 100644 --- a/Wino.Core.Domain/Translations/en_US/resources.json +++ b/Wino.Core.Domain/Translations/en_US/resources.json @@ -1127,6 +1127,12 @@ "CalendarSettings_NewEventBehavior_Description": "Choose whether the New Event button should ask for a calendar each time or always open a specific calendar.", "CalendarSettings_NewEventBehavior_AskEachTime": "Ask each time.", "CalendarSettings_NewEventBehavior_AlwaysUseSpecificCalendar": "Always use specific calendar.", + "CalendarSettings_Rendering_Title": "Rendering", + "CalendarSettings_Rendering_Description": "Configure calendar layout and display behavior.", + "CalendarSettings_Notifications_Title": "Notifications", + "CalendarSettings_Notifications_Description": "Choose default reminder and snooze behavior.", + "CalendarSettings_Preferences_Title": "Preferences", + "CalendarSettings_Preferences_Description": "Set how the New Event button behaves.", "WhatIsNew_GetStartedButton": "Get Started", "WhatIsNew_ContinueAnywayButton": "Continue anyway", "WhatIsNew_PreparingForNewVersionButton": "Preparing for new version...", @@ -1176,6 +1182,12 @@ "WinoAccount_Management_AiPackPromoPrice": "$4.99 / mo", "WinoAccount_Management_AiPackPromoRequests": "1,000 requests", "WinoAccount_Management_AiPackGetButton": "Get AI Pack", + "WinoAddOn_AI_PACK_Name": "Wino AI Pack", + "WinoAddOn_AI_PACK_Description": "AI-powered tools for translate, rewrite, and summarize actions in Wino Mail.", + "WinoAddOn_AI_PACK_Keywords": "AI, translate, rewrite, summarize, productivity", + "WinoAddOn_UNLIMITED_ACCOUNTS_Name": "Unlimited Accounts", + "WinoAddOn_UNLIMITED_ACCOUNTS_Description": "Remove the account limit and add as many mail accounts as you need.", + "WinoAddOn_UNLIMITED_ACCOUNTS_Keywords": "accounts, unlimited, premium, add-on", "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", diff --git a/Wino.Core.ViewModels/WinoAccountManagementPageViewModel.cs b/Wino.Core.ViewModels/WinoAccountManagementPageViewModel.cs index 516fb3fa..65d0a5d7 100644 --- a/Wino.Core.ViewModels/WinoAccountManagementPageViewModel.cs +++ b/Wino.Core.ViewModels/WinoAccountManagementPageViewModel.cs @@ -116,7 +116,7 @@ public partial class WinoAccountManagementPageViewModel : CoreBaseViewModel, [RelayCommand] private async Task ChangePasswordAsync() { - var account = await _profileService.GetActiveAccountAsync().ConfigureAwait(false); + var account = await _profileService.GetActiveAccountAsync(); if (account == null) { _dialogService.InfoBarMessage(Translator.GeneralTitle_Warning, @@ -128,14 +128,14 @@ public partial class WinoAccountManagementPageViewModel : CoreBaseViewModel, var shouldContinue = await _dialogService.ShowConfirmationDialogAsync( string.Format(Translator.WinoAccount_ChangePassword_ConfirmationMessage, account.Email), Translator.WinoAccount_ChangePassword_Title, - Translator.WinoAccount_ChangePassword_Action).ConfigureAwait(false); + Translator.WinoAccount_ChangePassword_Action); if (!shouldContinue) { return; } - var response = await _profileService.ForgotPasswordAsync(account.Email).ConfigureAwait(false); + var response = await _profileService.ForgotPasswordAsync(account.Email); if (!response.IsSuccess) { _dialogService.InfoBarMessage(Translator.GeneralTitle_Error, diff --git a/Wino.Mail.ViewModels/AppPreferencesPageViewModel.cs b/Wino.Mail.ViewModels/AppPreferencesPageViewModel.cs index 2cc0df80..8f4c2724 100644 --- a/Wino.Mail.ViewModels/AppPreferencesPageViewModel.cs +++ b/Wino.Mail.ViewModels/AppPreferencesPageViewModel.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; @@ -6,73 +6,22 @@ using Wino.Core.Domain; using Wino.Core.Domain.Enums; using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Models.Navigation; +using Wino.Core.Domain.Models.Translations; namespace Wino.Mail.ViewModels; public partial class AppPreferencesPageViewModel : MailBaseViewModel { - public IPreferencesService PreferencesService { get; } - - [ObservableProperty] - public partial List SearchModes { get; set; } - - [ObservableProperty] - public partial List ApplicationModes { get; set; } - - [ObservableProperty] - [NotifyPropertyChangedFor(nameof(IsStartupBehaviorDisabled))] - [NotifyPropertyChangedFor(nameof(IsStartupBehaviorEnabled))] - private StartupBehaviorResult startupBehaviorResult; - - private int _emailSyncIntervalMinutes; - public int EmailSyncIntervalMinutes - { - get => _emailSyncIntervalMinutes; - set - { - SetProperty(ref _emailSyncIntervalMinutes, value); - - PreferencesService.EmailSyncIntervalMinutes = value; - } - } - - public bool IsStartupBehaviorDisabled => !IsStartupBehaviorEnabled; - public bool IsStartupBehaviorEnabled => StartupBehaviorResult == StartupBehaviorResult.Enabled; - - private string _selectedDefaultSearchMode; - public string SelectedDefaultSearchMode - { - get => _selectedDefaultSearchMode; - set - { - SetProperty(ref _selectedDefaultSearchMode, value); - - PreferencesService.DefaultSearchMode = (SearchMode)SearchModes.IndexOf(value); - } - } - - private string _selectedDefaultApplicationMode; - public string SelectedDefaultApplicationMode - { - get => _selectedDefaultApplicationMode; - set - { - SetProperty(ref _selectedDefaultApplicationMode, value); - - PreferencesService.DefaultApplicationMode = (WinoApplicationMode)ApplicationModes.IndexOf(value); - } - } - - private readonly IMailDialogService _dialogService; - private readonly IStartupBehaviorService _startupBehaviorService; - - public AppPreferencesPageViewModel(IMailDialogService dialogService, - IPreferencesService preferencesService, - IStartupBehaviorService startupBehaviorService) + public AppPreferencesPageViewModel( + IMailDialogService dialogService, + IPreferencesService preferencesService, + IStartupBehaviorService startupBehaviorService, + ITranslationService translationService) { _dialogService = dialogService; PreferencesService = preferencesService; _startupBehaviorService = startupBehaviorService; + _translationService = translationService; SearchModes = [ @@ -93,17 +42,81 @@ public partial class AppPreferencesPageViewModel : MailBaseViewModel EmailSyncIntervalMinutes = PreferencesService.EmailSyncIntervalMinutes; } + public IPreferencesService PreferencesService { get; } + + [ObservableProperty] + public partial List SearchModes { get; set; } + + [ObservableProperty] + public partial List ApplicationModes { get; set; } + + [ObservableProperty] + public partial List AvailableLanguages { get; set; } = []; + + [ObservableProperty] + public partial AppLanguageModel SelectedLanguage { get; set; } + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(IsStartupBehaviorDisabled))] + [NotifyPropertyChangedFor(nameof(IsStartupBehaviorEnabled))] + private StartupBehaviorResult startupBehaviorResult; + + private readonly IMailDialogService _dialogService; + private readonly IStartupBehaviorService _startupBehaviorService; + private readonly ITranslationService _translationService; + private bool _isLanguageInitialized; + private int _emailSyncIntervalMinutes; + private string _selectedDefaultSearchMode; + private string _selectedDefaultApplicationMode; + + public int EmailSyncIntervalMinutes + { + get => _emailSyncIntervalMinutes; + set + { + SetProperty(ref _emailSyncIntervalMinutes, value); + PreferencesService.EmailSyncIntervalMinutes = value; + } + } + + public bool IsStartupBehaviorDisabled => !IsStartupBehaviorEnabled; + public bool IsStartupBehaviorEnabled => StartupBehaviorResult == StartupBehaviorResult.Enabled; + + public string SelectedDefaultSearchMode + { + get => _selectedDefaultSearchMode; + set + { + SetProperty(ref _selectedDefaultSearchMode, value); + PreferencesService.DefaultSearchMode = (SearchMode)SearchModes.IndexOf(value); + } + } + + public string SelectedDefaultApplicationMode + { + get => _selectedDefaultApplicationMode; + set + { + SetProperty(ref _selectedDefaultApplicationMode, value); + PreferencesService.DefaultApplicationMode = (WinoApplicationMode)ApplicationModes.IndexOf(value); + } + } + + partial void OnSelectedLanguageChanged(AppLanguageModel value) + { + if (!_isLanguageInitialized || value == null) + return; + + _ = _translationService.InitializeLanguageAsync(value.Language); + } + [RelayCommand] private async Task ToggleStartupBehaviorAsync() { if (IsStartupBehaviorEnabled) - { await DisableStartupAsync(); - } else - { await EnableStartupAsync(); - } OnPropertyChanged(nameof(IsStartupBehaviorEnabled)); } @@ -111,14 +124,12 @@ public partial class AppPreferencesPageViewModel : MailBaseViewModel private async Task EnableStartupAsync() { StartupBehaviorResult = await _startupBehaviorService.ToggleStartupBehavior(true); - NotifyCurrentStartupState(); } private async Task DisableStartupAsync() { StartupBehaviorResult = await _startupBehaviorService.ToggleStartupBehavior(false); - NotifyCurrentStartupState(); } @@ -146,12 +157,15 @@ public partial class AppPreferencesPageViewModel : MailBaseViewModel } } - - public override async void OnNavigatedTo(NavigationMode mode, object parameters) { base.OnNavigatedTo(mode, parameters); + AvailableLanguages = _translationService.GetAvailableLanguages(); + SelectedLanguage = AvailableLanguages.Find(language => language.Language == PreferencesService.CurrentLanguage) + ?? (AvailableLanguages.Count > 0 ? AvailableLanguages[0] : null); + _isLanguageInitialized = true; + StartupBehaviorResult = await _startupBehaviorService.GetCurrentStartupBehaviorAsync(); } } diff --git a/Wino.Mail.ViewModels/LanguageTimePageViewModel.cs b/Wino.Mail.ViewModels/LanguageTimePageViewModel.cs deleted file mode 100644 index 51d36ab9..00000000 --- a/Wino.Mail.ViewModels/LanguageTimePageViewModel.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using CommunityToolkit.Mvvm.ComponentModel; -using Wino.Core.Domain.Interfaces; -using Wino.Core.Domain.Models.Navigation; -using Wino.Core.Domain.Models.Translations; - -namespace Wino.Mail.ViewModels; - -public partial class LanguageTimePageViewModel(IPreferencesService preferencesService, ITranslationService translationService) : MailBaseViewModel -{ - public IPreferencesService PreferencesService { get; } = preferencesService; - private readonly ITranslationService _translationService = translationService; - - [ObservableProperty] - private List _availableLanguages; - - [ObservableProperty] - private AppLanguageModel _selectedLanguage; - - private bool isInitialized = false; - - public override void OnNavigatedTo(NavigationMode mode, object parameters) - { - base.OnNavigatedTo(mode, parameters); - - AvailableLanguages = _translationService.GetAvailableLanguages(); - - SelectedLanguage = AvailableLanguages.FirstOrDefault(a => a.Language == PreferencesService.CurrentLanguage); - - isInitialized = true; - } - - protected override async void OnPropertyChanged(PropertyChangedEventArgs e) - { - base.OnPropertyChanged(e); - - if (!isInitialized) return; - - if (e.PropertyName == nameof(SelectedLanguage)) - { - await _translationService.InitializeLanguageAsync(SelectedLanguage.Language); - } - } -} diff --git a/Wino.Mail.ViewModels/MessageListPageViewModel.cs b/Wino.Mail.ViewModels/MessageListPageViewModel.cs index 2a381093..a08cf320 100644 --- a/Wino.Mail.ViewModels/MessageListPageViewModel.cs +++ b/Wino.Mail.ViewModels/MessageListPageViewModel.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Threading.Tasks; using CommunityToolkit.Mvvm.Input; using Wino.Core.Domain; +using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Enums; using Wino.Core.Domain.Interfaces; @@ -40,6 +42,10 @@ public partial class MessageListPageViewModel : MailBaseViewModel Translator.HoverActionOption_MoveJunk ]; + public IMailItemDisplayInformation DemoPreviewMailItemInformation { get; } = new DemoMailItemDisplayInformation(); + + public MailListDisplayMode SelectedMailSpacingMode => availableMailSpacingOptions[selectedMailSpacingIndex]; + private int selectedMarkAsOptionIndex; public int SelectedMarkAsOptionIndex { @@ -62,6 +68,7 @@ public partial class MessageListPageViewModel : MailBaseViewModel if (SetProperty(ref selectedMailSpacingIndex, value) && value >= 0 && value < availableMailSpacingOptions.Count) { PreferencesService.MailItemDisplayMode = availableMailSpacingOptions[value]; + OnPropertyChanged(nameof(SelectedMailSpacingMode)); } } } @@ -135,4 +142,32 @@ public partial class MessageListPageViewModel : MailBaseViewModel _statePersistenceService.MailListPaneLength = 420; _dialogService.InfoBarMessage(Translator.GeneralTitle_Info, Translator.Info_MailListSizeResetSuccessMessage, InfoBarMessageType.Success); } + + private sealed class DemoMailItemDisplayInformation : IMailItemDisplayInformation + { + public event PropertyChangedEventHandler PropertyChanged + { + add { } + remove { } + } + + public string Subject => "Quarterly planning notes"; + public string FromName => "Ava Brooks"; + public string FromAddress => "ava@contoso.com"; + public string PreviewText => "Agenda draft, attendee updates, and a few follow-up items for this week."; + public bool IsRead => false; + public bool IsDraft => false; + public bool HasAttachments => true; + public bool IsCalendarEvent => false; + public bool IsFlagged => true; + public DateTime CreationDate => DateTime.Now.AddMinutes(-12); + public Guid? ContactPictureFileId => null; + public bool ThumbnailUpdatedEvent => false; + public bool IsThreadExpanded => false; + public AccountContact SenderContact => new() + { + Address = "ava@contoso.com", + Name = "Ava Brooks" + }; + } } diff --git a/Wino.Mail.WinUI/App.xaml.cs b/Wino.Mail.WinUI/App.xaml.cs index d7f8286b..467b9584 100644 --- a/Wino.Mail.WinUI/App.xaml.cs +++ b/Wino.Mail.WinUI/App.xaml.cs @@ -319,7 +319,6 @@ public partial class App : WinoApplication, services.AddTransient(typeof(MessageListPageViewModel)); services.AddTransient(typeof(ReadComposePanePageViewModel)); services.AddTransient(typeof(MergedAccountDetailsPageViewModel)); - services.AddTransient(typeof(LanguageTimePageViewModel)); services.AddTransient(typeof(AppPreferencesPageViewModel)); services.AddTransient(typeof(StoragePageViewModel)); services.AddTransient(typeof(WinoAccountManagementPageViewModel)); @@ -329,7 +328,9 @@ public partial class App : WinoApplication, services.AddTransient(typeof(EmailTemplatesPageViewModel)); services.AddTransient(typeof(CreateEmailTemplatePageViewModel)); services.AddSingleton(typeof(CalendarPageViewModel)); - services.AddTransient(typeof(CalendarSettingsPageViewModel)); + services.AddTransient(typeof(CalendarRenderingSettingsPageViewModel)); + services.AddTransient(typeof(CalendarNotificationSettingsPageViewModel)); + services.AddTransient(typeof(CalendarPreferenceSettingsPageViewModel)); services.AddTransient(typeof(CalendarAccountSettingsPageViewModel)); services.AddTransient(typeof(EventDetailsPageViewModel)); services.AddTransient(typeof(CalendarEventComposePageViewModel)); diff --git a/Wino.Mail.WinUI/Services/NavigationService.cs b/Wino.Mail.WinUI/Services/NavigationService.cs index ba69920c..cb204daf 100644 --- a/Wino.Mail.WinUI/Services/NavigationService.cs +++ b/Wino.Mail.WinUI/Services/NavigationService.cs @@ -80,7 +80,6 @@ public class NavigationService : NavigationServiceBase, INavigationService WinoPage.PersonalizationPage, WinoPage.MessageListPage, WinoPage.ReadComposePanePage, - WinoPage.LanguageTimePage, WinoPage.AppPreferencesPage, WinoPage.AliasManagementPage, WinoPage.ImapCalDavSettingsPage, @@ -91,6 +90,9 @@ public class NavigationService : NavigationServiceBase, INavigationService WinoPage.StoragePage, WinoPage.WinoAccountManagementPage, WinoPage.CalendarSettingsPage, + WinoPage.CalendarRenderingSettingsPage, + WinoPage.CalendarNotificationSettingsPage, + WinoPage.CalendarPreferenceSettingsPage, WinoPage.CalendarAccountSettingsPage ]; @@ -148,7 +150,6 @@ public class NavigationService : NavigationServiceBase, INavigationService WinoPage.SettingOptionsPage => typeof(SettingOptionsPage), WinoPage.AppPreferencesPage => typeof(AppPreferencesPage), WinoPage.AliasManagementPage => typeof(AliasManagementPage), - WinoPage.LanguageTimePage => typeof(LanguageTimePage), WinoPage.ImapCalDavSettingsPage => typeof(ImapCalDavSettingsPage), WinoPage.KeyboardShortcutsPage => typeof(KeyboardShortcutsPage), WinoPage.ContactsPage => typeof(ContactsPage), @@ -164,7 +165,10 @@ public class NavigationService : NavigationServiceBase, INavigationService WinoPage.CalendarPage => typeof(CalendarPage), WinoPage.EventDetailsPage => typeof(EventDetailsPage), WinoPage.CalendarEventComposePage => typeof(CalendarEventComposePage), - WinoPage.CalendarSettingsPage => typeof(CalendarSettingsPage), + WinoPage.CalendarSettingsPage => typeof(CalendarPreferenceSettingsPage), + WinoPage.CalendarRenderingSettingsPage => typeof(CalendarRenderingSettingsPage), + WinoPage.CalendarNotificationSettingsPage => typeof(CalendarNotificationSettingsPage), + WinoPage.CalendarPreferenceSettingsPage => typeof(CalendarPreferenceSettingsPage), WinoPage.CalendarAccountSettingsPage => typeof(CalendarAccountSettingsPage), _ => null, }; diff --git a/Wino.Mail.WinUI/Views/Abstract/CalendarNotificationSettingsPageAbstract.cs b/Wino.Mail.WinUI/Views/Abstract/CalendarNotificationSettingsPageAbstract.cs new file mode 100644 index 00000000..6879f6c3 --- /dev/null +++ b/Wino.Mail.WinUI/Views/Abstract/CalendarNotificationSettingsPageAbstract.cs @@ -0,0 +1,5 @@ +using Wino.Calendar.ViewModels; + +namespace Wino.Mail.WinUI.Views.Abstract; + +public abstract class CalendarNotificationSettingsPageAbstract : BasePage { } diff --git a/Wino.Mail.WinUI/Views/Abstract/CalendarPreferenceSettingsPageAbstract.cs b/Wino.Mail.WinUI/Views/Abstract/CalendarPreferenceSettingsPageAbstract.cs new file mode 100644 index 00000000..77065c8f --- /dev/null +++ b/Wino.Mail.WinUI/Views/Abstract/CalendarPreferenceSettingsPageAbstract.cs @@ -0,0 +1,5 @@ +using Wino.Calendar.ViewModels; + +namespace Wino.Mail.WinUI.Views.Abstract; + +public abstract class CalendarPreferenceSettingsPageAbstract : BasePage { } diff --git a/Wino.Mail.WinUI/Views/Abstract/CalendarRenderingSettingsPageAbstract.cs b/Wino.Mail.WinUI/Views/Abstract/CalendarRenderingSettingsPageAbstract.cs new file mode 100644 index 00000000..f83cc0bf --- /dev/null +++ b/Wino.Mail.WinUI/Views/Abstract/CalendarRenderingSettingsPageAbstract.cs @@ -0,0 +1,5 @@ +using Wino.Calendar.ViewModels; + +namespace Wino.Mail.WinUI.Views.Abstract; + +public abstract class CalendarRenderingSettingsPageAbstract : BasePage { } diff --git a/Wino.Mail.WinUI/Views/Abstract/CalendarSettingsPageAbstract.cs b/Wino.Mail.WinUI/Views/Abstract/CalendarSettingsPageAbstract.cs deleted file mode 100644 index af00668c..00000000 --- a/Wino.Mail.WinUI/Views/Abstract/CalendarSettingsPageAbstract.cs +++ /dev/null @@ -1,5 +0,0 @@ -using Wino.Calendar.ViewModels; - -namespace Wino.Mail.WinUI.Views.Abstract; - -public abstract class CalendarSettingsPageAbstract : BasePage { } diff --git a/Wino.Mail.WinUI/Views/Abstract/LanguageTimePageAbstract.cs b/Wino.Mail.WinUI/Views/Abstract/LanguageTimePageAbstract.cs deleted file mode 100644 index 58dd0137..00000000 --- a/Wino.Mail.WinUI/Views/Abstract/LanguageTimePageAbstract.cs +++ /dev/null @@ -1,6 +0,0 @@ -using Wino.Mail.WinUI; -using Wino.Mail.ViewModels; - -namespace Wino.Views.Abstract; - -public abstract class LanguageTimePageAbstract : BasePage { } diff --git a/Wino.Mail.WinUI/Views/Calendar/CalendarNotificationSettingsPage.xaml b/Wino.Mail.WinUI/Views/Calendar/CalendarNotificationSettingsPage.xaml new file mode 100644 index 00000000..a957dc61 --- /dev/null +++ b/Wino.Mail.WinUI/Views/Calendar/CalendarNotificationSettingsPage.xaml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail.WinUI/Views/Calendar/CalendarNotificationSettingsPage.xaml.cs b/Wino.Mail.WinUI/Views/Calendar/CalendarNotificationSettingsPage.xaml.cs new file mode 100644 index 00000000..09e89d81 --- /dev/null +++ b/Wino.Mail.WinUI/Views/Calendar/CalendarNotificationSettingsPage.xaml.cs @@ -0,0 +1,9 @@ +namespace Wino.Mail.WinUI.Views.Calendar; + +public sealed partial class CalendarNotificationSettingsPage : Wino.Mail.WinUI.Views.Abstract.CalendarNotificationSettingsPageAbstract +{ + public CalendarNotificationSettingsPage() + { + InitializeComponent(); + } +} diff --git a/Wino.Mail.WinUI/Views/Calendar/CalendarPreferenceSettingsPage.xaml b/Wino.Mail.WinUI/Views/Calendar/CalendarPreferenceSettingsPage.xaml new file mode 100644 index 00000000..965c0bc0 --- /dev/null +++ b/Wino.Mail.WinUI/Views/Calendar/CalendarPreferenceSettingsPage.xaml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail.WinUI/Views/Calendar/CalendarPreferenceSettingsPage.xaml.cs b/Wino.Mail.WinUI/Views/Calendar/CalendarPreferenceSettingsPage.xaml.cs new file mode 100644 index 00000000..196c0fb9 --- /dev/null +++ b/Wino.Mail.WinUI/Views/Calendar/CalendarPreferenceSettingsPage.xaml.cs @@ -0,0 +1,9 @@ +namespace Wino.Mail.WinUI.Views.Calendar; + +public sealed partial class CalendarPreferenceSettingsPage : Wino.Mail.WinUI.Views.Abstract.CalendarPreferenceSettingsPageAbstract +{ + public CalendarPreferenceSettingsPage() + { + InitializeComponent(); + } +} diff --git a/Wino.Mail.WinUI/Views/Calendar/CalendarRenderingSettingsPage.xaml b/Wino.Mail.WinUI/Views/Calendar/CalendarRenderingSettingsPage.xaml new file mode 100644 index 00000000..2692c0e1 --- /dev/null +++ b/Wino.Mail.WinUI/Views/Calendar/CalendarRenderingSettingsPage.xaml @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail.WinUI/Views/Calendar/CalendarRenderingSettingsPage.xaml.cs b/Wino.Mail.WinUI/Views/Calendar/CalendarRenderingSettingsPage.xaml.cs new file mode 100644 index 00000000..6cad889f --- /dev/null +++ b/Wino.Mail.WinUI/Views/Calendar/CalendarRenderingSettingsPage.xaml.cs @@ -0,0 +1,9 @@ +namespace Wino.Mail.WinUI.Views.Calendar; + +public sealed partial class CalendarRenderingSettingsPage : Wino.Mail.WinUI.Views.Abstract.CalendarRenderingSettingsPageAbstract +{ + public CalendarRenderingSettingsPage() + { + InitializeComponent(); + } +} diff --git a/Wino.Mail.WinUI/Views/Calendar/CalendarSettingsPage.xaml b/Wino.Mail.WinUI/Views/Calendar/CalendarSettingsPage.xaml deleted file mode 100644 index 17b3c7f1..00000000 --- a/Wino.Mail.WinUI/Views/Calendar/CalendarSettingsPage.xaml +++ /dev/null @@ -1,338 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Wino.Mail.WinUI/Views/Calendar/CalendarSettingsPage.xaml.cs b/Wino.Mail.WinUI/Views/Calendar/CalendarSettingsPage.xaml.cs deleted file mode 100644 index a6adb6e7..00000000 --- a/Wino.Mail.WinUI/Views/Calendar/CalendarSettingsPage.xaml.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Wino.Mail.WinUI.Views.Abstract; - -namespace Wino.Mail.WinUI.Views.Calendar; - -public sealed partial class CalendarSettingsPage : CalendarSettingsPageAbstract -{ - public CalendarSettingsPage() - { - InitializeComponent(); - } -} diff --git a/Wino.Mail.WinUI/Views/Settings/AppPreferencesPage.xaml b/Wino.Mail.WinUI/Views/Settings/AppPreferencesPage.xaml index c800df60..0a42de2b 100644 --- a/Wino.Mail.WinUI/Views/Settings/AppPreferencesPage.xaml +++ b/Wino.Mail.WinUI/Views/Settings/AppPreferencesPage.xaml @@ -8,10 +8,24 @@ xmlns:domain="using:Wino.Core.Domain" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:muxc="using:Microsoft.UI.Xaml.Controls" + xmlns:translations="using:Wino.Core.Domain.Models.Translations" mc:Ignorable="d"> + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Wino.Mail.WinUI/Views/Settings/LanguageTimePage.xaml.cs b/Wino.Mail.WinUI/Views/Settings/LanguageTimePage.xaml.cs deleted file mode 100644 index fe036e44..00000000 --- a/Wino.Mail.WinUI/Views/Settings/LanguageTimePage.xaml.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Wino.Views.Abstract; - -namespace Wino.Views.Settings; - -public sealed partial class LanguageTimePage : LanguageTimePageAbstract -{ - public LanguageTimePage() - { - this.InitializeComponent(); - } - - public override void OnLanguageChanged() - { - base.OnLanguageChanged(); - - Bindings.Update(); - } -} diff --git a/Wino.Mail.WinUI/Views/Settings/MessageListPage.xaml b/Wino.Mail.WinUI/Views/Settings/MessageListPage.xaml index e4c6e5d0..dac301a9 100644 --- a/Wino.Mail.WinUI/Views/Settings/MessageListPage.xaml +++ b/Wino.Mail.WinUI/Views/Settings/MessageListPage.xaml @@ -26,7 +26,10 @@ - + diff --git a/Wino.Mail.WinUI/Views/Settings/ReadComposePanePage.xaml b/Wino.Mail.WinUI/Views/Settings/ReadComposePanePage.xaml index b7c4a154..1d07a04d 100644 --- a/Wino.Mail.WinUI/Views/Settings/ReadComposePanePage.xaml +++ b/Wino.Mail.WinUI/Views/Settings/ReadComposePanePage.xaml @@ -18,7 +18,6 @@ - - - - - - - - - + + + + + + - - - - - + + - - - - - - - - diff --git a/Wino.Mail.WinUI/Wino.Mail.WinUI.csproj b/Wino.Mail.WinUI/Wino.Mail.WinUI.csproj index cc30a0b5..40293564 100644 --- a/Wino.Mail.WinUI/Wino.Mail.WinUI.csproj +++ b/Wino.Mail.WinUI/Wino.Mail.WinUI.csproj @@ -123,7 +123,6 @@ - @@ -320,9 +319,6 @@ Designer - - Designer - Designer @@ -357,11 +353,6 @@ MSBuild:Compile - - - MSBuild:Compile - - MSBuild:Compile diff --git a/Wino.Services/WinoAccountApiClient.cs b/Wino.Services/WinoAccountApiClient.cs index bc47a846..77a3faba 100644 --- a/Wino.Services/WinoAccountApiClient.cs +++ b/Wino.Services/WinoAccountApiClient.cs @@ -28,8 +28,8 @@ public sealed class WinoAccountApiClient : IWinoAccountApiClient, IDisposable private readonly SemaphoreSlim _tokenRefreshLock = new(1, 1); private readonly bool _ownsHttpClient; - // private const string ApiUrl = "https://localhost:7204/"; - private const string ApiUrl = "https://api.winomail.app/"; + private const string ApiUrl = "https://localhost:7204/"; + // private const string ApiUrl = "https://api.winomail.app/"; public WinoAccountApiClient(IDatabaseService databaseService, HttpClient? httpClient = null) {