Outlook calendar/event syncing basics without delta. Bunch of UI updates for the calendar view.
This commit is contained in:
@@ -224,7 +224,7 @@ namespace Wino.Calendar.ViewModels
|
||||
{
|
||||
var t = new NewCalendarSynchronizationRequested(new CalendarSynchronizationOptions()
|
||||
{
|
||||
AccountId = Guid.Parse("52fae547-0740-4aa3-8d51-519bd31278ca"),
|
||||
AccountId = Guid.Parse("bd0fc1ab-168a-436d-86ce-0661c0eabaf9"),
|
||||
Type = CalendarSynchronizationType.CalendarMetadata
|
||||
}, SynchronizationSource.Client);
|
||||
|
||||
|
||||
@@ -79,8 +79,8 @@ namespace Wino.Calendar.ViewModels
|
||||
[NotifyCanExecuteChangedFor(nameof(SaveQuickEventCommand))]
|
||||
private string _eventName;
|
||||
|
||||
public DateTime QuickEventStartTime => SelectedQuickEventDate.Value.Date.Add(_currentSettings.GetTimeSpan(SelectedStartTimeString).Value);
|
||||
public DateTime QuickEventEndTime => SelectedQuickEventDate.Value.Date.Add(_currentSettings.GetTimeSpan(SelectedEndTimeString).Value);
|
||||
public DateTime QuickEventStartTime => SelectedQuickEventDate.Value.Date.Add(CurrentSettings.GetTimeSpan(SelectedStartTimeString).Value);
|
||||
public DateTime QuickEventEndTime => SelectedQuickEventDate.Value.Date.Add(CurrentSettings.GetTimeSpan(SelectedEndTimeString).Value);
|
||||
|
||||
public bool CanSaveQuickEvent => SelectedQuickEventAccountCalendar != null &&
|
||||
!string.IsNullOrWhiteSpace(EventName) &&
|
||||
@@ -90,6 +90,8 @@ namespace Wino.Calendar.ViewModels
|
||||
|
||||
#endregion
|
||||
|
||||
#region Data Initialization
|
||||
|
||||
[ObservableProperty]
|
||||
private DayRangeCollection _dayRanges = [];
|
||||
|
||||
@@ -102,6 +104,20 @@ namespace Wino.Calendar.ViewModels
|
||||
[ObservableProperty]
|
||||
private bool _isCalendarEnabled = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Details
|
||||
|
||||
public event EventHandler DetailsShowCalendarItemChanged;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(IsEventDetailsVisible))]
|
||||
private CalendarItemViewModel _displayDetailsCalendarItemViewModel;
|
||||
|
||||
public bool IsEventDetailsVisible => DisplayDetailsCalendarItemViewModel != null;
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Get rid of some of the items if we have too many.
|
||||
private const int maxDayRangeSize = 10;
|
||||
|
||||
@@ -115,7 +131,9 @@ namespace Wino.Calendar.ViewModels
|
||||
|
||||
private SemaphoreSlim _calendarLoadingSemaphore = new(1);
|
||||
private bool isLoadMoreBlocked = false;
|
||||
private CalendarSettings _currentSettings = null;
|
||||
|
||||
[ObservableProperty]
|
||||
private CalendarSettings _currentSettings;
|
||||
|
||||
public IStatePersistanceService StatePersistanceService { get; }
|
||||
public IAccountCalendarStateService AccountCalendarStateService { get; }
|
||||
@@ -148,6 +166,8 @@ namespace Wino.Calendar.ViewModels
|
||||
var days = dayRangeRenderModels.SelectMany(a => a.CalendarDays);
|
||||
|
||||
days.ForEach(a => a.EventsCollection.FilterByCalendars(AccountCalendarStateService.ActiveCalendars.Select(a => a.Id)));
|
||||
|
||||
DisplayDetailsCalendarItemViewModel = null;
|
||||
}
|
||||
|
||||
// TODO: Replace when calendar settings are updated.
|
||||
@@ -156,8 +176,8 @@ namespace Wino.Calendar.ViewModels
|
||||
{
|
||||
return displayType switch
|
||||
{
|
||||
CalendarDisplayType.Day => new DayCalendarDrawingStrategy(_currentSettings),
|
||||
CalendarDisplayType.Week => new WeekCalendarDrawingStrategy(_currentSettings),
|
||||
CalendarDisplayType.Day => new DayCalendarDrawingStrategy(CurrentSettings),
|
||||
CalendarDisplayType.Week => new WeekCalendarDrawingStrategy(CurrentSettings),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
}
|
||||
@@ -178,6 +198,26 @@ namespace Wino.Calendar.ViewModels
|
||||
SelectedQuickEventAccountCalendar = AccountCalendarStateService.ActiveCalendars.FirstOrDefault(a => a.IsPrimary);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void NavigateSeries()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void NavigateEventDetails()
|
||||
{
|
||||
if (DisplayDetailsCalendarItemViewModel == null) return;
|
||||
|
||||
NavigateEvent(DisplayDetailsCalendarItemViewModel);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void NavigateEvent(CalendarItemViewModel calendarItemViewModel)
|
||||
{
|
||||
// Double tap or clicked 'view details' of the event detail popup.
|
||||
}
|
||||
|
||||
[RelayCommand(AllowConcurrentExecutions = false, CanExecute = nameof(CanSaveQuickEvent))]
|
||||
private async Task SaveQuickEventAsync()
|
||||
{
|
||||
@@ -211,13 +251,33 @@ namespace Wino.Calendar.ViewModels
|
||||
{
|
||||
IsAllDay = false;
|
||||
|
||||
SelectedStartTimeString = _currentSettings.GetTimeString(startTime);
|
||||
SelectedEndTimeString = _currentSettings.GetTimeString(endTime);
|
||||
SelectedStartTimeString = CurrentSettings.GetTimeString(startTime);
|
||||
SelectedEndTimeString = CurrentSettings.GetTimeString(endTime);
|
||||
}
|
||||
|
||||
// Manage event detail popup context and select-unselect the proper items.
|
||||
// Item selection rules are defined in the selection method.
|
||||
partial void OnDisplayDetailsCalendarItemViewModelChanging(CalendarItemViewModel oldValue, CalendarItemViewModel newValue)
|
||||
{
|
||||
if (oldValue != null)
|
||||
{
|
||||
UnselectCalendarItem(oldValue);
|
||||
}
|
||||
|
||||
if (newValue != null)
|
||||
{
|
||||
SelectCalendarItem(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Notify view that the detail context changed.
|
||||
// This will align the event detail popup to the selected event.
|
||||
partial void OnDisplayDetailsCalendarItemViewModelChanged(CalendarItemViewModel value)
|
||||
=> DetailsShowCalendarItemChanged?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
private void RefreshSettings()
|
||||
{
|
||||
_currentSettings = _preferencesService.GetCurrentCalendarSettings();
|
||||
CurrentSettings = _preferencesService.GetCurrentCalendarSettings();
|
||||
|
||||
// Populate the hour selection strings.
|
||||
var timeStrings = new List<string>();
|
||||
@@ -228,7 +288,7 @@ namespace Wino.Calendar.ViewModels
|
||||
{
|
||||
var time = new DateTime(1, 1, 1, hour, minute, 0);
|
||||
|
||||
if (_currentSettings.DayHeaderDisplayType == DayHeaderDisplayType.TwentyFourHour)
|
||||
if (CurrentSettings.DayHeaderDisplayType == DayHeaderDisplayType.TwentyFourHour)
|
||||
{
|
||||
timeStrings.Add(time.ToString("HH:mm"));
|
||||
}
|
||||
@@ -255,7 +315,6 @@ namespace Wino.Calendar.ViewModels
|
||||
// 2. Day display count is different.
|
||||
// 3. Display date is not in the visible range.
|
||||
|
||||
|
||||
if (DayRanges.DisplayRange == null) return false;
|
||||
|
||||
return
|
||||
@@ -289,6 +348,9 @@ namespace Wino.Calendar.ViewModels
|
||||
await RenderDatesAsync(message.CalendarInitInitiative,
|
||||
message.DisplayDate,
|
||||
CalendarLoadDirection.Replace);
|
||||
|
||||
// Scroll to the current hour.
|
||||
Messenger.Send(new ScrollToHourMessage(TimeSpan.FromHours(DateTime.Now.Hour)));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -403,7 +465,7 @@ namespace Wino.Calendar.ViewModels
|
||||
var endDate = startDate.AddDays(eachFlipItemCount);
|
||||
|
||||
var range = new DateRange(startDate, endDate);
|
||||
var renderOptions = new CalendarRenderOptions(range, _currentSettings);
|
||||
var renderOptions = new CalendarRenderOptions(range, CurrentSettings);
|
||||
|
||||
var dayRangeHeaderModel = new DayRangeRenderModel(renderOptions);
|
||||
renderModels.Add(dayRangeHeaderModel);
|
||||
@@ -613,14 +675,9 @@ namespace Wino.Calendar.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnSelectedQuickEventDateChanged(DateTime? value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
partial void OnSelectedStartTimeStringChanged(string newValue)
|
||||
{
|
||||
var parsedTime = _currentSettings.GetTimeSpan(newValue);
|
||||
var parsedTime = CurrentSettings.GetTimeSpan(newValue);
|
||||
|
||||
if (parsedTime == null)
|
||||
{
|
||||
@@ -634,7 +691,7 @@ namespace Wino.Calendar.ViewModels
|
||||
|
||||
partial void OnSelectedEndTimeStringChanged(string newValue)
|
||||
{
|
||||
var parsedTime = _currentSettings.GetTimeSpan(newValue);
|
||||
var parsedTime = CurrentSettings.GetTimeSpan(newValue);
|
||||
|
||||
if (parsedTime == null)
|
||||
{
|
||||
@@ -648,6 +705,8 @@ namespace Wino.Calendar.ViewModels
|
||||
|
||||
partial void OnSelectedDayRangeChanged(DayRangeRenderModel value)
|
||||
{
|
||||
DisplayDetailsCalendarItemViewModel = null;
|
||||
|
||||
if (DayRanges.Count == 0 || SelectedDateRangeIndex < 0) return;
|
||||
|
||||
var selectedRange = DayRanges[SelectedDateRangeIndex];
|
||||
@@ -699,71 +758,63 @@ namespace Wino.Calendar.ViewModels
|
||||
// Messenger.Send(new LoadCalendarMessage(DateTime.UtcNow.Date, CalendarInitInitiative.App, true));
|
||||
}
|
||||
|
||||
private IEnumerable<CalendarItemViewModel> GetCalendarItems(Guid calendarItemId)
|
||||
private IEnumerable<CalendarItemViewModel> GetCalendarItems(CalendarItemViewModel calendarItemViewModel, CalendarDayModel selectedDay)
|
||||
{
|
||||
// Multi-day events are sprated in multiple days.
|
||||
// All-day and multi-day events are selected collectively.
|
||||
// Recurring events must be selected as a single instance.
|
||||
// We need to find the day that the event is in, and then select the event.
|
||||
|
||||
return DayRanges
|
||||
.SelectMany(a => a.CalendarDays)
|
||||
.Select(b => b.EventsCollection.GetCalendarItem(calendarItemId))
|
||||
.Where(c => c != null)
|
||||
.Cast<CalendarItemViewModel>()
|
||||
.Distinct();
|
||||
}
|
||||
|
||||
private void ResetSelectedItems()
|
||||
{
|
||||
foreach (var item in AccountCalendarStateService.SelectedItems)
|
||||
if (calendarItemViewModel.IsSingleExceptionalInstance)
|
||||
{
|
||||
var items = GetCalendarItems(item.Id);
|
||||
|
||||
foreach (var childItem in items)
|
||||
{
|
||||
childItem.IsSelected = false;
|
||||
}
|
||||
return [calendarItemViewModel];
|
||||
}
|
||||
else
|
||||
{
|
||||
return DayRanges
|
||||
.SelectMany(a => a.CalendarDays)
|
||||
.Select(b => b.EventsCollection.GetCalendarItem(calendarItemViewModel.Id))
|
||||
.Where(c => c != null)
|
||||
.Cast<CalendarItemViewModel>()
|
||||
.Distinct();
|
||||
}
|
||||
|
||||
AccountCalendarStateService.SelectedItems.Clear();
|
||||
}
|
||||
|
||||
public async void Receive(CalendarItemTappedMessage message)
|
||||
private void UnselectCalendarItem(CalendarItemViewModel calendarItemViewModel, CalendarDayModel calendarDay = null)
|
||||
{
|
||||
if (calendarItemViewModel == null) return;
|
||||
|
||||
var itemsToUnselect = GetCalendarItems(calendarItemViewModel, calendarDay);
|
||||
|
||||
foreach (var item in itemsToUnselect)
|
||||
{
|
||||
item.IsSelected = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectCalendarItem(CalendarItemViewModel calendarItemViewModel, CalendarDayModel calendarDay = null)
|
||||
{
|
||||
if (calendarItemViewModel == null) return;
|
||||
|
||||
var itemsToSelect = GetCalendarItems(calendarItemViewModel, calendarDay);
|
||||
|
||||
foreach (var item in itemsToSelect)
|
||||
{
|
||||
item.IsSelected = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(CalendarItemTappedMessage message)
|
||||
{
|
||||
if (message.CalendarItemViewModel == null) return;
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
var calendarItems = GetCalendarItems(message.CalendarItemViewModel.Id);
|
||||
|
||||
if (!_keyPressService.IsCtrlKeyPressed())
|
||||
{
|
||||
ResetSelectedItems();
|
||||
}
|
||||
|
||||
foreach (var item in calendarItems)
|
||||
{
|
||||
item.IsSelected = !item.IsSelected;
|
||||
|
||||
// Multi-select logic.
|
||||
if (item.IsSelected && !AccountCalendarStateService.SelectedItems.Contains(item))
|
||||
{
|
||||
AccountCalendarStateService.SelectedItems.Add(message.CalendarItemViewModel);
|
||||
}
|
||||
else if (!item.IsSelected && AccountCalendarStateService.SelectedItems.Contains(item))
|
||||
{
|
||||
AccountCalendarStateService.SelectedItems.Remove(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
DisplayDetailsCalendarItemViewModel = message.CalendarItemViewModel;
|
||||
}
|
||||
|
||||
public void Receive(CalendarItemDoubleTappedMessage message)
|
||||
{
|
||||
// TODO: Navigate to the event details page.
|
||||
}
|
||||
public void Receive(CalendarItemDoubleTappedMessage message) => NavigateEvent(message.CalendarItemViewModel);
|
||||
|
||||
public void Receive(CalendarItemRightTappedMessage message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public async void Receive(CalendarItemDeleted message)
|
||||
@@ -777,9 +828,7 @@ namespace Wino.Calendar.ViewModels
|
||||
// Event might be spreaded into multiple days.
|
||||
// Remove from all.
|
||||
|
||||
var calendarItems = GetCalendarItems(deletedItem.Id);
|
||||
|
||||
|
||||
// var calendarItems = GetCalendarItems(deletedItem.Id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,6 @@ namespace Wino.Calendar.ViewModels
|
||||
[ObservableProperty]
|
||||
private bool _is24HourHeaders;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _ghostRenderAllDayEvents;
|
||||
|
||||
[ObservableProperty]
|
||||
private TimeSpan _workingHourStart;
|
||||
|
||||
@@ -64,7 +61,6 @@ namespace Wino.Calendar.ViewModels
|
||||
_workingHourStart = preferencesService.WorkingHourStart;
|
||||
_workingHourEnd = preferencesService.WorkingHourEnd;
|
||||
_cellHourHeight = preferencesService.HourHeight;
|
||||
_ghostRenderAllDayEvents = preferencesService.GhostRenderAllDayEvents;
|
||||
|
||||
_workingDayStartIndex = _dayNames.IndexOf(cultureInfo.DateTimeFormat.GetDayName(preferencesService.WorkingDayStart));
|
||||
_workingDayEndIndex = _dayNames.IndexOf(cultureInfo.DateTimeFormat.GetDayName(preferencesService.WorkingDayEnd));
|
||||
@@ -79,7 +75,6 @@ namespace Wino.Calendar.ViewModels
|
||||
partial void OnWorkingHourEndChanged(TimeSpan value) => SaveSettings();
|
||||
partial void OnWorkingDayStartIndexChanged(int value) => SaveSettings();
|
||||
partial void OnWorkingDayEndIndexChanged(int value) => SaveSettings();
|
||||
partial void OnGhostRenderAllDayEventsChanged(bool value) => SaveSettings();
|
||||
|
||||
public void SaveSettings()
|
||||
{
|
||||
|
||||
@@ -24,11 +24,13 @@ namespace Wino.Calendar.ViewModels.Data
|
||||
|
||||
public ITimePeriod Period => CalendarItem.Period;
|
||||
|
||||
public bool IsAllDayEvent => ((ICalendarItem)CalendarItem).IsAllDayEvent;
|
||||
public bool IsAllDayEvent => CalendarItem.IsAllDayEvent;
|
||||
|
||||
public bool IsMultiDayEvent => ((ICalendarItem)CalendarItem).IsMultiDayEvent;
|
||||
public bool IsMultiDayEvent => CalendarItem.IsMultiDayEvent;
|
||||
|
||||
public bool IsRecurringEvent => !string.IsNullOrEmpty(CalendarItem.Recurrence);
|
||||
public bool IsRecurringEvent => CalendarItem.IsRecurringEvent;
|
||||
|
||||
public bool IsSingleExceptionalInstance => CalendarItem.IsSingleExceptionalInstance;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isSelected;
|
||||
|
||||
@@ -22,9 +22,6 @@ namespace Wino.Calendar.ViewModels.Interfaces
|
||||
public void AddAccountCalendar(AccountCalendarViewModel accountCalendar);
|
||||
public void RemoveAccountCalendar(AccountCalendarViewModel accountCalendar);
|
||||
|
||||
ObservableCollection<CalendarItemViewModel> SelectedItems { get; }
|
||||
bool HasMultipleSelectedItems { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration of currently selected calendars.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
using Wino.Calendar.ViewModels.Data;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
namespace Wino.Calendar.ViewModels.Messages
|
||||
{
|
||||
public class CalendarItemTappedMessage
|
||||
{
|
||||
public CalendarItemTappedMessage(CalendarItemViewModel calendarItemViewModel)
|
||||
public CalendarItemTappedMessage(CalendarItemViewModel calendarItemViewModel, CalendarDayModel clickedPeriod)
|
||||
{
|
||||
CalendarItemViewModel = calendarItemViewModel;
|
||||
ClickedPeriod = clickedPeriod;
|
||||
}
|
||||
|
||||
public CalendarItemViewModel CalendarItemViewModel { get; }
|
||||
public CalendarDayModel ClickedPeriod { get; }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user