using System; using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel; using Itenso.TimePeriod; using Wino.Core.Domain; using Wino.Core.Domain.Entities.Calendar; using Wino.Core.Domain.Extensions; using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Models.Calendar; namespace Wino.Calendar.ViewModels.Data; public partial class CalendarItemViewModel : ObservableObject, ICalendarItem, ICalendarItemViewModel { public CalendarItem CalendarItem { get; } public string Title => CalendarItem.Title; public Guid Id => CalendarItem.Id; public IAccountCalendar AssignedCalendar => CalendarItem.AssignedCalendar; /// /// Gets or sets the start date converted to user's local timezone for display. /// The underlying CalendarItem stores dates according to their timezone. /// public DateTime StartDate { get { // Get start date in user's local timezone return CalendarItem.LocalStartDate; } set { // When setting from UI (in local time), convert to event's timezone for storage. CalendarItem.StartDate = value.ToTimeZoneFromLocal(CalendarItem.StartTimeZone); } } /// /// Gets the end date converted to user's local timezone for display. /// The underlying CalendarItem stores dates according to their timezone. /// public DateTime EndDate { get { // Get end date in user's local timezone return CalendarItem.LocalEndDate; } } public double DurationInSeconds { get => CalendarItem.DurationInSeconds; set => CalendarItem.DurationInSeconds = value; } /// /// Gets the time period in local time. /// public ITimePeriod Period { get { // Return a period using local times for UI display return new TimeRange(StartDate, EndDate); } } 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; [ObservableProperty] public partial bool IsSelected { get; set; } /// /// The period of the day where this item is currently being displayed. /// Used for multi-day event title formatting. /// [ObservableProperty] [NotifyPropertyChangedFor(nameof(DisplayTitle))] public partial ITimePeriod DisplayingPeriod { get; set; } /// /// Calendar settings for time formatting. /// [ObservableProperty] [NotifyPropertyChangedFor(nameof(DisplayTitle))] public partial CalendarSettings CalendarSettings { get; set; } /// /// Gets the display title based on the current displaying period. /// public string DisplayTitle { get { if (DisplayingPeriod == null || CalendarSettings == null) return Title; return GetDisplayTitle(DisplayingPeriod, CalendarSettings); } } public ObservableCollection Attendees { get; } = new ObservableCollection(); public CalendarItemViewModel(CalendarItem calendarItem) { CalendarItem = calendarItem; } /// /// Updates the underlying CalendarItem with new data and raises property change notifications. /// /// The updated calendar item data. 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)); OnPropertyChanged(nameof(AssignedCalendar)); OnPropertyChanged(nameof(DisplayTitle)); } /// /// Gets the display title for this calendar item when rendered in a specific day. /// 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; } } public override string ToString() => CalendarItem.Title; }