Calendar page and shell improvements to support navigations. Enabled page caching.

This commit is contained in:
Burak Kaan Köse
2024-12-28 16:39:43 +01:00
parent fbc3ca4517
commit 6e3fcf363a
14 changed files with 227 additions and 153 deletions

View File

@@ -21,9 +21,7 @@ namespace Wino.Calendar.ViewModels
public partial class AppShellViewModel : CalendarBaseViewModel, public partial class AppShellViewModel : CalendarBaseViewModel,
IRecipient<VisibleDateRangeChangedMessage>, IRecipient<VisibleDateRangeChangedMessage>,
IRecipient<CalendarEnableStatusChangedMessage>, IRecipient<CalendarEnableStatusChangedMessage>,
IRecipient<CalendarInitializedMessage>, IRecipient<NavigateManageAccountsRequested>
IRecipient<NavigateManageAccountsRequested>,
IRecipient<GoToCalendarDayMessage>
{ {
public event EventHandler<CalendarDisplayType> DisplayTypeChanged; public event EventHandler<CalendarDisplayType> DisplayTypeChanged;
public IPreferencesService PreferencesService { get; } public IPreferencesService PreferencesService { get; }
@@ -61,13 +59,10 @@ namespace Wino.Calendar.ViewModels
[ObservableProperty] [ObservableProperty]
private int _selectedDateNavigationHeaderIndex; private int _selectedDateNavigationHeaderIndex;
private readonly IAccountService _accountService;
public bool IsVerticalCalendar => StatePersistenceService.CalendarDisplayType == CalendarDisplayType.Month; public bool IsVerticalCalendar => StatePersistenceService.CalendarDisplayType == CalendarDisplayType.Month;
public AppShellViewModel(IPreferencesService preferencesService, public AppShellViewModel(IPreferencesService preferencesService,
IStatePersistanceService statePersistanceService, IStatePersistanceService statePersistanceService,
IAccountService accountService,
INavigationService navigationService, INavigationService navigationService,
IWinoServerConnectionManager serverConnectionManager) IWinoServerConnectionManager serverConnectionManager)
{ {
@@ -76,7 +71,6 @@ namespace Wino.Calendar.ViewModels
PreferencesService = preferencesService; PreferencesService = preferencesService;
StatePersistenceService = statePersistanceService; StatePersistenceService = statePersistanceService;
_accountService = accountService;
StatePersistenceService.StatePropertyChanged += PrefefencesChanged; StatePersistenceService.StatePropertyChanged += PrefefencesChanged;
} }
@@ -99,12 +93,32 @@ namespace Wino.Calendar.ViewModels
UpdateDateNavigationHeaderItems(); UpdateDateNavigationHeaderItems();
} }
private void ForceNavigateCalendarDate()
{
if (SelectedMenuItemIndex == -1)
{
var args = new CalendarPageNavigationArgs()
{
NavigationDate = _navigationDate ?? DateTime.Now.Date
};
// Already on calendar. Just navigate.
NavigationService.Navigate(WinoPage.CalendarPage, args);
_navigationDate = null;
}
else
{
SelectedMenuItemIndex = -1;
}
}
partial void OnSelectedMenuItemIndexChanged(int oldValue, int newValue) partial void OnSelectedMenuItemIndexChanged(int oldValue, int newValue)
{ {
switch (newValue) switch (newValue)
{ {
case -1: case -1:
NavigationService.Navigate(WinoPage.CalendarPage); ForceNavigateCalendarDate();
break; break;
case 0: case 0:
NavigationService.Navigate(WinoPage.AccountManagementPage); NavigationService.Navigate(WinoPage.AccountManagementPage);
@@ -162,20 +176,24 @@ namespace Wino.Calendar.ViewModels
return DateTime.Today.Date; return DateTime.Today.Date;
} }
private DateTime? _navigationDate;
public override void OnPageLoaded() public override void OnPageLoaded()
{ {
base.OnPageLoaded(); base.OnPageLoaded();
NavigationService.Navigate(WinoPage.CalendarPage, new CalendarPageNavigationArgs() TodayClicked();
{
RequestDefaultNavigation = true
});
} }
#region Commands #region Commands
[RelayCommand] [RelayCommand]
private void TodayClicked() => Messenger.Send(new GoToCalendarDayMessage(DateTime.Now.Date)); private void TodayClicked()
{
_navigationDate = DateTime.Now.Date;
ForceNavigateCalendarDate();
}
[RelayCommand] [RelayCommand]
public void ManageAccounts() => NavigationService.Navigate(WinoPage.AccountManagementPage); public void ManageAccounts() => NavigationService.Navigate(WinoPage.AccountManagementPage);
@@ -184,8 +202,12 @@ namespace Wino.Calendar.ViewModels
private Task ReconnectServerAsync() => ServerConnectionManager.ConnectAsync(); private Task ReconnectServerAsync() => ServerConnectionManager.ConnectAsync();
[RelayCommand] [RelayCommand]
private void DateClicked(CalendarViewDayClickedEventArgs clickedDate) private void DateClicked(CalendarViewDayClickedEventArgs clickedDateArgs)
=> Messenger.Send(new CalendarInitializeMessage(clickedDate.ClickedDate, CalendarInitInitiative.User)); {
_navigationDate = clickedDateArgs.ClickedDate;
ForceNavigateCalendarDate();
}
#endregion #endregion
@@ -238,11 +260,8 @@ namespace Wino.Calendar.ViewModels
public async void Receive(CalendarEnableStatusChangedMessage message) public async void Receive(CalendarEnableStatusChangedMessage message)
=> await ExecuteUIThread(() => IsCalendarEnabled = message.IsEnabled); => await ExecuteUIThread(() => IsCalendarEnabled = message.IsEnabled);
// Calendar page is loaded and calendar is ready to recieve render requests.
public void Receive(CalendarInitializedMessage message) => Messenger.Send(new GoToCalendarDayMessage(DateTime.Now.Date));
public void Receive(NavigateManageAccountsRequested message) => SelectedMenuItemIndex = 1; public void Receive(NavigateManageAccountsRequested message) => SelectedMenuItemIndex = 1;
public void Receive(GoToCalendarDayMessage message) => SelectedMenuItemIndex = -1; //public void Receive(GoToCalendarDayMessage message) => SelectedMenuItemIndex = -1;
} }
} }

