2024-12-28 23:17:16 +01:00
|
|
|
using System;
|
2025-01-16 22:00:05 +01:00
|
|
|
using System.Collections.ObjectModel;
|
2024-12-28 23:17:16 +01:00
|
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
|
using Itenso.TimePeriod;
|
2026-01-06 11:11:37 +01:00
|
|
|
using Wino.Core.Domain;
|
2024-12-29 19:37:36 +01:00
|
|
|
using Wino.Core.Domain.Entities.Calendar;
|
2026-02-11 01:49:29 +01:00
|
|
|
using Wino.Core.Domain.Extensions;
|
2024-12-28 23:17:16 +01:00
|
|
|
using Wino.Core.Domain.Interfaces;
|
2026-01-06 11:11:37 +01:00
|
|
|
using Wino.Core.Domain.Models.Calendar;
|
2024-12-28 23:17:16 +01:00
|
|
|
|
2025-05-18 14:06:25 +02:00
|
|
|
namespace Wino.Calendar.ViewModels.Data;
|
2024-12-28 23:17:16 +01:00
|
|
|
|
2025-05-18 14:06:25 +02:00
|
|
|
public partial class CalendarItemViewModel : ObservableObject, ICalendarItem, ICalendarItemViewModel
|
|
|
|
|
{
|
|
|
|
|
public CalendarItem CalendarItem { get; }
|
2024-12-28 23:17:16 +01:00
|
|
|
|
2025-05-18 14:06:25 +02:00
|
|
|
public string Title => CalendarItem.Title;
|
2024-12-28 23:17:16 +01:00
|
|
|
|
2025-05-18 14:06:25 +02:00
|
|
|
public Guid Id => CalendarItem.Id;
|
2024-12-28 23:17:16 +01:00
|
|
|
|
2025-05-18 14:06:25 +02:00
|
|
|
public IAccountCalendar AssignedCalendar => CalendarItem.AssignedCalendar;
|
2024-12-28 23:17:16 +01:00
|
|
|
|
2025-12-29 14:10:09 +01:00
|
|
|
/// <summary>
|
2026-01-01 10:07:56 +01:00
|
|
|
/// Gets or sets the start date converted to user's local timezone for display.
|
|
|
|
|
/// The underlying CalendarItem stores dates according to their timezone.
|
2025-12-29 14:10:09 +01:00
|
|
|
/// </summary>
|
|
|
|
|
public DateTime StartDate
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2026-01-01 10:07:56 +01:00
|
|
|
// Get start date in user's local timezone
|
|
|
|
|
return CalendarItem.LocalStartDate;
|
2025-12-29 14:10:09 +01:00
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
2026-03-28 01:44:12 +01:00
|
|
|
// All-day events use floating dates and should not shift across timezones.
|
|
|
|
|
CalendarItem.StartDate = CalendarItem.IsAllDayEvent
|
|
|
|
|
? value.Date
|
|
|
|
|
: value.ToTimeZoneFromLocal(CalendarItem.StartTimeZone);
|
2025-12-29 14:10:09 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-12-28 23:17:16 +01:00
|
|
|
|
2025-12-29 14:10:09 +01:00
|
|
|
/// <summary>
|
2026-01-01 10:07:56 +01:00
|
|
|
/// Gets the end date converted to user's local timezone for display.
|
|
|
|
|
/// The underlying CalendarItem stores dates according to their timezone.
|
2025-12-29 14:10:09 +01:00
|
|
|
/// </summary>
|
|
|
|
|
public DateTime EndDate
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2026-01-01 10:07:56 +01:00
|
|
|
// Get end date in user's local timezone
|
|
|
|
|
return CalendarItem.LocalEndDate;
|
2025-12-29 14:10:09 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-12-30 23:10:51 +01:00
|
|
|
|
2025-05-18 14:06:25 +02:00
|
|
|
public double DurationInSeconds { get => CalendarItem.DurationInSeconds; set => CalendarItem.DurationInSeconds = value; }
|
2024-12-29 19:37:36 +01:00
|
|
|
|
2025-12-29 14:10:09 +01:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the time period in local time.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ITimePeriod Period
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
// Return a period using local times for UI display
|
|
|
|
|
return new TimeRange(StartDate, EndDate);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-01-02 00:18:34 +01:00
|
|
|
|
2025-05-18 14:06:25 +02:00
|
|
|
public bool IsAllDayEvent => CalendarItem.IsAllDayEvent;
|
|
|
|
|
public bool IsMultiDayEvent => CalendarItem.IsMultiDayEvent;
|
|
|
|
|
public bool IsRecurringEvent => CalendarItem.IsRecurringEvent;
|
|
|
|
|
public bool IsRecurringChild => CalendarItem.IsRecurringChild;
|
|
|
|
|
public bool IsRecurringParent => CalendarItem.IsRecurringParent;
|
2026-04-08 23:46:02 +02:00
|
|
|
public bool CanDragDrop => CalendarItem.CanChangeStartAndEndDate;
|
2025-01-02 00:18:34 +01:00
|
|
|
|
2025-05-18 14:06:25 +02:00
|
|
|
[ObservableProperty]
|
2025-12-29 14:10:09 +01:00
|
|
|
public partial bool IsSelected { get; set; }
|
2025-01-16 22:00:05 +01:00
|
|
|
|
2026-02-15 19:26:06 +01:00
|
|
|
[ObservableProperty]
|
|
|
|
|
public partial bool IsBusy { get; set; }
|
|
|
|
|
|
2026-01-06 11:11:37 +01:00
|
|
|
/// <summary>
|
|
|
|
|
/// The period of the day where this item is currently being displayed.
|
|
|
|
|
/// Used for multi-day event title formatting.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[ObservableProperty]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(DisplayTitle))]
|
|
|
|
|
public partial ITimePeriod DisplayingPeriod { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Calendar settings for time formatting.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[ObservableProperty]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(DisplayTitle))]
|
|
|
|
|
public partial CalendarSettings CalendarSettings { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the display title based on the current displaying period.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string DisplayTitle
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (DisplayingPeriod == null || CalendarSettings == null)
|
|
|
|
|
return Title;
|
|
|
|
|
|
|
|
|
|
return GetDisplayTitle(DisplayingPeriod, CalendarSettings);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-18 14:06:25 +02:00
|
|
|
public ObservableCollection<CalendarEventAttendee> Attendees { get; } = new ObservableCollection<CalendarEventAttendee>();
|
2025-01-04 11:39:32 +01:00
|
|
|
|
2025-05-18 14:06:25 +02:00
|
|
|
public CalendarItemViewModel(CalendarItem calendarItem)
|
|
|
|
|
{
|
|
|
|
|
CalendarItem = calendarItem;
|
2024-12-28 23:17:16 +01:00
|
|
|
}
|
2025-05-18 14:06:25 +02:00
|
|
|
|
2026-01-06 11:11:37 +01:00
|
|
|
/// <summary>
|
|
|
|
|
/// Updates the underlying CalendarItem with new data and raises property change notifications.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="calendarItem">The updated calendar item data.</param>
|
|
|
|
|
public void UpdateFrom(CalendarItem calendarItem)
|
|
|
|
|
{
|
|
|
|
|
if (calendarItem == null || calendarItem.Id != CalendarItem.Id)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Update all mutable properties
|
|
|
|
|
CalendarItem.Title = calendarItem.Title;
|
|
|
|
|
CalendarItem.Description = calendarItem.Description;
|
|
|
|
|
CalendarItem.Location = calendarItem.Location;
|
|
|
|
|
CalendarItem.StartDate = calendarItem.StartDate;
|
|
|
|
|
CalendarItem.StartTimeZone = calendarItem.StartTimeZone;
|
|
|
|
|
CalendarItem.EndTimeZone = calendarItem.EndTimeZone;
|
|
|
|
|
CalendarItem.DurationInSeconds = calendarItem.DurationInSeconds;
|
|
|
|
|
CalendarItem.Recurrence = calendarItem.Recurrence;
|
|
|
|
|
CalendarItem.RecurringCalendarItemId = calendarItem.RecurringCalendarItemId;
|
|
|
|
|
CalendarItem.OrganizerDisplayName = calendarItem.OrganizerDisplayName;
|
|
|
|
|
CalendarItem.OrganizerEmail = calendarItem.OrganizerEmail;
|
|
|
|
|
CalendarItem.IsLocked = calendarItem.IsLocked;
|
|
|
|
|
CalendarItem.IsHidden = calendarItem.IsHidden;
|
|
|
|
|
CalendarItem.CustomEventColorHex = calendarItem.CustomEventColorHex;
|
|
|
|
|
CalendarItem.HtmlLink = calendarItem.HtmlLink;
|
|
|
|
|
CalendarItem.Status = calendarItem.Status;
|
|
|
|
|
CalendarItem.Visibility = calendarItem.Visibility;
|
|
|
|
|
CalendarItem.ShowAs = calendarItem.ShowAs;
|
|
|
|
|
CalendarItem.UpdatedAt = calendarItem.UpdatedAt;
|
|
|
|
|
CalendarItem.AssignedCalendar = calendarItem.AssignedCalendar;
|
|
|
|
|
|
|
|
|
|
// Raise property changed for all bindable properties
|
|
|
|
|
OnPropertyChanged(nameof(Title));
|
|
|
|
|
OnPropertyChanged(nameof(StartDate));
|
|
|
|
|
OnPropertyChanged(nameof(EndDate));
|
|
|
|
|
OnPropertyChanged(nameof(DurationInSeconds));
|
|
|
|
|
OnPropertyChanged(nameof(Period));
|
|
|
|
|
OnPropertyChanged(nameof(IsAllDayEvent));
|
|
|
|
|
OnPropertyChanged(nameof(IsMultiDayEvent));
|
|
|
|
|
OnPropertyChanged(nameof(IsRecurringEvent));
|
|
|
|
|
OnPropertyChanged(nameof(IsRecurringChild));
|
|
|
|
|
OnPropertyChanged(nameof(IsRecurringParent));
|
2026-04-08 23:46:02 +02:00
|
|
|
OnPropertyChanged(nameof(CanDragDrop));
|
2026-01-06 11:11:37 +01:00
|
|
|
OnPropertyChanged(nameof(AssignedCalendar));
|
|
|
|
|
OnPropertyChanged(nameof(DisplayTitle));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the display title for this calendar item when rendered in a specific day.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string GetDisplayTitle(ITimePeriod displayingPeriod, CalendarSettings calendarSettings)
|
|
|
|
|
{
|
|
|
|
|
if (!IsMultiDayEvent)
|
|
|
|
|
return Title;
|
|
|
|
|
|
|
|
|
|
var periodRelation = Period.GetRelation(displayingPeriod);
|
|
|
|
|
|
|
|
|
|
if (periodRelation == PeriodRelation.StartInside || periodRelation == PeriodRelation.EnclosingStartTouching)
|
|
|
|
|
{
|
|
|
|
|
// Event starts within this day: "HH:mm -> Title"
|
|
|
|
|
return $"{calendarSettings.GetTimeString(StartDate.TimeOfDay)} -> {Title}";
|
|
|
|
|
}
|
|
|
|
|
else if (periodRelation == PeriodRelation.EndInside || periodRelation == PeriodRelation.EnclosingEndTouching)
|
|
|
|
|
{
|
|
|
|
|
// Event ends within this day: "Title <- HH:mm"
|
|
|
|
|
return $"{Title} <- {calendarSettings.GetTimeString(EndDate.TimeOfDay)}";
|
|
|
|
|
}
|
|
|
|
|
else if (periodRelation == PeriodRelation.Enclosing)
|
|
|
|
|
{
|
|
|
|
|
// Event spans the entire day
|
|
|
|
|
return $"{Translator.CalendarItemAllDay} {Title}";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return Title;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-18 14:06:25 +02:00
|
|
|
public override string ToString() => CalendarItem.Title;
|
2026-01-01 10:07:56 +01:00
|
|
|
}
|