Event details UI improvements.

This commit is contained in:
Burak Kaan Köse
2026-01-01 10:07:56 +01:00
parent e71c050724
commit 3b485dc1fe
14 changed files with 311 additions and 113 deletions
@@ -817,15 +817,10 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
private IEnumerable<CalendarItemViewModel> GetCalendarItems(CalendarItemViewModel calendarItemViewModel, CalendarDayModel selectedDay)
{
// 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.
// Multi-day events, all-day events, and recurring events are rendered across multiple days.
// We need to find all instances with the same ID across all visible date ranges.
if (!calendarItemViewModel.IsRecurringEvent)
{
return [calendarItemViewModel];
}
else
if (calendarItemViewModel.IsRecurringEvent || calendarItemViewModel.IsMultiDayEvent)
{
return DayRanges
.SelectMany(a => a.CalendarDays)
@@ -834,6 +829,11 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
.Cast<CalendarItemViewModel>()
.Distinct();
}
else
{
// Single-day, non-recurring events only appear once
return [calendarItemViewModel];
}
}
private void UnselectCalendarItem(CalendarItemViewModel calendarItemViewModel, CalendarDayModel calendarDay = null)
@@ -877,6 +877,13 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
Debug.WriteLine($"Calendar item deleted: {calendarItem.Id}");
// Check if the deleted item is currently displayed in details view
if (DisplayDetailsCalendarItemViewModel?.Id == calendarItem.Id)
{
// Clear the details view since this item was deleted
DisplayDetailsCalendarItemViewModel = null;
}
// Remove the event and its occurrences from all visible date ranges.
await ExecuteUIThread(() =>
{
@@ -956,6 +963,47 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
// Check if event falls into the current date range.
if (DayRanges.DisplayRange == null) return;
// Check if this is a server-synced item that matches a local preview
// Local previews don't have RemoteEventId, server-synced items do
if (!string.IsNullOrEmpty(calendarItem.RemoteEventId))
{
// Find local preview items that match this event's properties
var localPreviewItems = DayRanges
.SelectMany(a => a.CalendarDays)
.SelectMany(b => b.EventsCollection.RegularEvents.Concat(b.EventsCollection.AllDayEvents))
.OfType<CalendarItemViewModel>()
.Where(c => c.AssignedCalendar.Id == calendarItem.CalendarId &&
c.CalendarItem.IsLocalPreview && // Local preview (no RemoteEventId)
c.Title == calendarItem.Title &&
Math.Abs((c.StartDate - calendarItem.LocalStartDate).TotalSeconds) < 60 &&
Math.Abs(c.DurationInSeconds - calendarItem.DurationInSeconds) < 1)
.ToList();
if (localPreviewItems.Any())
{
Debug.WriteLine($"Found {localPreviewItems.Count} matching local preview items for {calendarItem.Title}, removing them.");
// Remove all matching local preview items
await ExecuteUIThread(() =>
{
foreach (var dayRange in DayRanges)
{
foreach (var calendarDay in dayRange.CalendarDays)
{
foreach (var localPreview in localPreviewItems)
{
var itemInDay = calendarDay.EventsCollection.GetCalendarItem(localPreview.Id);
if (itemInDay != null)
{
calendarDay.EventsCollection.RemoveCalendarItem(itemInDay);
}
}
}
}
});
}
}
// Get all periods from the visible day ranges
var visiblePeriods = DayRanges.Select(dr => dr.Period).ToList();
@@ -18,54 +18,51 @@ public partial class CalendarItemViewModel : ObservableObject, ICalendarItem, IC
public IAccountCalendar AssignedCalendar => CalendarItem.AssignedCalendar;
/// <summary>
/// Gets or sets the start date in local time based on the event's timezone.
/// The underlying CalendarItem stores dates in UTC.
/// Gets or sets the start date converted to user's local timezone for display.
/// The underlying CalendarItem stores dates according to their timezone.
/// </summary>
public DateTime StartDate
{
get
{
// Convert from UTC stored in database to local time using the event's timezone
var startDateTimeOffset = CalendarItem.StartDateTimeOffset;
return startDateTimeOffset.LocalDateTime;
// Get start date in user's local timezone
return CalendarItem.LocalStartDate;
}
set
{
// When setting, convert from local time to UTC for storage
// Preserve the timezone information
// When setting from UI (in local time), convert to event's timezone for storage
if (!string.IsNullOrEmpty(CalendarItem.StartTimeZone))
{
try
{
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(CalendarItem.StartTimeZone);
var utcDateTime = TimeZoneInfo.ConvertTimeToUtc(value, timeZoneInfo);
CalendarItem.StartDate = utcDateTime;
var sourceTimeZone = TimeZoneInfo.Local;
var targetTimeZone = TimeZoneInfo.FindSystemTimeZoneById(CalendarItem.StartTimeZone);
CalendarItem.StartDate = TimeZoneInfo.ConvertTime(value, sourceTimeZone, targetTimeZone);
}
catch
{
// If timezone lookup fails, assume value is already in UTC
// If timezone lookup fails, set as-is
CalendarItem.StartDate = value;
}
}
else
{
// No timezone info, assume UTC
// No timezone info, set as-is
CalendarItem.StartDate = value;
}
}
}
/// <summary>
/// Gets the end date in local time based on the event's timezone.
/// The underlying CalendarItem stores dates in UTC.
/// Gets the end date converted to user's local timezone for display.
/// The underlying CalendarItem stores dates according to their timezone.
/// </summary>
public DateTime EndDate
{
get
{
// Convert from UTC stored in database to local time using the event's timezone
var endDateTimeOffset = CalendarItem.EndDateTimeOffset;
return endDateTimeOffset.LocalDateTime;
// Get end date in user's local timezone
return CalendarItem.LocalEndDate;
}
}
@@ -100,4 +97,4 @@ public partial class CalendarItemViewModel : ObservableObject, ICalendarItem, IC
}
public override string ToString() => CalendarItem.Title;
}
}
@@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Wino.Calendar.ViewModels.Data;
using Wino.Core.Domain;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Calendar;
@@ -19,6 +21,9 @@ public partial class EventDetailsPageViewModel : CalendarBaseViewModel
private readonly ICalendarService _calendarService;
private readonly INativeAppService _nativeAppService;
private readonly IPreferencesService _preferencesService;
private readonly IMailDialogService _dialogService;
private readonly IWinoRequestDelegator _winoRequestDelegator;
private readonly INavigationService _navigationService;
public CalendarSettings CurrentSettings { get; }
@@ -26,20 +31,55 @@ public partial class EventDetailsPageViewModel : CalendarBaseViewModel
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(CanViewSeries))]
[NotifyPropertyChangedFor(nameof(CanEditSeries))]
private CalendarItemViewModel _currentEvent;
[ObservableProperty]
private CalendarItemViewModel _seriesParent;
/// <summary>
/// Returns true if the event is part of a recurring series (as a child occurrence).
/// Used to enable "View Series" functionality.
/// </summary>
public bool CanViewSeries => CurrentEvent?.IsRecurringChild ?? false;
/// <summary>
/// Returns true if the "Edit Series" button should be visible.
/// Only visible for child occurrences of recurring events, not for master events or single events.
/// </summary>
public bool CanEditSeries => CurrentEvent?.IsRecurringChild ?? false;
#endregion
public EventDetailsPageViewModel(ICalendarService calendarService, INativeAppService nativeAppService, IPreferencesService preferencesService)
#region Show As Options
public List<CalendarItemShowAs> ShowAsOptions { get; } =
[
CalendarItemShowAs.Free,
CalendarItemShowAs.Tentative,
CalendarItemShowAs.Busy,
CalendarItemShowAs.OutOfOffice,
CalendarItemShowAs.WorkingElsewhere
];
[ObservableProperty]
public partial CalendarItemShowAs SelectedShowAs { get; set; } = CalendarItemShowAs.Busy;
#endregion
public EventDetailsPageViewModel(ICalendarService calendarService,
INativeAppService nativeAppService,
IPreferencesService preferencesService,
IMailDialogService dialogService,
IWinoRequestDelegator winoRequestDelegator,
INavigationService navigationService)
{
_calendarService = calendarService;
_nativeAppService = nativeAppService;
_preferencesService = preferencesService;
_dialogService = dialogService;
_winoRequestDelegator = winoRequestDelegator;
_navigationService = navigationService;
CurrentSettings = _preferencesService.GetCurrentCalendarSettings();
}
@@ -90,26 +130,57 @@ public partial class EventDetailsPageViewModel : CalendarBaseViewModel
[RelayCommand]
private async Task SaveAsync()
{
// TODO: Implement saving
}
[RelayCommand]
private async Task DeleteAsync()
{
if (CurrentEvent == null) return;
// If the event is a master recurring event, ask for confirmation
if (CurrentEvent.IsRecurringParent)
{
var confirmed = await _dialogService.ShowConfirmationDialogAsync(
Translator.DialogMessage_DeleteRecurringSeriesMessage,
Translator.DialogMessage_DeleteRecurringSeriesTitle,
Translator.Buttons_Delete);
if (!confirmed) return;
}
try
{
var preparationRequest = new CalendarOperationPreparationRequest(
CalendarSynchronizerOperation.DeleteEvent,
CurrentEvent.CalendarItem,
null);
await _winoRequestDelegator.ExecuteAsync(preparationRequest);
// Navigate back after successful deletion
_navigationService.GoBack();
}
catch (Exception ex)
{
Debug.WriteLine($"Error deleting calendar event: {ex.Message}");
}
}
[RelayCommand]
private Task JoinOnline()
private Task JoinOnlineAsync()
{
if (CurrentEvent == null || string.IsNullOrEmpty(CurrentEvent.CalendarItem.HtmlLink)) return Task.CompletedTask;
if (CurrentEvent == null || string.IsNullOrEmpty(CurrentEvent.CalendarItem.HtmlLink))
return Task.CompletedTask;
return _nativeAppService.LaunchUriAsync(new Uri(CurrentEvent.CalendarItem.HtmlLink));
}
[RelayCommand]
private async Task Respond(CalendarItemStatus status)
private async Task RespondAsync(CalendarItemStatus status)
{
if (CurrentEvent == null) return;
// TODO: Implement response
}
}
@@ -17,6 +17,14 @@ public class CalendarItem : ICalendarItem
public string Description { get; set; }
public string Location { get; set; }
/// <summary>
/// Indicates whether this item is a local preview that hasn't been synced to the server yet.
/// When true, the item exists only in the local database without a RemoteEventId.
/// Used to prevent duplicates when the server returns the newly created event.
/// </summary>
[Ignore]
public bool IsLocalPreview => string.IsNullOrEmpty(RemoteEventId);
public DateTime StartDate { get; set; }
public DateTime EndDate
@@ -187,66 +195,70 @@ public class CalendarItem : ICalendarItem
}
/// <summary>
/// Gets the start date as a DateTimeOffset with the correct timezone.
/// If StartTimeZone is available, uses it to calculate the offset.
/// Otherwise, assumes UTC (StartDate is stored as UTC in database).
/// Gets the start date converted to user's local timezone for display.
/// StartDate is stored according to StartTimeZone.
/// </summary>
[Ignore]
public DateTimeOffset StartDateTimeOffset
public DateTime LocalStartDate
{
get
{
if (!string.IsNullOrEmpty(StartTimeZone))
if (string.IsNullOrEmpty(StartTimeZone))
{
try
{
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(StartTimeZone);
// StartDate is stored in UTC, convert to the specified timezone
var utcDateTime = DateTime.SpecifyKind(StartDate, DateTimeKind.Utc);
var zonedDateTime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, timeZoneInfo);
var offset = timeZoneInfo.GetUtcOffset(zonedDateTime);
return new DateTimeOffset(zonedDateTime, offset);
}
catch
{
// If timezone lookup fails, assume UTC
}
// No timezone info, return as-is
return StartDate;
}
// Assume UTC (StartDate is stored as UTC in database)
return new DateTimeOffset(StartDate, TimeSpan.Zero);
try
{
var sourceTimeZone = TimeZoneInfo.FindSystemTimeZoneById(StartTimeZone);
var localTimeZone = TimeZoneInfo.Local;
// Ensure DateTime is Unspecified kind before conversion
var unspecifiedDateTime = DateTime.SpecifyKind(StartDate, DateTimeKind.Unspecified);
// Convert from source timezone to local timezone
return TimeZoneInfo.ConvertTime(unspecifiedDateTime, sourceTimeZone, localTimeZone);
}
catch
{
// If timezone lookup fails, return as-is
return StartDate;
}
}
}
/// <summary>
/// Gets the end date as a DateTimeOffset with the correct timezone.
/// If EndTimeZone is available, uses it to calculate the offset.
/// Otherwise, assumes UTC (EndDate is stored as UTC in database).
/// Gets the end date converted to user's local timezone for display.
/// EndDate is calculated from StartDate and is in StartTimeZone.
/// </summary>
[Ignore]
public DateTimeOffset EndDateTimeOffset
public DateTime LocalEndDate
{
get
{
if (!string.IsNullOrEmpty(EndTimeZone))
if (string.IsNullOrEmpty(EndTimeZone))
{
try
{
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(EndTimeZone);
// EndDate is stored in UTC, convert to the specified timezone
var utcDateTime = DateTime.SpecifyKind(EndDate, DateTimeKind.Utc);
var zonedDateTime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, timeZoneInfo);
var offset = timeZoneInfo.GetUtcOffset(zonedDateTime);
return new DateTimeOffset(zonedDateTime, offset);
}
catch
{
// If timezone lookup fails, assume UTC
}
// No timezone info, return as-is
return EndDate;
}
// Assume UTC (EndDate is stored as UTC in database)
return new DateTimeOffset(EndDate, TimeSpan.Zero);
try
{
var sourceTimeZone = TimeZoneInfo.FindSystemTimeZoneById(EndTimeZone);
var localTimeZone = TimeZoneInfo.Local;
// Ensure DateTime is Unspecified kind before conversion
var unspecifiedDateTime = DateTime.SpecifyKind(EndDate, DateTimeKind.Unspecified);
// Convert from source timezone to local timezone
return TimeZoneInfo.ConvertTime(unspecifiedDateTime, sourceTimeZone, localTimeZone);
}
catch
{
// If timezone lookup fails, return as-is
return EndDate;
}
}
}
}
@@ -0,0 +1,13 @@
namespace Wino.Core.Domain.Enums;
/// <summary>
/// Defines how a calendar item should be displayed in terms of availability.
/// </summary>
public enum CalendarItemShowAs
{
Free,
Tentative,
Busy,
OutOfOffice,
WorkingElsewhere
}
@@ -1,5 +0,0 @@
namespace Wino.Core.Domain.Interfaces;
public interface ICalendarDialogService : IDialogServiceBase
{
}
@@ -11,9 +11,3 @@ namespace Wino.Core.Domain.Models.Calendar;
/// <param name="CalendarItem">Calendar item to operate on.</param>
/// <param name="Attendees">List of attendees for the calendar event.</param>
public record CalendarOperationPreparationRequest(CalendarSynchronizerOperation Operation, CalendarItem CalendarItem, List<CalendarEventAttendee> Attendees);
//{
// public CalendarOperationPreparationRequest(CalendarItem calendarItem)
// : this(calendarItem ?? throw new ArgumentNullException(nameof(calendarItem)), null)
// {
// }
//}
@@ -83,6 +83,19 @@
"CalendarAllDayEventSummary": "all-day events",
"CalendarDisplayOptions_Color": "Color",
"CalendarDisplayOptions_Expand": "Expand",
"CalendarEventResponse_Accept": "Accept",
"CalendarEventResponse_Decline": "Decline",
"CalendarEventResponse_Tentative": "Tentative",
"CalendarEventDetails_Attachments": "Attachments",
"CalendarEventDetails_Details": "Details",
"CalendarEventDetails_EditSeries": "Edit Series",
"CalendarEventDetails_Editing": "Editing",
"CalendarEventDetails_InviteSomeone": "Invite someone",
"CalendarEventDetails_JoinOnline": "Join Online",
"CalendarEventDetails_People": "People",
"CalendarEventDetails_ReadOnlyEvent": "Read-only event",
"CalendarEventDetails_Reminder": "Reminder",
"CalendarEventDetails_ShowAs": "Show as",
"CalendarItem_DetailsPopup_JoinOnline": "Join online",
"CalendarItem_DetailsPopup_ViewEventButton": "View event",
"CalendarItem_DetailsPopup_ViewSeriesButton": "View series",
@@ -133,6 +146,8 @@
"DialogMessage_CreateLinkedAccountTitle": "Account Link Name",
"DialogMessage_DeleteAccountConfirmationMessage": "Delete {0}?",
"DialogMessage_DeleteAccountConfirmationTitle": "All data associated with this account will be deleted from disk permanently.",
"DialogMessage_DeleteRecurringSeriesMessage": "This will delete all events in the series. Do you want to continue?",
"DialogMessage_DeleteRecurringSeriesTitle": "Delete Recurring Series",
"DialogMessage_DiscardDraftConfirmationMessage": "This draft will be discarded. Do you want to continue?",
"DialogMessage_DiscardDraftConfirmationTitle": "Discard Draft",
"DialogMessage_EmptySubjectConfirmation": "Missing Subject",
@@ -283,9 +283,12 @@ public static class OutlookIntegratorExtensions
};
}
public static CalendarEventAttendee CreateAttendee(this Attendee attendee, Guid calendarItemId)
public static CalendarEventAttendee CreateAttendee(this Attendee attendee, Guid calendarItemId, string organizerEmail = null)
{
bool isOrganizer = attendee?.Status?.Response == ResponseType.Organizer;
// Check if this attendee is the organizer by comparing email addresses
bool isOrganizer = !string.IsNullOrEmpty(organizerEmail) &&
!string.IsNullOrEmpty(attendee?.EmailAddress?.Address) &&
string.Equals(attendee.EmailAddress.Address, organizerEmail, StringComparison.OrdinalIgnoreCase);
var eventAttendee = new CalendarEventAttendee()
{
@@ -170,7 +170,9 @@ public class OutlookChangeProcessor(IDatabaseService databaseService,
List<CalendarEventAttendee> attendees = null;
if (calendarEvent.Attendees != null)
{
attendees = calendarEvent.Attendees.Select(a => a.CreateAttendee(savingItemId)).ToList();
// Pass the organizer's email address to properly identify the organizer in the attendees list
string organizerEmail = calendarEvent.Organizer?.EmailAddress?.Address;
attendees = calendarEvent.Attendees.Select(a => a.CreateAttendee(savingItemId, organizerEmail)).ToList();
}
// Use CalendarService to create or update the event
@@ -0,0 +1,32 @@
using CommunityToolkit.Mvvm.Messaging;
using Wino.Core.Domain.Entities.Calendar;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Requests;
using Wino.Messaging.Client.Calendar;
namespace Wino.Core.Requests.Calendar;
/// <summary>
/// Request to delete a calendar event on the server.
/// </summary>
public record DeleteCalendarEventRequest(CalendarItem Item) : CalendarRequestBase(Item)
{
public override CalendarSynchronizerOperation Operation => CalendarSynchronizerOperation.DeleteEvent;
/// <summary>
/// After successful deletion, resync to confirm the event was removed.
/// </summary>
public override int ResynchronizationDelay => 2000;
public override void ApplyUIChanges()
{
// Notify UI that the event was deleted
WeakReferenceMessenger.Default.Send(new CalendarItemDeleted(Item));
}
public override void RevertUIChanges()
{
// If deletion fails, we should notify the UI to add it back
WeakReferenceMessenger.Default.Send(new CalendarItemAdded(Item));
}
}
+2 -2
View File
@@ -140,9 +140,9 @@ public class WinoRequestDelegator : IWinoRequestDelegator
IRequestBase request = calendarPreparationRequest.Operation switch
{
CalendarSynchronizerOperation.CreateEvent => new CreateCalendarEventRequest(calendarPreparationRequest.CalendarItem, calendarPreparationRequest.Attendees),
// Future support for update and delete operations
CalendarSynchronizerOperation.DeleteEvent => new DeleteCalendarEventRequest(calendarPreparationRequest.CalendarItem),
// Future support for update operations
// CalendarSynchronizerOperation.UpdateEvent => new UpdateCalendarEventRequest(calendarPreparationRequest.CalendarItem, calendarPreparationRequest.Attendees),
// CalendarSynchronizerOperation.DeleteEvent => new DeleteCalendarEventRequest(calendarPreparationRequest.CalendarItem),
_ => throw new NotImplementedException($"Calendar operation {calendarPreparationRequest.Operation} is not implemented yet.")
};
@@ -42,7 +42,7 @@ public static class CalendarXamlHelpers
public static string GetRecurrenceString(CalendarItemViewModel calendarItemViewModel)
{
if (calendarItemViewModel == null || !calendarItemViewModel.IsRecurringChild) return string.Empty;
if (calendarItemViewModel == null || string.IsNullOrEmpty(calendarItemViewModel.CalendarItem.Recurrence)) return string.Empty;
// Parse recurrence rules
var calendarEvent = new CalendarEvent
@@ -103,4 +103,10 @@ public static class CalendarXamlHelpers
return XamlHelpers.GetPlaccementModeForCalendarType(calendarDisplayType);
}
/// <summary>
/// Returns true if the calendar item has an online meeting link.
/// </summary>
public static bool HasOnlineMeetingLink(CalendarItemViewModel calendarItemViewModel)
=> calendarItemViewModel != null && !string.IsNullOrEmpty(calendarItemViewModel.CalendarItem?.HtmlLink);
}
@@ -9,6 +9,7 @@
xmlns:coreControls="using:Wino.Mail.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:domain="using:Wino.Core.Domain"
xmlns:enums="using:Wino.Core.Domain.Enums"
xmlns:helpers="using:Wino.Helpers"
xmlns:local="using:Wino.Calendar.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@@ -61,14 +62,14 @@
OverflowButtonVisibility="Auto">
<AppBarToggleButton
x:Name="ReadOnlyToggle"
Content="Read-only event"
Content="{x:Bind domain:Translator.CalendarEventDetails_ReadOnlyEvent}"
IsChecked="True" />
<AppBarButton Label="{x:Bind domain:Translator.Buttons_Save}">
<AppBarButton Command="{x:Bind ViewModel.SaveCommand}" Label="{x:Bind domain:Translator.Buttons_Save}">
<AppBarButton.Icon>
<coreControls:WinoFontIcon Icon="Save" />
</AppBarButton.Icon>
</AppBarButton>
<AppBarButton Label="{x:Bind domain:Translator.Buttons_Delete}">
<AppBarButton Command="{x:Bind ViewModel.DeleteCommand}" Label="{x:Bind domain:Translator.Buttons_Delete}">
<AppBarButton.Icon>
<coreControls:WinoFontIcon Icon="Delete" />
</AppBarButton.Icon>
@@ -77,7 +78,10 @@
<AppBarSeparator />
<!-- Join Online -->
<AppBarButton Label="Join Online">
<AppBarButton
Command="{x:Bind ViewModel.JoinOnlineCommand}"
IsEnabled="{x:Bind calendarHelpers:CalendarXamlHelpers.HasOnlineMeetingLink(ViewModel.CurrentEvent), Mode=OneWay}"
Label="{x:Bind domain:Translator.CalendarEventDetails_JoinOnline}">
<AppBarButton.Icon>
<coreControls:WinoFontIcon Icon="EventJoinOnline" />
</AppBarButton.Icon>
@@ -85,27 +89,30 @@
<AppBarSeparator />
<!-- Join Options -->
<AppBarButton Label="Accept">
<!-- Response Options -->
<AppBarButton
Command="{x:Bind ViewModel.RespondCommand}"
CommandParameter="{x:Bind enums:CalendarItemStatus.Confirmed}"
Label="{x:Bind domain:Translator.CalendarEventResponse_Accept}">
<AppBarButton.Icon>
<coreControls:WinoFontIcon Foreground="#527257" Icon="EventAccept" />
</AppBarButton.Icon>
</AppBarButton>
<AppBarButton Label="Tentative">
<AppBarButton
Command="{x:Bind ViewModel.RespondCommand}"
CommandParameter="{x:Bind enums:CalendarItemStatus.Tentative}"
Label="{x:Bind domain:Translator.CalendarEventResponse_Tentative}">
<AppBarButton.Icon>
<coreControls:WinoFontIcon Foreground="#805682" Icon="EventTentative" />
</AppBarButton.Icon>
</AppBarButton>
<AppBarButton Label="Decline">
<AppBarButton.Icon>
<PathIcon Data="F1 M 10.253906 9.375 L 16.064453 15.185547 C 16.18815 15.309245 16.25 15.455729 16.25 15.625 C 16.25 15.794271 16.18815 15.940756 16.064453 16.064453 C 15.940754 16.188152 15.79427 16.25 15.625 16.25 C 15.455729 16.25 15.309244 16.188152 15.185547 16.064453 L 9.375 10.253906 L 3.564453 16.064453 C 3.440755 16.188152 3.294271 16.25 3.125 16.25 C 2.955729 16.25 2.809245 16.188152 2.685547 16.064453 C 2.561849 15.940756 2.5 15.794271 2.5 15.625 C 2.5 15.455729 2.561849 15.309245 2.685547 15.185547 L 8.496094 9.375 L 2.685547 3.564453 C 2.561849 3.440756 2.5 3.294271 2.5 3.125 C 2.5 2.95573 2.561849 2.809246 2.685547 2.685547 C 2.809245 2.56185 2.955729 2.5 3.125 2.5 C 3.294271 2.5 3.440755 2.56185 3.564453 2.685547 L 9.375 8.496094 L 15.185547 2.685547 C 15.309244 2.56185 15.455729 2.5 15.625 2.5 C 15.79427 2.5 15.940754 2.56185 16.064453 2.685547 C 16.18815 2.809246 16.25 2.95573 16.25 3.125 C 16.25 3.294271 16.18815 3.440756 16.064453 3.564453 Z " Foreground="#d94b4e" />
</AppBarButton.Icon>
</AppBarButton>
<AppBarButton Label="Respond">
<AppBarButton
Command="{x:Bind ViewModel.RespondCommand}"
CommandParameter="{x:Bind enums:CalendarItemStatus.Cancelled}"
Label="{x:Bind domain:Translator.CalendarEventResponse_Decline}">
<AppBarButton.Icon>
<coreControls:WinoFontIcon Foreground="#805682" Icon="EventRespond" />
</AppBarButton.Icon>
@@ -116,8 +123,11 @@
<!-- Show as -->
<AppBarElementContainer>
<StackPanel Style="{StaticResource ActionBarElementContainerStackStyle}">
<TextBlock VerticalAlignment="Center" Text="Show as" />
<ComboBox Width="150" />
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.CalendarEventDetails_ShowAs}" />
<ComboBox
Width="150"
ItemsSource="{x:Bind ViewModel.ShowAsOptions}"
SelectedItem="{x:Bind ViewModel.SelectedShowAs, Mode=TwoWay}" />
</StackPanel>
</AppBarElementContainer>
@@ -125,7 +135,7 @@
<AppBarElementContainer>
<StackPanel Style="{StaticResource ActionBarElementContainerStackStyle}">
<coreControls:WinoFontIcon FontSize="16" Icon="Reminder" />
<TextBlock VerticalAlignment="Center" Text="Reminder" />
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.CalendarEventDetails_Reminder}" />
<ComboBox Width="150" />
</StackPanel>
</AppBarElementContainer>
@@ -133,7 +143,7 @@
<AppBarSeparator />
<!-- Edit Series -->
<AppBarButton Label="Edit Series">
<AppBarButton Label="{x:Bind domain:Translator.CalendarEventDetails_EditSeries}" Visibility="{x:Bind ViewModel.CanEditSeries, Mode=OneWay}">
<AppBarButton.Icon>
<coreControls:WinoFontIcon Icon="EventEditSeries" />
</AppBarButton.Icon>
@@ -163,7 +173,7 @@
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Style="{StaticResource EventDetailsPanelTitleStyle}" Text="Details" />
<TextBlock Style="{StaticResource EventDetailsPanelTitleStyle}" Text="{x:Bind domain:Translator.CalendarEventDetails_Details}" />
<Grid Grid.Row="1">
<!-- Read-Only Event -->
@@ -194,7 +204,7 @@
<Grid
Grid.Row="3"
ColumnSpacing="6"
Visibility="{x:Bind ViewModel.CurrentEvent.IsRecurringEvent}">
Visibility="{x:Bind ViewModel.CurrentEvent.IsRecurringParent}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
@@ -212,7 +222,7 @@
<!-- Editable Event -->
<Grid Visibility="{x:Bind helpers:XamlHelpers.ReverseVisibilityConverter(ReadOnlyDetailsGrid.Visibility), Mode=OneWay}">
<TextBlock Text="editing" />
<TextBlock Text="{x:Bind domain:Translator.CalendarEventDetails_Editing}" />
</Grid>
</Grid>
</Grid>
@@ -227,7 +237,7 @@
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Style="{StaticResource EventDetailsPanelTitleStyle}" Text="People" />
<TextBlock Style="{StaticResource EventDetailsPanelTitleStyle}" Text="{x:Bind domain:Translator.CalendarEventDetails_People}" />
<Grid Grid.Row="1" RowSpacing="12">
<Grid.RowDefinitions>
@@ -237,7 +247,7 @@
<AutoSuggestBox
Margin="6,0"
BorderThickness="0"
PlaceholderText="Invite someone" />
PlaceholderText="{x:Bind domain:Translator.CalendarEventDetails_InviteSomeone}" />
<ListView
Grid.Row="1"
@@ -287,7 +297,7 @@
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Style="{StaticResource EventDetailsPanelTitleStyle}" Text="Attachments" />
<TextBlock Style="{StaticResource EventDetailsPanelTitleStyle}" Text="{x:Bind domain:Translator.CalendarEventDetails_Attachments}" />
</Grid>
</Grid>