View File

@@ -12,14 +12,13 @@ using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Calendar; using Wino.Core.Domain.Models.Calendar;
using Wino.Core.Domain.Models.Calendar.CalendarTypeStrategies; using Wino.Core.Domain.Models.Calendar.CalendarTypeStrategies;
using Wino.Core.Domain.Models.Navigation;
using Wino.Core.ViewModels; using Wino.Core.ViewModels;
using Wino.Messaging.Client.Calendar; using Wino.Messaging.Client.Calendar;
namespace Wino.Calendar.ViewModels namespace Wino.Calendar.ViewModels
{ {
public partial class CalendarPageViewModel : CalendarBaseViewModel, public partial class CalendarPageViewModel : CalendarBaseViewModel,
IRecipient<CalendarInitializeMessage> IRecipient<LoadCalendarMessage>
{ {
[ObservableProperty] [ObservableProperty]
private ObservableRangeCollection<DayRangeRenderModel> _dayRanges = []; private ObservableRangeCollection<DayRangeRenderModel> _dayRanges = [];
@@ -56,13 +55,6 @@ namespace Wino.Calendar.ViewModels
_currentSettings = _preferencesService.GetCurrentCalendarSettings(); _currentSettings = _preferencesService.GetCurrentCalendarSettings();
} }
public override void OnNavigatedTo(NavigationMode mode, object parameters)
{
base.OnNavigatedTo(mode, parameters);
Messenger.Send(new CalendarInitializedMessage());
}
// TODO: Replace when calendar settings are updated. // TODO: Replace when calendar settings are updated.
// Should be a field ideally. // Should be a field ideally.
private BaseCalendarTypeDrawingStrategy GetDrawingStrategy(CalendarDisplayType displayType) private BaseCalendarTypeDrawingStrategy GetDrawingStrategy(CalendarDisplayType displayType)
@@ -77,7 +69,7 @@ namespace Wino.Calendar.ViewModels
partial void OnIsCalendarEnabledChanging(bool oldValue, bool newValue) => Messenger.Send(new CalendarEnableStatusChangedMessage(newValue)); partial void OnIsCalendarEnabledChanging(bool oldValue, bool newValue) => Messenger.Send(new CalendarEnableStatusChangedMessage(newValue));
private bool ShouldResetDayRanges(CalendarInitializeMessage message) private bool ShouldResetDayRanges(LoadCalendarMessage message)
{ {
// Never reset if the initiative is from the app. // Never reset if the initiative is from the app.
if (message.CalendarInitInitiative == CalendarInitInitiative.App) return false; if (message.CalendarInitInitiative == CalendarInitInitiative.App) return false;
@@ -86,13 +78,17 @@ namespace Wino.Calendar.ViewModels
// 2. Day display count is different. // 2. Day display count is different.
// 3. Display date is not in the visible range. // 3. Display date is not in the visible range.
var loadedRange = GetLoadedDateRange();
if (loadedRange == null) return false;
return return
(_currentDisplayType != StatePersistanceService.CalendarDisplayType || (_currentDisplayType != StatePersistanceService.CalendarDisplayType ||
_displayDayCount != StatePersistanceService.DayDisplayCount || _displayDayCount != StatePersistanceService.DayDisplayCount ||
(DayRanges != null && !DayRanges.Select(a => a.CalendarRenderOptions).Any(b => b.DateRange.IsInRange(message.DisplayDate)))); !(message.DisplayDate >= loadedRange.StartDate && message.DisplayDate <= loadedRange.EndDate));
} }
public async void Receive(CalendarInitializeMessage message) public async void Receive(LoadCalendarMessage message)
{ {
await _calendarLoadingSemaphore.WaitAsync(); await _calendarLoadingSemaphore.WaitAsync();
@@ -132,6 +128,16 @@ namespace Wino.Calendar.ViewModels
} }
} }
private DateRange GetLoadedDateRange()
{
if (DayRanges.Count == 0) return null;
var minimumLoadedDate = DayRanges[0].CalendarRenderOptions.DateRange.StartDate;
var maximumLoadedDate = DayRanges[DayRanges.Count - 1].CalendarRenderOptions.DateRange.EndDate;
return new DateRange(minimumLoadedDate, maximumLoadedDate);
}
private async Task RenderDatesAsync(CalendarInitInitiative calendarInitInitiative, private async Task RenderDatesAsync(CalendarInitInitiative calendarInitInitiative,
DateTime? loadingDisplayDate = null, DateTime? loadingDisplayDate = null,
CalendarLoadDirection calendarLoadDirection = CalendarLoadDirection.Replace) CalendarLoadDirection calendarLoadDirection = CalendarLoadDirection.Replace)
@@ -165,10 +171,7 @@ namespace Wino.Calendar.ViewModels
} }
else else
{ {
var minimumLoadedDate = DayRanges[0].CalendarRenderOptions.DateRange.StartDate; var initializedDateRange = GetLoadedDateRange();
var maximumLoadedDate = DayRanges[DayRanges.Count - 1].CalendarRenderOptions.DateRange.EndDate;
var currentInitializedDateRange = new DateRange(minimumLoadedDate, maximumLoadedDate);
// App is trying to load. // App is trying to load.
// This should be based on direction. We'll load the next or previous range. // This should be based on direction. We'll load the next or previous range.
@@ -176,11 +179,11 @@ namespace Wino.Calendar.ViewModels
if (calendarLoadDirection == CalendarLoadDirection.Previous) if (calendarLoadDirection == CalendarLoadDirection.Previous)
{ {
flipLoadRange = strategy.GetPreviousDateRange(currentInitializedDateRange, StatePersistanceService.DayDisplayCount); flipLoadRange = strategy.GetPreviousDateRange(initializedDateRange, StatePersistanceService.DayDisplayCount);
} }
else else
{ {
flipLoadRange = strategy.GetNextDateRange(currentInitializedDateRange, StatePersistanceService.DayDisplayCount); flipLoadRange = strategy.GetNextDateRange(initializedDateRange, StatePersistanceService.DayDisplayCount);
} }
} }
@@ -310,7 +313,7 @@ namespace Wino.Calendar.ViewModels
} }
} }
private bool ShouldScrollToItem(CalendarInitializeMessage message) private bool ShouldScrollToItem(LoadCalendarMessage message)
{ {
// Never scroll if the initiative is from the app. // Never scroll if the initiative is from the app.
if (message.CalendarInitInitiative == CalendarInitInitiative.App) return false; if (message.CalendarInitInitiative == CalendarInitInitiative.App) return false;
@@ -318,36 +321,34 @@ namespace Wino.Calendar.ViewModels
// Nothing to scroll. // Nothing to scroll.
if (DayRanges.Count == 0) return false; if (DayRanges.Count == 0) return false;
var minimumLoadedDate = DayRanges[0].CalendarRenderOptions.DateRange.StartDate; var initializedDateRange = GetLoadedDateRange();
var maximumLoadedDate = DayRanges[DayRanges.Count - 1].CalendarRenderOptions.DateRange.EndDate;
var selectedDate = message.DisplayDate; var selectedDate = message.DisplayDate;
return selectedDate >= minimumLoadedDate && selectedDate <= maximumLoadedDate; return selectedDate >= initializedDateRange.StartDate && selectedDate <= initializedDateRange.EndDate;
} }
partial void OnSelectedDayRangeChanged(DayRangeRenderModel value) partial void OnSelectedDayRangeChanged(DayRangeRenderModel value)
{ {
if (DayRanges.Count == 0 || SelectedDateRangeIndex < 0) return; if (DayRanges.Count == 0 || SelectedDateRangeIndex < 0) return;
if (isLoadMoreBlocked) return;
var selectedRange = DayRanges[SelectedDateRangeIndex]; var selectedRange = DayRanges[SelectedDateRangeIndex];
if (selectedRange != null) Messenger.Send(new VisibleDateRangeChangedMessage(new DateRange(selectedRange.Period.Start, selectedRange.Period.End)));
{
// Send the loading message initiated by the app.
if (SelectedDateRangeIndex == DayRanges.Count - 1)
{
// Load next, starting from the end date.
_ = LoadMoreAsync(CalendarLoadDirection.Next);
}
else if (SelectedDateRangeIndex == 0)
{
// Load previous, starting from the start date.
_ = LoadMoreAsync(CalendarLoadDirection.Previous); if (isLoadMoreBlocked) return;
}
// Send the loading message initiated by the app.
if (SelectedDateRangeIndex == DayRanges.Count - 1)
{
// Load next, starting from the end date.
_ = LoadMoreAsync(CalendarLoadDirection.Next);
}
else if (SelectedDateRangeIndex == 0)
{
// Load previous, starting from the start date.
_ = LoadMoreAsync(CalendarLoadDirection.Previous);
} }
} }

