Listing account calendars on the shell, some visual state updates and reacting to calendar setting changes properly.
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Wino.Calendar.ViewModels.Data;
|
||||
using Wino.Calendar.ViewModels.Interfaces;
|
||||
using Wino.Core.Domain.Collections;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
@@ -26,6 +29,7 @@ namespace Wino.Calendar.ViewModels
|
||||
public event EventHandler<CalendarDisplayType> DisplayTypeChanged;
|
||||
public IPreferencesService PreferencesService { get; }
|
||||
public IStatePersistanceService StatePersistenceService { get; }
|
||||
public IAccountCalendarStateService AccountCalendarStateService { get; }
|
||||
public INavigationService NavigationService { get; }
|
||||
public IWinoServerConnectionManager ServerConnectionManager { get; }
|
||||
|
||||
@@ -63,9 +67,16 @@ namespace Wino.Calendar.ViewModels
|
||||
|
||||
public AppShellViewModel(IPreferencesService preferencesService,
|
||||
IStatePersistanceService statePersistanceService,
|
||||
IAccountService accountService,
|
||||
ICalendarService calendarService,
|
||||
IAccountCalendarStateService accountCalendarStateService,
|
||||
INavigationService navigationService,
|
||||
IWinoServerConnectionManager serverConnectionManager)
|
||||
{
|
||||
_accountService = accountService;
|
||||
_calendarService = calendarService;
|
||||
|
||||
AccountCalendarStateService = accountCalendarStateService;
|
||||
NavigationService = navigationService;
|
||||
ServerConnectionManager = serverConnectionManager;
|
||||
PreferencesService = preferencesService;
|
||||
@@ -86,11 +97,53 @@ namespace Wino.Calendar.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
UpdateDateNavigationHeaderItems();
|
||||
|
||||
await InitializeAccountCalendarsAsync();
|
||||
}
|
||||
|
||||
private void AddGroupedAccountCalendarViewModel(GroupedAccountCalendarViewModel groupedAccountCalendarViewModel)
|
||||
{
|
||||
foreach (var calendarViewModel in groupedAccountCalendarViewModel.AccountCalendars)
|
||||
{
|
||||
calendarViewModel.CalendarSelectionStateChanged += UpdateAccountCalendarRequested;
|
||||
}
|
||||
|
||||
AccountCalendarStateService.GroupedAccountCalendars.Add(groupedAccountCalendarViewModel);
|
||||
}
|
||||
|
||||
private async void UpdateAccountCalendarRequested(object sender, AccountCalendarViewModel e)
|
||||
=> await _calendarService.UpdateAccountCalendarAsync(e.AccountCalendar).ConfigureAwait(false);
|
||||
|
||||
private async Task InitializeAccountCalendarsAsync()
|
||||
{
|
||||
await Dispatcher.ExecuteOnUIThread(() => AccountCalendarStateService.GroupedAccountCalendars.Clear());
|
||||
|
||||
var accounts = await _accountService.GetAccountsAsync().ConfigureAwait(false);
|
||||
|
||||
foreach (var account in accounts)
|
||||
{
|
||||
var accountCalendars = await _calendarService.GetAccountCalendarsAsync(account.Id).ConfigureAwait(false);
|
||||
var calendarViewModels = new List<AccountCalendarViewModel>();
|
||||
|
||||
foreach (var calendar in accountCalendars)
|
||||
{
|
||||
var calendarViewModel = new AccountCalendarViewModel(account, calendar);
|
||||
|
||||
calendarViewModels.Add(calendarViewModel);
|
||||
}
|
||||
|
||||
var groupedAccountCalendarViewModel = new GroupedAccountCalendarViewModel(account, calendarViewModels);
|
||||
|
||||
await Dispatcher.ExecuteOnUIThread(() =>
|
||||
{
|
||||
AddGroupedAccountCalendarViewModel(groupedAccountCalendarViewModel);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void ForceNavigateCalendarDate()
|
||||
@@ -177,6 +230,8 @@ namespace Wino.Calendar.ViewModels
|
||||
}
|
||||
|
||||
private DateTime? _navigationDate;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly ICalendarService _calendarService;
|
||||
|
||||
public override void OnPageLoaded()
|
||||
{
|
||||
|
||||
@@ -7,18 +7,21 @@ using System.Threading.Tasks;
|
||||
using CommunityToolkit.Diagnostics;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Wino.Calendar.ViewModels.Interfaces;
|
||||
using Wino.Core.Domain.Collections;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
using Wino.Core.Domain.Models.Calendar.CalendarTypeStrategies;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
using Wino.Core.ViewModels;
|
||||
using Wino.Messaging.Client.Calendar;
|
||||
|
||||
namespace Wino.Calendar.ViewModels
|
||||
{
|
||||
public partial class CalendarPageViewModel : CalendarBaseViewModel,
|
||||
IRecipient<LoadCalendarMessage>
|
||||
IRecipient<LoadCalendarMessage>,
|
||||
IRecipient<CalendarSettingsUpdatedMessage>
|
||||
{
|
||||
[ObservableProperty]
|
||||
private ObservableRangeCollection<DayRangeRenderModel> _dayRanges = [];
|
||||
@@ -32,9 +35,11 @@ namespace Wino.Calendar.ViewModels
|
||||
[ObservableProperty]
|
||||
private bool _isCalendarEnabled = true;
|
||||
|
||||
// Get rid of some of the items if we have too many.
|
||||
// TODO: Get rid of some of the items if we have too many.
|
||||
private const int maxDayRangeSize = 10;
|
||||
|
||||
private readonly ICalendarService _calendarService;
|
||||
private readonly IAccountCalendarStateService _accountCalendarStateService;
|
||||
private readonly IPreferencesService _preferencesService;
|
||||
|
||||
// Store latest rendered options.
|
||||
@@ -49,13 +54,14 @@ namespace Wino.Calendar.ViewModels
|
||||
|
||||
public CalendarPageViewModel(IStatePersistanceService statePersistanceService,
|
||||
ICalendarService calendarService,
|
||||
IAccountCalendarStateService accountCalendarStateService,
|
||||
IPreferencesService preferencesService)
|
||||
{
|
||||
StatePersistanceService = statePersistanceService;
|
||||
_calendarService = calendarService;
|
||||
_preferencesService = preferencesService;
|
||||
|
||||
_currentSettings = _preferencesService.GetCurrentCalendarSettings();
|
||||
_calendarService = calendarService;
|
||||
_accountCalendarStateService = accountCalendarStateService;
|
||||
_preferencesService = preferencesService;
|
||||
}
|
||||
|
||||
// TODO: Replace when calendar settings are updated.
|
||||
@@ -70,10 +76,25 @@ namespace Wino.Calendar.ViewModels
|
||||
};
|
||||
}
|
||||
|
||||
public override void OnNavigatedFrom(NavigationMode mode, object parameters)
|
||||
{
|
||||
// Do not call base method because that will unregister messenger recipient.
|
||||
// This is a singleton view model and should not be unregistered.
|
||||
}
|
||||
|
||||
public override void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
_currentSettings = _preferencesService.GetCurrentCalendarSettings();
|
||||
}
|
||||
|
||||
partial void OnIsCalendarEnabledChanging(bool oldValue, bool newValue) => Messenger.Send(new CalendarEnableStatusChangedMessage(newValue));
|
||||
|
||||
private bool ShouldResetDayRanges(LoadCalendarMessage message)
|
||||
{
|
||||
if (message.ForceRedraw) return true;
|
||||
|
||||
// Never reset if the initiative is from the app.
|
||||
if (message.CalendarInitInitiative == CalendarInitInitiative.App) return false;
|
||||
|
||||
@@ -168,14 +189,14 @@ namespace Wino.Calendar.ViewModels
|
||||
|
||||
DateRange flipLoadRange = null;
|
||||
|
||||
if (calendarInitInitiative == CalendarInitInitiative.User)
|
||||
var initializedDateRange = GetLoadedDateRange();
|
||||
|
||||
if (calendarInitInitiative == CalendarInitInitiative.User || initializedDateRange == null)
|
||||
{
|
||||
flipLoadRange = strategy.GetRenderDateRange(displayDate, StatePersistanceService.DayDisplayCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
var initializedDateRange = GetLoadedDateRange();
|
||||
|
||||
// App is trying to load.
|
||||
// This should be based on direction. We'll load the next or previous range.
|
||||
// DisplayDate is either the start or end date of the current visible range.
|
||||
@@ -214,7 +235,7 @@ namespace Wino.Calendar.ViewModels
|
||||
{
|
||||
foreach (var day in renderModel.CalendarDays)
|
||||
{
|
||||
var events = await _calendarService.GetCalendarEventsAsync(Guid.Parse("13e8e385-a1bb-4764-95b4-757901cad35a"), day.Period.Start, day.Period.End).ConfigureAwait(false);
|
||||
var events = await _calendarService.GetCalendarEventsAsync(Guid.Parse("9ead7613-dacb-4163-8d33-2e32e65008a1"), day.Period.Start, day.Period.End).ConfigureAwait(false);
|
||||
|
||||
foreach (var calendarItem in events)
|
||||
{
|
||||
@@ -343,6 +364,8 @@ namespace Wino.Calendar.ViewModels
|
||||
|
||||
var initializedDateRange = GetLoadedDateRange();
|
||||
|
||||
if (initializedDateRange == null) return false;
|
||||
|
||||
var selectedDate = message.DisplayDate;
|
||||
|
||||
return selectedDate >= initializedDateRange.StartDate && selectedDate <= initializedDateRange.EndDate;
|
||||
@@ -391,67 +414,14 @@ namespace Wino.Calendar.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
protected override async void OnCalendarEventAdded(ICalendarItem calendarItem)
|
||||
public void Receive(CalendarSettingsUpdatedMessage message)
|
||||
{
|
||||
base.OnCalendarEventAdded(calendarItem);
|
||||
_currentSettings = _preferencesService.GetCurrentCalendarSettings();
|
||||
|
||||
// Test
|
||||
//var eventDays = DayRanges.SelectMany(a => a.CalendarDays).Where(b => b.Period.Start.Date == calendarItem.StartTime.Date);
|
||||
// TODO: This might need throttling due to slider in the settings page for hour height.
|
||||
// or make sure the slider does not update on each tick but on focus lost.
|
||||
|
||||
//var beforeAllDay = new CalendarItem(calendarItem.StartTime.Date.AddHours(0), calendarItem.StartTime.Date.AddMinutes(30))
|
||||
//{
|
||||
// Title = "kj"
|
||||
//};
|
||||
|
||||
//var allday = new CalendarItem(calendarItem.StartTime.Date.AddHours(1), calendarItem.StartTime.Date.AddHours(10).AddMinutes(59))
|
||||
//{
|
||||
// Title = "All day"
|
||||
//};
|
||||
|
||||
//var test = new CalendarItem(calendarItem.StartTime.Date.AddHours(4), calendarItem.StartTime.Date.AddHours(4).AddMinutes(30))
|
||||
//{
|
||||
// Title = "test"
|
||||
//};
|
||||
|
||||
//var hour = new CalendarItem(calendarItem.StartTime.Date.AddHours(7), calendarItem.StartTime.Date.AddHours(8))
|
||||
//{
|
||||
// Title = "1 h"
|
||||
//};
|
||||
|
||||
//var hourandhalf = new CalendarItem(calendarItem.StartTime.Date.AddHours(7), calendarItem.StartTime.Date.AddHours(8).AddMinutes(30))
|
||||
//{
|
||||
// Title = "1.5 h"
|
||||
//};
|
||||
//var halfhour1 = new CalendarItem(calendarItem.StartTime.Date.AddHours(7), calendarItem.StartTime.Date.AddHours(7).AddMinutes(30))
|
||||
//{
|
||||
// Title = "30 min"
|
||||
//};
|
||||
|
||||
//var halfhour2 = new CalendarItem(calendarItem.StartTime.Date.AddHours(7).AddMinutes(30), calendarItem.StartTime.Date.AddHours(8))
|
||||
//{
|
||||
// Title = "30 min"
|
||||
//};
|
||||
//var halfhour3 = new CalendarItem(calendarItem.StartTime.Date.AddHours(8), calendarItem.StartTime.Date.AddHours(8).AddMinutes(30))
|
||||
//{
|
||||
// Title = "30 min"
|
||||
//};
|
||||
|
||||
//foreach (var day in eventDays)
|
||||
//{
|
||||
// await ExecuteUIThread(() =>
|
||||
// {
|
||||
// day.Events.Add(beforeAllDay);
|
||||
// day.Events.Add(allday);
|
||||
// day.Events.Add(hourandhalf);
|
||||
// day.Events.Add(hour);
|
||||
// day.Events.Add(halfhour1);
|
||||
// day.Events.Add(halfhour2);
|
||||
// day.Events.Add(halfhour3);
|
||||
// day.Events.Add(test);
|
||||
// });
|
||||
//}
|
||||
|
||||
//return;
|
||||
Messenger.Send(new LoadCalendarMessage(DateTime.UtcNow.Date, CalendarInitInitiative.App, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
75
Wino.Calendar.ViewModels/Data/AccountCalendarViewModel.cs
Normal file
75
Wino.Calendar.ViewModels/Data/AccountCalendarViewModel.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Wino.Core.Domain.Entities.Calendar;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Calendar.ViewModels.Data
|
||||
{
|
||||
public partial class AccountCalendarViewModel : ObservableObject, IAccountCalendar
|
||||
{
|
||||
public event EventHandler<AccountCalendarViewModel> CalendarSelectionStateChanged;
|
||||
|
||||
public MailAccount Account { get; }
|
||||
public AccountCalendar AccountCalendar { get; }
|
||||
|
||||
public AccountCalendarViewModel(MailAccount account, AccountCalendar accountCalendar)
|
||||
{
|
||||
Account = account;
|
||||
AccountCalendar = accountCalendar;
|
||||
|
||||
IsChecked = accountCalendar.IsExtended;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isChecked;
|
||||
|
||||
partial void OnIsCheckedChanged(bool value)
|
||||
{
|
||||
IsExtended = value;
|
||||
CalendarSelectionStateChanged?.Invoke(this, this);
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => AccountCalendar.Name;
|
||||
set => SetProperty(AccountCalendar.Name, value, AccountCalendar, (u, n) => u.Name = n);
|
||||
}
|
||||
|
||||
public string TextColorHex
|
||||
{
|
||||
get => AccountCalendar.TextColorHex;
|
||||
set => SetProperty(AccountCalendar.TextColorHex, value, AccountCalendar, (u, t) => u.TextColorHex = t);
|
||||
}
|
||||
|
||||
public string BackgroundColorHex
|
||||
{
|
||||
get => AccountCalendar.BackgroundColorHex;
|
||||
set => SetProperty(AccountCalendar.BackgroundColorHex, value, AccountCalendar, (u, b) => u.BackgroundColorHex = b);
|
||||
}
|
||||
|
||||
public bool IsExtended
|
||||
{
|
||||
get => AccountCalendar.IsExtended;
|
||||
set => SetProperty(AccountCalendar.IsExtended, value, AccountCalendar, (u, i) => u.IsExtended = i);
|
||||
}
|
||||
|
||||
public bool IsPrimary
|
||||
{
|
||||
get => AccountCalendar.IsPrimary;
|
||||
set => SetProperty(AccountCalendar.IsPrimary, value, AccountCalendar, (u, i) => u.IsPrimary = i);
|
||||
}
|
||||
|
||||
public Guid AccountId
|
||||
{
|
||||
get => AccountCalendar.AccountId;
|
||||
set => SetProperty(AccountCalendar.AccountId, value, AccountCalendar, (u, a) => u.AccountId = a);
|
||||
}
|
||||
|
||||
public string RemoteCalendarId
|
||||
{
|
||||
get => AccountCalendar.RemoteCalendarId;
|
||||
set => SetProperty(AccountCalendar.RemoteCalendarId, value, AccountCalendar, (u, r) => u.RemoteCalendarId = r);
|
||||
}
|
||||
}
|
||||
}
|
||||
116
Wino.Calendar.ViewModels/Data/GroupedAccountCalendarViewModel.cs
Normal file
116
Wino.Calendar.ViewModels/Data/GroupedAccountCalendarViewModel.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
|
||||
namespace Wino.Calendar.ViewModels.Data
|
||||
{
|
||||
public partial class GroupedAccountCalendarViewModel : ObservableObject
|
||||
{
|
||||
public MailAccount Account { get; }
|
||||
public ObservableCollection<AccountCalendarViewModel> AccountCalendars { get; }
|
||||
|
||||
public GroupedAccountCalendarViewModel(MailAccount account, IEnumerable<AccountCalendarViewModel> calendarViewModels)
|
||||
{
|
||||
Account = account;
|
||||
AccountCalendars = new ObservableCollection<AccountCalendarViewModel>(calendarViewModels);
|
||||
|
||||
ManageIsCheckedState();
|
||||
|
||||
foreach (var calendarViewModel in calendarViewModels)
|
||||
{
|
||||
calendarViewModel.PropertyChanged += CalendarPropertyChanged;
|
||||
}
|
||||
|
||||
AccountCalendars.CollectionChanged += CalendarListUpdated;
|
||||
}
|
||||
|
||||
private void CalendarListUpdated(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (e.Action == NotifyCollectionChangedAction.Add)
|
||||
{
|
||||
foreach (AccountCalendarViewModel calendar in e.NewItems)
|
||||
{
|
||||
calendar.PropertyChanged += CalendarPropertyChanged;
|
||||
}
|
||||
}
|
||||
else if (e.Action == NotifyCollectionChangedAction.Remove)
|
||||
{
|
||||
foreach (AccountCalendarViewModel calendar in e.OldItems)
|
||||
{
|
||||
calendar.PropertyChanged -= CalendarPropertyChanged;
|
||||
}
|
||||
}
|
||||
else if (e.Action == NotifyCollectionChangedAction.Reset)
|
||||
{
|
||||
foreach (AccountCalendarViewModel calendar in e.OldItems)
|
||||
{
|
||||
calendar.PropertyChanged -= CalendarPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CalendarPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (sender is AccountCalendarViewModel viewModel)
|
||||
{
|
||||
if (e.PropertyName == nameof(AccountCalendarViewModel.IsChecked))
|
||||
{
|
||||
ManageIsCheckedState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isExpanded = true;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool? isCheckedState = true;
|
||||
|
||||
private bool _isExternalPropChangeBlocked = false;
|
||||
|
||||
private void ManageIsCheckedState()
|
||||
{
|
||||
_isExternalPropChangeBlocked = true;
|
||||
|
||||
if (AccountCalendars.All(c => c.IsChecked))
|
||||
{
|
||||
IsCheckedState = true;
|
||||
}
|
||||
else if (AccountCalendars.All(c => !c.IsChecked))
|
||||
{
|
||||
IsCheckedState = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsCheckedState = null;
|
||||
}
|
||||
|
||||
_isExternalPropChangeBlocked = false;
|
||||
}
|
||||
|
||||
partial void OnIsCheckedStateChanged(bool? newValue)
|
||||
{
|
||||
if (_isExternalPropChangeBlocked) return;
|
||||
|
||||
if (newValue == null)
|
||||
{
|
||||
// Only primary calendars must be checked.
|
||||
|
||||
foreach (var calendar in AccountCalendars)
|
||||
{
|
||||
calendar.IsChecked = calendar.IsPrimary;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var calendar in AccountCalendars)
|
||||
{
|
||||
calendar.IsChecked = newValue.GetValueOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using Wino.Calendar.ViewModels.Data;
|
||||
|
||||
namespace Wino.Calendar.ViewModels.Interfaces
|
||||
{
|
||||
public interface IAccountCalendarStateService
|
||||
{
|
||||
ObservableCollection<GroupedAccountCalendarViewModel> GroupedAccountCalendars { get; }
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ using Wino.Activation;
|
||||
using Wino.Calendar.Activation;
|
||||
using Wino.Calendar.Services;
|
||||
using Wino.Calendar.ViewModels;
|
||||
using Wino.Calendar.ViewModels.Interfaces;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Exceptions;
|
||||
@@ -71,6 +72,7 @@ namespace Wino.Calendar
|
||||
services.AddTransient<ISettingsBuilderService, SettingsBuilderService>();
|
||||
services.AddTransient<IProviderService, ProviderService>();
|
||||
services.AddSingleton<IAuthenticatorConfig, CalendarAuthenticatorConfig>();
|
||||
services.AddSingleton<IAccountCalendarStateService, AccountCalendarStateService>();
|
||||
}
|
||||
|
||||
private void RegisterViewModels(IServiceCollection services)
|
||||
|
||||
13
Wino.Calendar/Services/AccountCalendarStateService.cs
Normal file
13
Wino.Calendar/Services/AccountCalendarStateService.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Wino.Calendar.ViewModels.Data;
|
||||
using Wino.Calendar.ViewModels.Interfaces;
|
||||
|
||||
namespace Wino.Calendar.Services
|
||||
{
|
||||
public partial class AccountCalendarStateService : ObservableObject, IAccountCalendarStateService
|
||||
{
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<GroupedAccountCalendarViewModel> _groupedAccountCalendars = new ObservableCollection<GroupedAccountCalendarViewModel>();
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
xmlns:coreControls="using:Wino.Core.UWP.Controls"
|
||||
xmlns:coreSelectors="using:Wino.Core.UWP.Selectors"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:data="using:Wino.Calendar.ViewModels.Data"
|
||||
xmlns:domain="using:Wino.Core.Domain"
|
||||
xmlns:helpers="using:Wino.Helpers"
|
||||
xmlns:local="using:Wino.Calendar.Views"
|
||||
@@ -174,20 +175,87 @@
|
||||
HighlightedDateRange="{x:Bind ViewModel.HighlightedDateRange, Mode=OneWay}"
|
||||
TodayBackgroundColor="{ThemeResource SystemAccentColor}" />
|
||||
|
||||
<ScrollViewer
|
||||
<!-- Account Calendars Host -->
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
HorizontalScrollMode="Disabled"
|
||||
VerticalScrollBarVisibility="Hidden">
|
||||
<Grid>
|
||||
ItemsSource="{x:Bind ViewModel.AccountCalendarStateService.GroupedAccountCalendars}"
|
||||
SelectionMode="None">
|
||||
<ListView.Header>
|
||||
<TextBlock
|
||||
Margin="20,12,12,12"
|
||||
FontSize="16"
|
||||
Text="Calendars" />
|
||||
</ListView.Header>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="data:GroupedAccountCalendarViewModel">
|
||||
<muxc:Expander
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
IsExpanded="{x:Bind IsExpanded, Mode=TwoWay}">
|
||||
<muxc:Expander.Header>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<CheckBox
|
||||
Width="26"
|
||||
MinWidth="0"
|
||||
IsChecked="{x:Bind IsCheckedState, Mode=TwoWay}"
|
||||
IsThreeState="True" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
TextWrapping="Wrap">
|
||||
<Run FontWeight="SemiBold" Text="{x:Bind Account.Name}" />
|
||||
<Run FontSize="12" Text="(" /><Run FontSize="12" Text="{x:Bind Account.Address}" /><Run FontSize="12" Text=")" />
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</muxc:Expander.Header>
|
||||
<muxc:Expander.Content>
|
||||
<ItemsControl ItemsSource="{x:Bind AccountCalendars}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="data:AccountCalendarViewModel">
|
||||
<CheckBox
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
IsChecked="{x:Bind IsChecked, Mode=TwoWay}">
|
||||
<Border
|
||||
Margin="0,0,0,4"
|
||||
Padding="4,2,4,2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Background="{x:Bind helpers:XamlHelpers.GetSolidColorBrushFromHex(BackgroundColorHex), Mode=OneWay}"
|
||||
CornerRadius="3">
|
||||
<TextBlock
|
||||
FontSize="14"
|
||||
Foreground="{x:Bind helpers:XamlHelpers.GetSolidColorBrushFromHex(TextColorHex), Mode=OneWay}"
|
||||
Text="{x:Bind Name, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</Border>
|
||||
</CheckBox>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</muxc:Expander.Content>
|
||||
</muxc:Expander>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
<ListView.Footer>
|
||||
<Button
|
||||
Margin="14,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Command="{x:Bind ViewModel.SyncCommand}"
|
||||
Content="Test Sync" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</ListView.Footer>
|
||||
</ListView>
|
||||
|
||||
<!-- Menu Items -->
|
||||
<ListView
|
||||
Grid.Row="2"
|
||||
ItemTemplateSelector="{StaticResource NavigationMenuTemplateSelector}"
|
||||
@@ -219,6 +287,7 @@
|
||||
</ListViewItem>
|
||||
</ListView.Items>
|
||||
</ListView>
|
||||
|
||||
<!--<CommandBar Grid.Row="2" DefaultLabelPosition="Right">
|
||||
<AppBarButton Label="Manage Accounts">
|
||||
<AppBarButton.Icon>
|
||||
@@ -270,11 +339,15 @@
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="1200" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="MainSplitView.IsPaneOpen" Value="True" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="SmallScreen">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="NavigationTitleStack.Visibility" Value="Collapsed" />
|
||||
<Setter Target="SearchBox.(Grid.ColumnSpan)" Value="2" />
|
||||
<Setter Target="MainSplitView.IsPaneOpen" Value="False" />
|
||||
</VisualState.Setters>
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="0" />
|
||||
|
||||
@@ -56,7 +56,6 @@
|
||||
|
||||
<TimePicker x:Name="EventTimePicker" ClockIdentifier="24HourClock" />
|
||||
|
||||
|
||||
<!-- Create events dialog -->
|
||||
<Button
|
||||
x:Name="AddEvent"
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -155,6 +155,7 @@
|
||||
</Compile>
|
||||
<Compile Include="Models\CalendarItemMeasurement.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Services\AccountCalendarStateService.cs" />
|
||||
<Compile Include="Services\CalendarAuthenticatorConfig.cs" />
|
||||
<Compile Include="Services\DialogService.cs" />
|
||||
<Compile Include="Services\NavigationService.cs" />
|
||||
@@ -367,4 +368,4 @@
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
using System;
|
||||
using SQLite;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Core.Domain.Entities.Calendar
|
||||
{
|
||||
public class AccountCalendar
|
||||
public class AccountCalendar : IAccountCalendar
|
||||
{
|
||||
[PrimaryKey]
|
||||
public Guid Id { get; set; }
|
||||
public Guid AccountId { get; set; }
|
||||
public string RemoteCalendarId { get; set; }
|
||||
public string SynchronizationDeltaToken { get; set; }
|
||||
public Guid AccountId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string ColorHex { get; set; }
|
||||
public string TimeZone { get; set; }
|
||||
public bool IsPrimary { get; set; }
|
||||
public bool IsExtended { get; set; } = true;
|
||||
public string TextColorHex { get; set; }
|
||||
public string BackgroundColorHex { get; set; }
|
||||
public string TimeZone { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
15
Wino.Core.Domain/Interfaces/IAccountCalendar.cs
Normal file
15
Wino.Core.Domain/Interfaces/IAccountCalendar.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Wino.Core.Domain.Interfaces
|
||||
{
|
||||
public interface IAccountCalendar
|
||||
{
|
||||
string Name { get; set; }
|
||||
string TextColorHex { get; set; }
|
||||
string BackgroundColorHex { get; set; }
|
||||
bool IsPrimary { get; set; }
|
||||
Guid AccountId { get; set; }
|
||||
string RemoteCalendarId { get; set; }
|
||||
bool IsExtended { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using CommunityToolkit.Diagnostics;
|
||||
using Google.Apis.Calendar.v3.Data;
|
||||
using Google.Apis.Gmail.v1.Data;
|
||||
using MimeKit;
|
||||
@@ -9,6 +10,7 @@ using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Entities.Calendar;
|
||||
using Wino.Core.Domain.Entities.Mail;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Misc;
|
||||
using Wino.Services;
|
||||
using Wino.Services.Extensions;
|
||||
|
||||
@@ -170,7 +172,7 @@ namespace Wino.Core.Extensions
|
||||
|
||||
public static AccountCalendar AsCalendar(this CalendarListEntry calendarListEntry, Guid accountId)
|
||||
{
|
||||
return new AccountCalendar()
|
||||
var calendar = new AccountCalendar()
|
||||
{
|
||||
RemoteCalendarId = calendarListEntry.Id,
|
||||
AccountId = accountId,
|
||||
@@ -179,6 +181,25 @@ namespace Wino.Core.Extensions
|
||||
TimeZone = calendarListEntry.TimeZone,
|
||||
IsPrimary = calendarListEntry.Primary.GetValueOrDefault(),
|
||||
};
|
||||
|
||||
// Optional background color.
|
||||
if (calendarListEntry.BackgroundColor != null) calendar.BackgroundColorHex = calendarListEntry.BackgroundColor;
|
||||
|
||||
if (!string.IsNullOrEmpty(calendarListEntry.ForegroundColor))
|
||||
{
|
||||
calendar.TextColorHex = calendarListEntry.ForegroundColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Calendars must have text color assigned.
|
||||
// Generate one if not provided.
|
||||
|
||||
var randomColor = RandomFlatColorGenerator.Generate();
|
||||
|
||||
calendar.TextColorHex = randomColor.ToHexString();
|
||||
}
|
||||
|
||||
return calendar;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
44
Wino.Core/Misc/RandomFlatColorGenerator.cs
Normal file
44
Wino.Core/Misc/RandomFlatColorGenerator.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace Wino.Core.Misc
|
||||
{
|
||||
public static class RandomFlatColorGenerator
|
||||
{
|
||||
public static Color Generate()
|
||||
{
|
||||
Random random = new();
|
||||
int hue = random.Next(0, 360); // Full hue range
|
||||
int saturation = 70 + random.Next(30); // High saturation (70-100%)
|
||||
int lightness = 50 + random.Next(20); // Bright colors (50-70%)
|
||||
|
||||
return FromHsl(hue, saturation, lightness);
|
||||
}
|
||||
|
||||
private static Color FromHsl(int h, int s, int l)
|
||||
{
|
||||
double hue = h / 360.0;
|
||||
double saturation = s / 100.0;
|
||||
double lightness = l / 100.0;
|
||||
|
||||
// Conversion from HSL to RGB
|
||||
var chroma = (1 - Math.Abs(2 * lightness - 1)) * saturation;
|
||||
var x = chroma * (1 - Math.Abs((hue * 6) % 2 - 1));
|
||||
var m = lightness - chroma / 2;
|
||||
|
||||
double r = 0, g = 0, b = 0;
|
||||
|
||||
if (hue < 1.0 / 6.0) { r = chroma; g = x; b = 0; }
|
||||
else if (hue < 2.0 / 6.0) { r = x; g = chroma; b = 0; }
|
||||
else if (hue < 3.0 / 6.0) { r = 0; g = chroma; b = x; }
|
||||
else if (hue < 4.0 / 6.0) { r = 0; g = x; b = chroma; }
|
||||
else if (hue < 5.0 / 6.0) { r = x; g = 0; b = chroma; }
|
||||
else { r = chroma; g = 0; b = x; }
|
||||
|
||||
return Color.FromArgb(
|
||||
(int)((r + m) * 255),
|
||||
(int)((g + m) * 255),
|
||||
(int)((b + m) * 255));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,5 +9,6 @@ namespace Wino.Messaging.Client.Calendar
|
||||
/// <param name="DisplayType">Type of the calendar.</param>
|
||||
/// <param name="DisplayDate">Exact date to highlight.</param>
|
||||
/// <param name="DayDisplayCount">How many days to load with Day calendar display type.</param>
|
||||
public record LoadCalendarMessage(DateTime DisplayDate, CalendarInitInitiative CalendarInitInitiative);
|
||||
/// <param name="ForceRedraw">Remove all days and force re-render of everything. Used when settings are updated.</param>
|
||||
public record LoadCalendarMessage(DateTime DisplayDate, CalendarInitInitiative CalendarInitInitiative, bool ForceRedraw = false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user