View File

@@ -75,7 +75,7 @@ namespace Wino.Calendar
private void RegisterViewModels(IServiceCollection services) private void RegisterViewModels(IServiceCollection services)
{ {
services.AddSingleton(typeof(AppShellViewModel)); services.AddSingleton(typeof(AppShellViewModel));
services.AddTransient(typeof(CalendarPageViewModel)); services.AddSingleton(typeof(CalendarPageViewModel));
services.AddTransient(typeof(CalendarSettingsPageViewModel)); services.AddTransient(typeof(CalendarSettingsPageViewModel));
services.AddTransient(typeof(AccountManagementViewModel)); services.AddTransient(typeof(AccountManagementViewModel));
} }

View File

@@ -1,11 +1,10 @@
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.Messaging; using System.Diagnostics;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Wino.Calendar.Args; using Wino.Calendar.Args;
using Wino.Core.Domain.Models.Calendar; using Wino.Core.Domain.Models.Calendar;
using Wino.Messaging.Client.Calendar;
namespace Wino.Calendar.Controls namespace Wino.Calendar.Controls
{ {
@@ -21,6 +20,7 @@ namespace Wino.Calendar.Controls
public static readonly DependencyProperty DayRangesProperty = DependencyProperty.Register(nameof(DayRanges), typeof(ObservableCollection<DayRangeRenderModel>), typeof(WinoCalendarControl), new PropertyMetadata(null)); public static readonly DependencyProperty DayRangesProperty = DependencyProperty.Register(nameof(DayRanges), typeof(ObservableCollection<DayRangeRenderModel>), typeof(WinoCalendarControl), new PropertyMetadata(null));
public static readonly DependencyProperty SelectedFlipViewIndexProperty = DependencyProperty.Register(nameof(SelectedFlipViewIndex), typeof(int), typeof(WinoCalendarControl), new PropertyMetadata(-1)); public static readonly DependencyProperty SelectedFlipViewIndexProperty = DependencyProperty.Register(nameof(SelectedFlipViewIndex), typeof(int), typeof(WinoCalendarControl), new PropertyMetadata(-1));
public static readonly DependencyProperty SelectedFlipViewDayRangeProperty = DependencyProperty.Register(nameof(SelectedFlipViewDayRange), typeof(DayRangeRenderModel), typeof(WinoCalendarControl), new PropertyMetadata(null)); public static readonly DependencyProperty SelectedFlipViewDayRangeProperty = DependencyProperty.Register(nameof(SelectedFlipViewDayRange), typeof(DayRangeRenderModel), typeof(WinoCalendarControl), new PropertyMetadata(null));
public static readonly DependencyProperty ActiveCanvasProperty = DependencyProperty.Register(nameof(ActiveCanvas), typeof(WinoDayTimelineCanvas), typeof(WinoCalendarControl), new PropertyMetadata(null, new PropertyChangedCallback(OnActiveCanvasChanged)));
public DayRangeRenderModel SelectedFlipViewDayRange public DayRangeRenderModel SelectedFlipViewDayRange
{ {
@@ -28,6 +28,12 @@ namespace Wino.Calendar.Controls
set { SetValue(SelectedFlipViewDayRangeProperty, value); } set { SetValue(SelectedFlipViewDayRangeProperty, value); }
} }
public WinoDayTimelineCanvas ActiveCanvas
{
get { return (WinoDayTimelineCanvas)GetValue(ActiveCanvasProperty); }
set { SetValue(ActiveCanvasProperty, value); }
}
/// <summary> /// <summary>
/// Gets or sets the collection of day ranges to render. /// Gets or sets the collection of day ranges to render.
/// Each day range usually represents a week, but it may support other ranges. /// Each day range usually represents a week, but it may support other ranges.
@@ -46,38 +52,6 @@ namespace Wino.Calendar.Controls
#endregion #endregion
private WinoDayTimelineCanvas _activeCanvas;
public WinoDayTimelineCanvas ActiveCanvas
{
get { return _activeCanvas; }
set
{
// FlipView's timeline is changing.
// Make sure to unregister from the old one.
if (_activeCanvas != null)
{
// Dismiss any selection on the old canvas.
_activeCanvas.SelectedDateTime = null;
_activeCanvas.TimelineCellSelected -= ActiveTimelineCellSelected;
_activeCanvas.TimelineCellUnselected -= ActiveTimelineCellUnselected;
}
_activeCanvas = value;
if (_activeCanvas != null)
{
_activeCanvas.TimelineCellSelected += ActiveTimelineCellSelected;
_activeCanvas.TimelineCellUnselected += ActiveTimelineCellUnselected;
// Raise visible date range change to shell.
WeakReferenceMessenger.Default.Send(new VisibleDateRangeChangedMessage(_activeCanvas.RenderOptions.DateRange));
}
}
}
private WinoCalendarFlipView InternalFlipView; private WinoCalendarFlipView InternalFlipView;
public WinoCalendarControl() public WinoCalendarControl()
@@ -86,6 +60,59 @@ namespace Wino.Calendar.Controls
SizeChanged += CalendarSizeChanged; SizeChanged += CalendarSizeChanged;
} }
private static void OnActiveCanvasChanged(DependencyObject calendar, DependencyPropertyChangedEventArgs e)
{
if (calendar is WinoCalendarControl calendarControl)
{
if (e.OldValue is WinoDayTimelineCanvas oldCanvas)
{
// Dismiss any selection on the old canvas.
calendarControl.DeregisterCanvas(oldCanvas);
}
if (e.NewValue is WinoDayTimelineCanvas newCanvas)
{
calendarControl.RegisterCanvas(newCanvas);
}
calendarControl.ManageHighlightedDateRange();
}
}
private void ManageHighlightedDateRange()
{
if (ActiveCanvas == null)
{
SelectedFlipViewDayRange = null;
}
else
{
SelectedFlipViewDayRange = InternalFlipView.SelectedItem as DayRangeRenderModel;
}
}
private void DeregisterCanvas(WinoDayTimelineCanvas canvas)
{
if (canvas == null) return;
Debug.WriteLine("Deregister active canvas.");
canvas.SelectedDateTime = null;
canvas.TimelineCellSelected -= ActiveTimelineCellSelected;
canvas.TimelineCellUnselected -= ActiveTimelineCellUnselected;
}
private void RegisterCanvas(WinoDayTimelineCanvas canvas)
{
if (canvas == null) return;
Debug.WriteLine("Register new canvas.");
canvas.SelectedDateTime = null;
canvas.TimelineCellSelected += ActiveTimelineCellSelected;
canvas.TimelineCellUnselected += ActiveTimelineCellUnselected;
}
private void CalendarSizeChanged(object sender, SizeChangedEventArgs e) private void CalendarSizeChanged(object sender, SizeChangedEventArgs e)
{ {
if (ActiveCanvas == null) return; if (ActiveCanvas == null) return;
@@ -98,11 +125,6 @@ namespace Wino.Calendar.Controls
base.OnApplyTemplate(); base.OnApplyTemplate();
InternalFlipView = GetTemplateChild(PART_WinoFlipView) as WinoCalendarFlipView; InternalFlipView = GetTemplateChild(PART_WinoFlipView) as WinoCalendarFlipView;
// Each FlipViewItem will have 1 timeline canvas to draw hour cells in the background that supports selection of them.
// When the selection changes, we need to stop listening to the old canvas and start listening to the new one to catch events.
InternalFlipView.ActiveTimelineCanvasChanged += FlipViewsActiveTimelineCanvasChanged;
} }
private void FlipViewsActiveTimelineCanvasChanged(object sender, WinoDayTimelineCanvas e) private void FlipViewsActiveTimelineCanvasChanged(object sender, WinoDayTimelineCanvas e)

View File

@@ -2,6 +2,7 @@
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using CommunityToolkit.WinUI; using CommunityToolkit.WinUI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Collections; using Wino.Core.Domain.Collections;
using Wino.Core.Domain.Models.Calendar; using Wino.Core.Domain.Models.Calendar;
@@ -10,29 +11,42 @@ namespace Wino.Calendar.Controls
{ {
public class WinoCalendarFlipView : CustomCalendarFlipView public class WinoCalendarFlipView : CustomCalendarFlipView
{ {
public event EventHandler<WinoDayTimelineCanvas> ActiveTimelineCanvasChanged; public WinoDayTimelineCanvas ActiveCanvas
{
get { return (WinoDayTimelineCanvas)GetValue(ActiveCanvasProperty); }
set { SetValue(ActiveCanvasProperty, value); }
}
public static readonly DependencyProperty ActiveCanvasProperty = DependencyProperty.Register(nameof(ActiveCanvas), typeof(WinoDayTimelineCanvas), typeof(WinoCalendarFlipView), new PropertyMetadata(null));
public WinoCalendarFlipView() public WinoCalendarFlipView()
{ {
SelectionChanged += CalendarDisplayRangeChanged; RegisterPropertyChangedCallback(SelectedIndexProperty, new DependencyPropertyChangedCallback(OnSelectedIndexUpdated));
} }
private async void CalendarDisplayRangeChanged(object sender, SelectionChangedEventArgs e) private static void OnSelectedIndexUpdated(DependencyObject d, DependencyProperty e)
{
if (d is WinoCalendarFlipView flipView)
{
flipView.UpdateActiveCanvas();
}
}
public async void UpdateActiveCanvas()
{ {
if (SelectedIndex < 0) if (SelectedIndex < 0)
ActiveTimelineCanvasChanged?.Invoke(this, null); ActiveCanvas = null;
else else
{ {
// TODO: Refactor this mechanism by listening to PrepareContainerForItemOverride and Loaded events together. // TODO: Refactor this mechanism by listening to PrepareContainerForItemOverride and Loaded events together.
while (ContainerFromIndex(SelectedIndex) == null) while (ContainerFromIndex(SelectedIndex) == null)
{ {
await Task.Delay(250); await Task.Delay(100);
} }
if (ContainerFromIndex(SelectedIndex) is FlipViewItem flipViewItem) if (ContainerFromIndex(SelectedIndex) is FlipViewItem flipViewItem)
{ {
var canvas = flipViewItem.FindDescendant<WinoDayTimelineCanvas>(); ActiveCanvas = flipViewItem.FindDescendant<WinoDayTimelineCanvas>();
ActiveTimelineCanvasChanged?.Invoke(this, canvas);
} }
} }
} }
@@ -43,42 +57,46 @@ namespace Wino.Calendar.Controls
/// <param name="dateTime">Date to navigate.</param> /// <param name="dateTime">Date to navigate.</param>
public async void NavigateToDay(DateTime dateTime) public async void NavigateToDay(DateTime dateTime)
{ {
// Find the day range that contains the date. await Task.Yield();
var dayRange = GetItemsSource()?.FirstOrDefault(a => a.CalendarDays.Any(b => b.RepresentingDate.Date == dateTime.Date));
if (dayRange != null) await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
{ {
var navigationItemIndex = GetItemsSource().IndexOf(dayRange); // Find the day range that contains the date.
var dayRange = GetItemsSource()?.FirstOrDefault(a => a.CalendarDays.Any(b => b.RepresentingDate.Date == dateTime.Date));
if (Math.Abs(navigationItemIndex - SelectedIndex) > 4) if (dayRange != null)
{ {
// Difference between dates are high. var navigationItemIndex = GetItemsSource().IndexOf(dayRange);
// No need to animate this much, just go without animating.
SelectedIndex = navigationItemIndex; if (Math.Abs(navigationItemIndex - SelectedIndex) > 4)
}
else
{
// Until we reach the day in the flip, simulate next-prev button clicks.
// This will make sure the FlipView animations are triggered.
// Setting SelectedIndex directly doesn't trigger the animations.
while (SelectedIndex != navigationItemIndex)
{ {
if (SelectedIndex > navigationItemIndex) // Difference between dates are high.
// No need to animate this much, just go without animating.
SelectedIndex = navigationItemIndex;
}
else
{
// Until we reach the day in the flip, simulate next-prev button clicks.
// This will make sure the FlipView animations are triggered.
// Setting SelectedIndex directly doesn't trigger the animations.
while (SelectedIndex != navigationItemIndex)
{ {
GoPreviousFlip(); if (SelectedIndex > navigationItemIndex)
} {
else GoPreviousFlip();
{ }
GoNextFlip(); else
{
GoNextFlip();
}
} }
} }
} }
} });
} }
public void NavigateHour(TimeSpan hourTimeSpan) public void NavigateHour(TimeSpan hourTimeSpan)
{ {
// Total height of the FlipViewItem is the same as vertical ScrollViewer to position day headers. // Total height of the FlipViewItem is the same as vertical ScrollViewer to position day headers.

View File

@@ -109,6 +109,7 @@
x:Name="PART_WinoFlipView" x:Name="PART_WinoFlipView"
HorizontalContentAlignment="Stretch" HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
ActiveCanvas="{x:Bind ActiveCanvas, Mode=TwoWay}"
IsTabStop="False" IsTabStop="False"
ItemTemplate="{StaticResource FlipTemplate}" ItemTemplate="{StaticResource FlipTemplate}"
ItemsSource="{TemplateBinding DayRanges}" ItemsSource="{TemplateBinding DayRanges}"

View File

@@ -169,6 +169,7 @@
<calendarControls:WinoCalendarView <calendarControls:WinoCalendarView
x:Name="CalendarView" x:Name="CalendarView"
HorizontalAlignment="Center" HorizontalAlignment="Center"
DateClickedCommand="{x:Bind ViewModel.DateClickedCommand}" DateClickedCommand="{x:Bind ViewModel.DateClickedCommand}"
HighlightedDateRange="{x:Bind ViewModel.HighlightedDateRange, Mode=OneWay}" /> HighlightedDateRange="{x:Bind ViewModel.HighlightedDateRange, Mode=OneWay}" />

View File

@@ -6,8 +6,7 @@ using Wino.Messaging.Client.Calendar;
namespace Wino.Calendar.Views namespace Wino.Calendar.Views
{ {
public sealed partial class AppShell : AppShellAbstract, public sealed partial class AppShell : AppShellAbstract
IRecipient<GoToCalendarDayMessage>
{ {
private const string STATE_HorizontalCalendar = "HorizontalCalendar"; private const string STATE_HorizontalCalendar = "HorizontalCalendar";
private const string STATE_VerticalCalendar = "VerticalCalendar"; private const string STATE_VerticalCalendar = "VerticalCalendar";
@@ -45,10 +44,10 @@ namespace Wino.Calendar.Views
} }
public void Receive(GoToCalendarDayMessage message) //public void Receive(GoToCalendarDayMessage message)
{ //{
CalendarView.GoToDay(message.DateTime); // CalendarView.GoToDay(message.DateTime);
} //}
private void PreviousDateClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new GoPreviousDateRequestedMessage()); private void PreviousDateClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new GoPreviousDateRequestedMessage());

View File

@@ -2,8 +2,11 @@
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using Wino.Calendar.Args; using Wino.Calendar.Args;
using Wino.Calendar.Views.Abstract; using Wino.Calendar.Views.Abstract;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Calendar;
using Wino.Messaging.Client.Calendar; using Wino.Messaging.Client.Calendar;
namespace Wino.Calendar.Views namespace Wino.Calendar.Views
@@ -17,6 +20,7 @@ namespace Wino.Calendar.Views
public CalendarPage() public CalendarPage()
{ {
InitializeComponent(); InitializeComponent();
NavigationCacheMode = Windows.UI.Xaml.Navigation.NavigationCacheMode.Enabled;
} }
public void Receive(ScrollToDateMessage message) => CalendarControl.NavigateToDay(message.Date); public void Receive(ScrollToDateMessage message) => CalendarControl.NavigateToDay(message.Date);
@@ -25,6 +29,25 @@ namespace Wino.Calendar.Views
public void Receive(GoPreviousDateRequestedMessage message) => CalendarControl.GoPreviousRange(); public void Receive(GoPreviousDateRequestedMessage message) => CalendarControl.GoPreviousRange();
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (e.Parameter is CalendarPageNavigationArgs args)
{
if (args.RequestDefaultNavigation)
{
// Go today.
WeakReferenceMessenger.Default.Send(new LoadCalendarMessage(DateTime.Now.Date, CalendarInitInitiative.App));
}
else
{
// Go specified date.
WeakReferenceMessenger.Default.Send(new LoadCalendarMessage(args.NavigationDate, CalendarInitInitiative.User));
}
}
}
private void CellSelected(object sender, TimelineCellSelectedArgs e) private void CellSelected(object sender, TimelineCellSelectedArgs e)
{ {
selectedDateTime = e.ClickedDate; selectedDateTime = e.ClickedDate;

View File

@@ -1,4 +1,6 @@
namespace Wino.Core.Domain.Models.Calendar using System;
namespace Wino.Core.Domain.Models.Calendar
{ {
public class CalendarPageNavigationArgs public class CalendarPageNavigationArgs
{ {
@@ -6,5 +8,10 @@
/// When the app launches, automatically request the default calendar navigation options. /// When the app launches, automatically request the default calendar navigation options.
/// </summary> /// </summary>
public bool RequestDefaultNavigation { get; set; } public bool RequestDefaultNavigation { get; set; }
/// <summary>
/// Display the calendar view for the specified date.
/// </summary>
public DateTime NavigationDate { get; set; }
} }
} }

View File

@@ -11,7 +11,7 @@ namespace Wino.Core.Domain.Models.Calendar
/// </summary> /// </summary>
public class DayRangeRenderModel public class DayRangeRenderModel
{ {
ITimePeriod Period { get; } public ITimePeriod Period { get; }
public List<CalendarDayModel> CalendarDays { get; } = new List<CalendarDayModel>(); public List<CalendarDayModel> CalendarDays { get; } = new List<CalendarDayModel>();
public List<DayHeaderRenderModel> DayHeaders { get; } = new List<DayHeaderRenderModel>(); public List<DayHeaderRenderModel> DayHeaders { get; } = new List<DayHeaderRenderModel>();
public CalendarRenderOptions CalendarRenderOptions { get; } public CalendarRenderOptions CalendarRenderOptions { get; }

View File

@@ -1,7 +0,0 @@
namespace Wino.Messaging.Client.Calendar
{
/// <summary>
/// Raised when OnNavigatedTo of CalendarPage is called.
/// </summary>
public record CalendarInitializedMessage;
}

View File

@@ -1,10 +0,0 @@
using System;
namespace Wino.Messaging.Client.Calendar
{
/// <summary>
/// Raised when specific date is requested to be clicked on CalendarView.
/// </summary>
/// <param name="DateTime">Date to be navigated.</param>
public record GoToCalendarDayMessage(DateTime DateTime);
}

View File

@@ -9,5 +9,5 @@ namespace Wino.Messaging.Client.Calendar
/// <param name="DisplayType">Type of the calendar.</param> /// <param name="DisplayType">Type of the calendar.</param>
/// <param name="DisplayDate">Exact date to highlight.</param> /// <param name="DisplayDate">Exact date to highlight.</param>
/// <param name="DayDisplayCount">How many days to load with Day calendar display type.</param> /// <param name="DayDisplayCount">How many days to load with Day calendar display type.</param>
public record CalendarInitializeMessage(DateTime DisplayDate, CalendarInitInitiative CalendarInitInitiative); public record LoadCalendarMessage(DateTime DisplayDate, CalendarInitInitiative CalendarInitInitiative);
} }