RSVP options.
This commit is contained in:
@@ -66,4 +66,5 @@ public partial class AccountCalendarViewModel : ObservableObject, IAccountCalend
|
||||
set => SetProperty(AccountCalendar.RemoteCalendarId, value, AccountCalendar, (u, r) => u.RemoteCalendarId = r);
|
||||
}
|
||||
public Guid Id { get => ((IAccountCalendar)AccountCalendar).Id; set => ((IAccountCalendar)AccountCalendar).Id = value; }
|
||||
public MailAccount MailAccount { get => MailAccount; set => MailAccount = value; }
|
||||
}
|
||||
|
||||
@@ -36,7 +36,10 @@ public partial class EventDetailsPageViewModel : CalendarBaseViewModel
|
||||
[NotifyPropertyChangedFor(nameof(CanViewSeries))]
|
||||
[NotifyPropertyChangedFor(nameof(CanEditSeries))]
|
||||
[NotifyPropertyChangedFor(nameof(IsCurrentUserOrganizer))]
|
||||
[NotifyPropertyChangedFor(nameof(CurrentRsvpText))]
|
||||
[NotifyPropertyChangedFor(nameof(CurrentRsvpStatus))]
|
||||
public partial CalendarItemViewModel CurrentEvent { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial CalendarItemViewModel SeriesParent { get; set; }
|
||||
[ObservableProperty]
|
||||
@@ -80,6 +83,45 @@ public partial class EventDetailsPageViewModel : CalendarBaseViewModel
|
||||
|
||||
#endregion
|
||||
|
||||
#region RSVP Panel
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsRsvpPanelVisible { get; set; }
|
||||
|
||||
public bool IncludeRsvpMessage => !string.IsNullOrEmpty(RsvpMessage);
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string RsvpMessage { get; set; } = string.Empty;
|
||||
|
||||
public ObservableCollection<RsvpStatusOption> RsvpStatusOptions { get; } = new ObservableCollection<RsvpStatusOption>();
|
||||
|
||||
public CalendarItemStatus CurrentRsvpStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
return CurrentEvent?.CalendarItem?.Status ?? CalendarItemStatus.NotResponded;
|
||||
}
|
||||
}
|
||||
|
||||
public string CurrentRsvpText
|
||||
{
|
||||
get
|
||||
{
|
||||
if (CurrentEvent?.CalendarItem == null) return Translator.CalendarEventResponse_Accept;
|
||||
|
||||
return CurrentEvent.CalendarItem.Status switch
|
||||
{
|
||||
CalendarItemStatus.Accepted => Translator.CalendarEventResponse_AcceptedResponse,
|
||||
CalendarItemStatus.Tentative => Translator.CalendarEventResponse_TentativeResponse,
|
||||
CalendarItemStatus.Cancelled => Translator.CalendarEventResponse_DeclinedResponse,
|
||||
CalendarItemStatus.NotResponded => Translator.CalendarEventResponse_NotResponded,
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public EventDetailsPageViewModel(ICalendarService calendarService,
|
||||
INativeAppService nativeAppService,
|
||||
IPreferencesService preferencesService,
|
||||
@@ -95,6 +137,11 @@ public partial class EventDetailsPageViewModel : CalendarBaseViewModel
|
||||
_navigationService = navigationService;
|
||||
|
||||
CurrentSettings = _preferencesService.GetCurrentCalendarSettings();
|
||||
|
||||
// Initialize RSVP status options
|
||||
RsvpStatusOptions.Add(new RsvpStatusOption(CalendarItemStatus.Accepted));
|
||||
RsvpStatusOptions.Add(new RsvpStatusOption(CalendarItemStatus.Tentative));
|
||||
RsvpStatusOptions.Add(new RsvpStatusOption(CalendarItemStatus.Cancelled));
|
||||
}
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
@@ -109,6 +156,17 @@ public partial class EventDetailsPageViewModel : CalendarBaseViewModel
|
||||
await LoadCalendarItemTargetAsync(args);
|
||||
}
|
||||
|
||||
protected override void OnCalendarItemDeleted(CalendarItem calendarItem)
|
||||
{
|
||||
base.OnCalendarItemDeleted(calendarItem);
|
||||
|
||||
// If the current event was deleted, navigate back
|
||||
if (CurrentEvent?.CalendarItem?.Id == calendarItem.Id || CurrentEvent?.CalendarItem.RecurringCalendarItemId == calendarItem.Id)
|
||||
{
|
||||
_navigationService.GoBack();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadCalendarItemTargetAsync(CalendarItemTarget target)
|
||||
{
|
||||
try
|
||||
@@ -265,11 +323,68 @@ public partial class EventDetailsPageViewModel : CalendarBaseViewModel
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task RespondAsync(CalendarItemStatus status)
|
||||
private void ToggleRsvpPanel()
|
||||
{
|
||||
IsRsvpPanelVisible = !IsRsvpPanelVisible;
|
||||
|
||||
if (IsRsvpPanelVisible && CurrentEvent?.CalendarItem != null)
|
||||
{
|
||||
// Initialize selection based on current status
|
||||
foreach (var item in RsvpStatusOptions)
|
||||
{
|
||||
item.IsSelected = CurrentEvent?.CalendarItem?.Status == item.Status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void CloseRsvpPanel()
|
||||
{
|
||||
IsRsvpPanelVisible = false;
|
||||
RsvpMessage = string.Empty;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SendRsvpResponse(AttendeeStatus status)
|
||||
{
|
||||
if (CurrentEvent == null) return;
|
||||
|
||||
// TODO: Implement response
|
||||
try
|
||||
{
|
||||
// Get the optional response message if user wants to include it
|
||||
var responseMessage = IncludeRsvpMessage ? RsvpMessage : null;
|
||||
|
||||
// Map status to operation
|
||||
CalendarSynchronizerOperation operation = status switch
|
||||
{
|
||||
AttendeeStatus.Accepted => CalendarSynchronizerOperation.AcceptEvent,
|
||||
AttendeeStatus.Tentative => CalendarSynchronizerOperation.TentativeEvent,
|
||||
AttendeeStatus.Declined => CalendarSynchronizerOperation.DeclineEvent,
|
||||
_ => throw new InvalidOperationException($"Invalid RSVP status: {status}")
|
||||
};
|
||||
|
||||
// Create preparation request with the optional message
|
||||
var preparationRequest = new CalendarOperationPreparationRequest(
|
||||
operation,
|
||||
CurrentEvent.CalendarItem,
|
||||
null,
|
||||
responseMessage);
|
||||
|
||||
await _winoRequestDelegator.ExecuteAsync(preparationRequest);
|
||||
|
||||
OnPropertyChanged(nameof(CurrentRsvpText));
|
||||
OnPropertyChanged(nameof(CurrentRsvpStatus));
|
||||
|
||||
CloseRsvpPanel();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error sending RSVP response: {ex.Message}");
|
||||
_dialogService.InfoBarMessage(
|
||||
Translator.Info_AttachmentSaveFailedTitle,
|
||||
ex.Message,
|
||||
InfoBarMessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -323,3 +438,30 @@ public partial class ReminderOption : ObservableObject
|
||||
IsCustom = isCustom;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class RsvpStatusOption : ObservableObject
|
||||
{
|
||||
public CalendarItemStatus Status { get; }
|
||||
|
||||
public string StatusText
|
||||
{
|
||||
get
|
||||
{
|
||||
return Status switch
|
||||
{
|
||||
CalendarItemStatus.Accepted => Translator.CalendarEventResponse_Accept,
|
||||
CalendarItemStatus.Tentative => Translator.CalendarEventResponse_Tentative,
|
||||
CalendarItemStatus.Cancelled => Translator.CalendarEventResponse_Decline,
|
||||
_ => Translator.CalendarEventResponse_Accept
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsSelected { get; set; }
|
||||
|
||||
public RsvpStatusOption(CalendarItemStatus status)
|
||||
{
|
||||
Status = status;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,18 +258,32 @@
|
||||
Height="40"
|
||||
DisplayName="{x:Bind Name}" />
|
||||
|
||||
<!-- TODO: Organizer -->
|
||||
<Grid Grid.Column="1">
|
||||
<Grid Grid.Column="1" RowSpacing="4">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock FontWeight="SemiBold" Text="{x:Bind Name}" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
FontSize="13"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{x:Bind Email}" />
|
||||
<Border
|
||||
Grid.Row="2"
|
||||
Padding="6,2"
|
||||
HorizontalAlignment="Left"
|
||||
Background="{ThemeResource AccentFillColorDefaultBrush}"
|
||||
CornerRadius="4"
|
||||
Visibility="{x:Bind IsOrganizer}">
|
||||
<TextBlock
|
||||
FontSize="11"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextOnAccentFillColorPrimaryBrush}"
|
||||
Text="{x:Bind domain:Translator.CalendarEventDetails_Organizer}" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using SQLite;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Core.Domain.Entities.Calendar;
|
||||
@@ -22,4 +23,7 @@ public class AccountCalendar : IAccountCalendar
|
||||
public string TextColorHex { get; set; }
|
||||
public string BackgroundColorHex { get; set; }
|
||||
public string TimeZone { get; set; }
|
||||
|
||||
[Ignore]
|
||||
public MailAccount MailAccount { get; set; }
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
public enum CalendarItemStatus
|
||||
{
|
||||
NotResponded,
|
||||
Confirmed,
|
||||
Accepted,
|
||||
Tentative,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@ public enum CalendarSynchronizerOperation
|
||||
CreateEvent,
|
||||
UpdateEvent,
|
||||
DeleteEvent,
|
||||
AcceptEvent,
|
||||
DeclineEvent,
|
||||
TentativeEvent,
|
||||
}
|
||||
|
||||
// UI requests
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
|
||||
namespace Wino.Core.Domain.Interfaces;
|
||||
|
||||
@@ -12,4 +13,5 @@ public interface IAccountCalendar
|
||||
string RemoteCalendarId { get; set; }
|
||||
bool IsExtended { get; set; }
|
||||
Guid Id { get; set; }
|
||||
MailAccount MailAccount { get; set; }
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ namespace Wino.Core.Domain.Models.Calendar;
|
||||
/// <summary>
|
||||
/// Encapsulates the options for preparing calendar operation requests.
|
||||
/// </summary>
|
||||
/// <param name="Operation">Calendar operation to execute (Create, Update, Delete).</param>
|
||||
/// <param name="Operation">Calendar operation to execute (Create, Update, Delete, Accept, Decline, Tentative).</param>
|
||||
/// <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);
|
||||
/// <param name="ResponseMessage">Optional message to include with event responses (Accept, Decline, Tentative).</param>
|
||||
public record CalendarOperationPreparationRequest(CalendarSynchronizerOperation Operation, CalendarItem CalendarItem, List<CalendarEventAttendee> Attendees, string ResponseMessage = null);
|
||||
|
||||
@@ -84,14 +84,26 @@
|
||||
"CalendarDisplayOptions_Color": "Color",
|
||||
"CalendarDisplayOptions_Expand": "Expand",
|
||||
"CalendarEventResponse_Accept": "Accept",
|
||||
"CalendarEventResponse_AcceptedResponse": "Accepted",
|
||||
"CalendarEventResponse_Decline": "Decline",
|
||||
"CalendarEventResponse_DeclinedResponse": "Declined",
|
||||
"CalendarEventResponse_NotResponded": "Not Responded",
|
||||
"CalendarEventResponse_Tentative": "Tentative",
|
||||
"CalendarEventResponse_TentativeResponse": "Tentative",
|
||||
"CalendarEventRsvpPanel_Accept": "Accept",
|
||||
"CalendarEventRsvpPanel_AddMessage": "Add a message to your response... (optional)",
|
||||
"CalendarEventRsvpPanel_Decline": "Decline",
|
||||
"CalendarEventRsvpPanel_Message": "Message",
|
||||
"CalendarEventRsvpPanel_SendReplyMessage": "Send a reply message",
|
||||
"CalendarEventRsvpPanel_Tentative": "Tentative",
|
||||
"CalendarEventRsvpPanel_Title": "Response Options",
|
||||
"CalendarEventDetails_Attachments": "Attachments",
|
||||
"CalendarEventDetails_Details": "Details",
|
||||
"CalendarEventDetails_EditSeries": "Edit Series",
|
||||
"CalendarEventDetails_Editing": "Editing",
|
||||
"CalendarEventDetails_InviteSomeone": "Invite someone",
|
||||
"CalendarEventDetails_JoinOnline": "Join Online",
|
||||
"CalendarEventDetails_Organizer": "Organizer",
|
||||
"CalendarEventDetails_People": "People",
|
||||
"CalendarEventDetails_ReadOnlyEvent": "Read-only event",
|
||||
"CalendarEventDetails_Reminder": "Reminder",
|
||||
|
||||
@@ -349,10 +349,10 @@ public class GmailChangeProcessor : DefaultChangeProcessor, IGmailChangeProcesso
|
||||
{
|
||||
return status switch
|
||||
{
|
||||
"confirmed" => CalendarItemStatus.Confirmed,
|
||||
"confirmed" => CalendarItemStatus.Accepted,
|
||||
"tentative" => CalendarItemStatus.Tentative,
|
||||
"cancelled" => CalendarItemStatus.Cancelled,
|
||||
_ => CalendarItemStatus.Confirmed
|
||||
_ => CalendarItemStatus.Accepted
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ public class OutlookChangeProcessor(IDatabaseService databaseService,
|
||||
break;
|
||||
case ResponseType.Accepted:
|
||||
case ResponseType.Organizer:
|
||||
savingItem.Status = CalendarItemStatus.Confirmed;
|
||||
savingItem.Status = CalendarItemStatus.Accepted;
|
||||
break;
|
||||
case ResponseType.Declined:
|
||||
savingItem.Status = CalendarItemStatus.Cancelled;
|
||||
@@ -164,7 +164,7 @@ public class OutlookChangeProcessor(IDatabaseService databaseService,
|
||||
}
|
||||
else
|
||||
{
|
||||
savingItem.Status = CalendarItemStatus.Confirmed;
|
||||
savingItem.Status = CalendarItemStatus.Accepted;
|
||||
}
|
||||
|
||||
// Prepare attendees list
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
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 accept a calendar event invitation on the server.
|
||||
/// The calendar item status should be updated locally before queuing this request.
|
||||
/// </summary>
|
||||
public record AcceptEventRequest(CalendarItem Item, string ResponseMessage = null) : CalendarRequestBase(Item)
|
||||
{
|
||||
private readonly CalendarItemStatus _previousStatus = Item.Status;
|
||||
|
||||
public override CalendarSynchronizerOperation Operation => CalendarSynchronizerOperation.AcceptEvent;
|
||||
|
||||
/// <summary>
|
||||
/// After successful acceptance, we need to resync to get updated status.
|
||||
/// </summary>
|
||||
public override int ResynchronizationDelay => 2000;
|
||||
|
||||
public override void ApplyUIChanges()
|
||||
{
|
||||
// Update the item status locally
|
||||
Item.Status = CalendarItemStatus.Accepted;
|
||||
|
||||
// Notify UI that the event status was updated
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemUpdated(Item));
|
||||
}
|
||||
|
||||
public override void RevertUIChanges()
|
||||
{
|
||||
// If acceptance fails, revert to the previous status
|
||||
Item.Status = _previousStatus;
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemUpdated(Item));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
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 decline a calendar event invitation on the server.
|
||||
/// The calendar item status should be updated locally before queuing this request.
|
||||
/// </summary>
|
||||
public record DeclineEventRequest(CalendarItem Item, string ResponseMessage = null) : CalendarRequestBase(Item)
|
||||
{
|
||||
private readonly CalendarItemStatus _previousStatus = Item.Status;
|
||||
|
||||
public override CalendarSynchronizerOperation Operation => CalendarSynchronizerOperation.DeclineEvent;
|
||||
|
||||
/// <summary>
|
||||
/// After successful decline, we need to resync to get updated status.
|
||||
/// </summary>
|
||||
public override int ResynchronizationDelay => 2000;
|
||||
|
||||
public override void ApplyUIChanges()
|
||||
{
|
||||
// Update the item status locally
|
||||
Item.Status = CalendarItemStatus.Cancelled;
|
||||
|
||||
// Notify UI that the event status was updated
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemUpdated(Item));
|
||||
}
|
||||
|
||||
public override void RevertUIChanges()
|
||||
{
|
||||
// If decline fails, revert to the previous status
|
||||
Item.Status = _previousStatus;
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemUpdated(Item));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
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>
|
||||
/// Outlook-specific request to decline a calendar event invitation.
|
||||
/// In Outlook, declined events are removed from the calendar by the API after synchronization,
|
||||
/// so this request sends a delete notification to remove the event from the UI.
|
||||
/// </summary>
|
||||
public record OutlookDeclineEventRequest(CalendarItem Item, string ResponseMessage = null) : CalendarRequestBase(Item)
|
||||
{
|
||||
private readonly CalendarItemStatus _previousStatus = Item.Status;
|
||||
|
||||
public override CalendarSynchronizerOperation Operation => CalendarSynchronizerOperation.DeclineEvent;
|
||||
|
||||
/// <summary>
|
||||
/// After successful decline, we need to resync to confirm the event is removed.
|
||||
/// </summary>
|
||||
public override int ResynchronizationDelay => 2000;
|
||||
|
||||
public override void ApplyUIChanges()
|
||||
{
|
||||
// In Outlook, declined events are deleted from the calendar after sync
|
||||
// Send deleted message to remove from UI immediately
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemDeleted(Item));
|
||||
}
|
||||
|
||||
public override void RevertUIChanges()
|
||||
{
|
||||
// If decline fails, restore the previous status and re-add the event
|
||||
Item.Status = _previousStatus;
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemAdded(Item));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
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 tentatively accept a calendar event invitation on the server.
|
||||
/// The calendar item status should be updated locally before queuing this request.
|
||||
/// </summary>
|
||||
public record TentativeEventRequest(CalendarItem Item, string ResponseMessage = null) : CalendarRequestBase(Item)
|
||||
{
|
||||
private readonly CalendarItemStatus _previousStatus = Item.Status;
|
||||
|
||||
public override CalendarSynchronizerOperation Operation => CalendarSynchronizerOperation.TentativeEvent;
|
||||
|
||||
/// <summary>
|
||||
/// After successful tentative acceptance, we need to resync to get updated status.
|
||||
/// </summary>
|
||||
public override int ResynchronizationDelay => 2000;
|
||||
|
||||
public override void ApplyUIChanges()
|
||||
{
|
||||
// Update the item status locally
|
||||
Item.Status = CalendarItemStatus.Tentative;
|
||||
|
||||
// Notify UI that the event status was updated
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemUpdated(Item));
|
||||
}
|
||||
|
||||
public override void RevertUIChanges()
|
||||
{
|
||||
// If tentative acceptance fails, revert to the previous status
|
||||
Item.Status = _previousStatus;
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemUpdated(Item));
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Serilog;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Entities.Calendar;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Exceptions;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
@@ -141,6 +142,9 @@ public class WinoRequestDelegator : IWinoRequestDelegator
|
||||
{
|
||||
CalendarSynchronizerOperation.CreateEvent => new CreateCalendarEventRequest(calendarPreparationRequest.CalendarItem, calendarPreparationRequest.Attendees),
|
||||
CalendarSynchronizerOperation.DeleteEvent => new DeleteCalendarEventRequest(calendarPreparationRequest.CalendarItem),
|
||||
CalendarSynchronizerOperation.AcceptEvent => new AcceptEventRequest(calendarPreparationRequest.CalendarItem, calendarPreparationRequest.ResponseMessage),
|
||||
CalendarSynchronizerOperation.DeclineEvent => CreateDeclineRequest(calendarPreparationRequest.CalendarItem, calendarPreparationRequest.ResponseMessage),
|
||||
CalendarSynchronizerOperation.TentativeEvent => new TentativeEventRequest(calendarPreparationRequest.CalendarItem, calendarPreparationRequest.ResponseMessage),
|
||||
// Future support for update operations
|
||||
// CalendarSynchronizerOperation.UpdateEvent => new UpdateCalendarEventRequest(calendarPreparationRequest.CalendarItem, calendarPreparationRequest.Attendees),
|
||||
_ => throw new NotImplementedException($"Calendar operation {calendarPreparationRequest.Operation} is not implemented yet.")
|
||||
@@ -150,6 +154,18 @@ public class WinoRequestDelegator : IWinoRequestDelegator
|
||||
await QueueCalendarSynchronizationAsync(calendarPreparationRequest.CalendarItem.AssignedCalendar.AccountId);
|
||||
}
|
||||
|
||||
private IRequestBase CreateDeclineRequest(CalendarItem calendarItem, string responseMessage)
|
||||
{
|
||||
// For Outlook accounts, declined events are deleted by the server after synchronization.
|
||||
// Use OutlookDeclineEventRequest to handle UI removal.
|
||||
if (calendarItem.AssignedCalendar?.MailAccount?.ProviderType == MailProviderType.Outlook)
|
||||
{
|
||||
return new OutlookDeclineEventRequest(calendarItem, responseMessage);
|
||||
}
|
||||
|
||||
return new DeclineEventRequest(calendarItem, responseMessage);
|
||||
}
|
||||
|
||||
private async Task QueueRequestAsync(IRequestBase request, Guid accountId)
|
||||
{
|
||||
// Don't trigger synchronization for individual requests - we'll trigger it once for all requests
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Google;
|
||||
using Google.Apis.Calendar.v3;
|
||||
using Google.Apis.Calendar.v3.Data;
|
||||
using Google.Apis.Gmail.v1;
|
||||
using Google.Apis.Gmail.v1.Data;
|
||||
@@ -1648,7 +1649,7 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
|
||||
Summary = calendarItem.Title,
|
||||
Description = calendarItem.Description,
|
||||
Location = calendarItem.Location,
|
||||
Status = calendarItem.Status == CalendarItemStatus.Confirmed ? "confirmed" : "tentative"
|
||||
Status = calendarItem.Status == CalendarItemStatus.Accepted ? "confirmed" : "tentative"
|
||||
};
|
||||
|
||||
// Set start and end time
|
||||
@@ -1696,6 +1697,125 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
|
||||
return [new HttpRequestBundle<IClientServiceRequest>(insertRequest, request)];
|
||||
}
|
||||
|
||||
public override List<IRequestBundle<IClientServiceRequest>> AcceptEvent(AcceptEventRequest request)
|
||||
{
|
||||
var calendarItem = request.Item;
|
||||
var calendar = calendarItem.AssignedCalendar;
|
||||
|
||||
if (calendar == null)
|
||||
{
|
||||
throw new InvalidOperationException("Calendar item must have an assigned calendar");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(calendarItem.RemoteEventId))
|
||||
{
|
||||
throw new InvalidOperationException("Cannot accept event without remote event ID");
|
||||
}
|
||||
|
||||
// For Gmail, we need to patch the event with the user's response status
|
||||
// Get the current user's email from the account
|
||||
var userEmail = Account.Address;
|
||||
|
||||
// Create a patch event to update only the attendee response
|
||||
var patchEvent = new Event();
|
||||
|
||||
// We need to get the event first to update the specific attendee
|
||||
// However, for efficiency, we'll use the patch method with sendUpdates parameter
|
||||
var patchRequest = _calendarService.Events.Patch(new Event
|
||||
{
|
||||
// The API will handle updating the current user's attendee status
|
||||
Attendees = new List<EventAttendee>
|
||||
{
|
||||
new EventAttendee
|
||||
{
|
||||
Email = userEmail,
|
||||
ResponseStatus = "accepted"
|
||||
}
|
||||
}
|
||||
}, calendar.RemoteCalendarId, calendarItem.RemoteEventId);
|
||||
|
||||
// Send updates to other attendees if there's a message
|
||||
patchRequest.SendUpdates = !string.IsNullOrEmpty(request.ResponseMessage)
|
||||
? Google.Apis.Calendar.v3.EventsResource.PatchRequest.SendUpdatesEnum.All
|
||||
: Google.Apis.Calendar.v3.EventsResource.PatchRequest.SendUpdatesEnum.None;
|
||||
|
||||
return [new HttpRequestBundle<IClientServiceRequest>(patchRequest, request)];
|
||||
}
|
||||
|
||||
public override List<IRequestBundle<IClientServiceRequest>> DeclineEvent(DeclineEventRequest request)
|
||||
{
|
||||
var calendarItem = request.Item;
|
||||
var calendar = calendarItem.AssignedCalendar;
|
||||
|
||||
if (calendar == null)
|
||||
{
|
||||
throw new InvalidOperationException("Calendar item must have an assigned calendar");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(calendarItem.RemoteEventId))
|
||||
{
|
||||
throw new InvalidOperationException("Cannot decline event without remote event ID");
|
||||
}
|
||||
|
||||
var userEmail = Account.Address;
|
||||
|
||||
var patchRequest = _calendarService.Events.Patch(new Event
|
||||
{
|
||||
Attendees = new List<EventAttendee>
|
||||
{
|
||||
new EventAttendee
|
||||
{
|
||||
Email = userEmail,
|
||||
ResponseStatus = "declined",
|
||||
Comment = request.ResponseMessage
|
||||
}
|
||||
}
|
||||
}, calendar.RemoteCalendarId, calendarItem.RemoteEventId);
|
||||
|
||||
patchRequest.SendUpdates = !string.IsNullOrEmpty(request.ResponseMessage)
|
||||
? Google.Apis.Calendar.v3.EventsResource.PatchRequest.SendUpdatesEnum.All
|
||||
: Google.Apis.Calendar.v3.EventsResource.PatchRequest.SendUpdatesEnum.None;
|
||||
|
||||
return [new HttpRequestBundle<IClientServiceRequest>(patchRequest, request)];
|
||||
}
|
||||
|
||||
public override List<IRequestBundle<IClientServiceRequest>> TentativeEvent(TentativeEventRequest request)
|
||||
{
|
||||
var calendarItem = request.Item;
|
||||
var calendar = calendarItem.AssignedCalendar;
|
||||
|
||||
if (calendar == null)
|
||||
{
|
||||
throw new InvalidOperationException("Calendar item must have an assigned calendar");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(calendarItem.RemoteEventId))
|
||||
{
|
||||
throw new InvalidOperationException("Cannot tentatively accept event without remote event ID");
|
||||
}
|
||||
|
||||
var userEmail = Account.Address;
|
||||
|
||||
var patchRequest = _calendarService.Events.Patch(new Event
|
||||
{
|
||||
Attendees = new List<EventAttendee>
|
||||
{
|
||||
new EventAttendee
|
||||
{
|
||||
Email = userEmail,
|
||||
ResponseStatus = "tentative",
|
||||
Comment = request.ResponseMessage
|
||||
}
|
||||
}
|
||||
}, calendar.RemoteCalendarId, calendarItem.RemoteEventId);
|
||||
|
||||
patchRequest.SendUpdates = !string.IsNullOrEmpty(request.ResponseMessage)
|
||||
? Google.Apis.Calendar.v3.EventsResource.PatchRequest.SendUpdatesEnum.All
|
||||
: Google.Apis.Calendar.v3.EventsResource.PatchRequest.SendUpdatesEnum.None;
|
||||
|
||||
return [new HttpRequestBundle<IClientServiceRequest>(patchRequest, request)];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override async Task KillSynchronizerAsync()
|
||||
|
||||
@@ -1680,6 +1680,9 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
|
||||
|
||||
foreach (var item in events)
|
||||
{
|
||||
// Declined events are returned as Deleted from the API.
|
||||
// There is no way to distinguish unfortunately atm.
|
||||
|
||||
if (IsResourceDeleted(item.AdditionalData))
|
||||
{
|
||||
await _outlookChangeProcessor.DeleteCalendarItemAsync(item.Id, calendar.Id).ConfigureAwait(false);
|
||||
@@ -1886,6 +1889,80 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
|
||||
return [new HttpRequestBundle<RequestInformation>(createRequest, request)];
|
||||
}
|
||||
|
||||
public override List<IRequestBundle<RequestInformation>> AcceptEvent(AcceptEventRequest request)
|
||||
{
|
||||
var calendarItem = request.Item;
|
||||
var calendar = calendarItem.AssignedCalendar;
|
||||
|
||||
if (calendar == null)
|
||||
{
|
||||
throw new InvalidOperationException("Calendar item must have an assigned calendar");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(calendarItem.RemoteEventId))
|
||||
{
|
||||
throw new InvalidOperationException("Cannot accept event without remote event ID");
|
||||
}
|
||||
|
||||
var acceptRequestInfo = _graphClient.Me.Calendars[calendar.RemoteCalendarId].Events[calendarItem.RemoteEventId].Accept.ToPostRequestInformation(new Microsoft.Graph.Me.Calendars.Item.Events.Item.Accept.AcceptPostRequestBody
|
||||
{
|
||||
Comment = request.ResponseMessage,
|
||||
SendResponse = !string.IsNullOrEmpty(request.ResponseMessage)
|
||||
});
|
||||
|
||||
return [new HttpRequestBundle<RequestInformation>(acceptRequestInfo, request)];
|
||||
}
|
||||
|
||||
public override List<IRequestBundle<RequestInformation>> OutlookDeclineEvent(OutlookDeclineEventRequest request)
|
||||
{
|
||||
var responseMessage = request.ResponseMessage;
|
||||
|
||||
var calendarItem = request.Item;
|
||||
var calendar = calendarItem.AssignedCalendar;
|
||||
|
||||
if (calendar == null)
|
||||
{
|
||||
throw new InvalidOperationException("Calendar item must have an assigned calendar");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(calendarItem.RemoteEventId))
|
||||
{
|
||||
throw new InvalidOperationException("Cannot decline event without remote event ID");
|
||||
}
|
||||
|
||||
var declineRequestInfo = _graphClient.Me.Calendars[calendar.RemoteCalendarId].Events[calendarItem.RemoteEventId].Decline.ToPostRequestInformation(new Microsoft.Graph.Me.Calendars.Item.Events.Item.Decline.DeclinePostRequestBody
|
||||
{
|
||||
Comment = responseMessage,
|
||||
SendResponse = !string.IsNullOrEmpty(responseMessage)
|
||||
});
|
||||
|
||||
return [new HttpRequestBundle<RequestInformation>(declineRequestInfo, request)];
|
||||
}
|
||||
|
||||
public override List<IRequestBundle<RequestInformation>> TentativeEvent(TentativeEventRequest request)
|
||||
{
|
||||
var calendarItem = request.Item;
|
||||
var calendar = calendarItem.AssignedCalendar;
|
||||
|
||||
if (calendar == null)
|
||||
{
|
||||
throw new InvalidOperationException("Calendar item must have an assigned calendar");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(calendarItem.RemoteEventId))
|
||||
{
|
||||
throw new InvalidOperationException("Cannot tentatively accept event without remote event ID");
|
||||
}
|
||||
|
||||
var tentativelyAcceptRequestInfo = _graphClient.Me.Calendars[calendar.RemoteCalendarId].Events[calendarItem.RemoteEventId].TentativelyAccept.ToPostRequestInformation(new Microsoft.Graph.Me.Calendars.Item.Events.Item.TentativelyAccept.TentativelyAcceptPostRequestBody
|
||||
{
|
||||
Comment = request.ResponseMessage,
|
||||
SendResponse = !string.IsNullOrEmpty(request.ResponseMessage)
|
||||
});
|
||||
|
||||
return [new HttpRequestBundle<RequestInformation>(tentativelyAcceptRequestInfo, request)];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override async Task KillSynchronizerAsync()
|
||||
|
||||
@@ -364,6 +364,22 @@ public abstract class WinoSynchronizer<TBaseRequest, TMessageType, TCalendarEven
|
||||
case CalendarSynchronizerOperation.CreateEvent:
|
||||
nativeRequests.AddRange(CreateCalendarEvent(group.ElementAt(0) as CreateCalendarEventRequest));
|
||||
break;
|
||||
case CalendarSynchronizerOperation.AcceptEvent:
|
||||
nativeRequests.AddRange(AcceptEvent(group.ElementAt(0) as AcceptEventRequest));
|
||||
break;
|
||||
case CalendarSynchronizerOperation.DeclineEvent:
|
||||
if (Account.ProviderType == MailProviderType.Outlook)
|
||||
{
|
||||
nativeRequests.AddRange(OutlookDeclineEvent(group.ElementAt(0) as OutlookDeclineEventRequest));
|
||||
}
|
||||
else
|
||||
{
|
||||
nativeRequests.AddRange(DeclineEvent(group.ElementAt(0) as DeclineEventRequest));
|
||||
}
|
||||
break;
|
||||
case CalendarSynchronizerOperation.TentativeEvent:
|
||||
nativeRequests.AddRange(TentativeEvent(group.ElementAt(0) as TentativeEventRequest));
|
||||
break;
|
||||
case CalendarSynchronizerOperation.UpdateEvent:
|
||||
// TODO: Implement UpdateCalendarEvent
|
||||
break;
|
||||
@@ -494,6 +510,10 @@ public abstract class WinoSynchronizer<TBaseRequest, TMessageType, TCalendarEven
|
||||
#region Calendar Operations
|
||||
|
||||
public virtual List<IRequestBundle<TBaseRequest>> CreateCalendarEvent(CreateCalendarEventRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
|
||||
public virtual List<IRequestBundle<TBaseRequest>> AcceptEvent(AcceptEventRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
|
||||
public virtual List<IRequestBundle<TBaseRequest>> DeclineEvent(DeclineEventRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
|
||||
public virtual List<IRequestBundle<TBaseRequest>> OutlookDeclineEvent(OutlookDeclineEventRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
|
||||
public virtual List<IRequestBundle<TBaseRequest>> TentativeEvent(TentativeEventRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
Binary file not shown.
@@ -100,6 +100,15 @@ public static class ControlConstants
|
||||
{ WinoIconGlyph.EventJoinOnline, "\uE926" },
|
||||
{ WinoIconGlyph.ViewMessageSource, "\uE943" },
|
||||
{ WinoIconGlyph.Apple, "\uE92B" },
|
||||
{ WinoIconGlyph.Yahoo, "\uE92C" }
|
||||
{ WinoIconGlyph.Yahoo, "\uE92C" },
|
||||
{ WinoIconGlyph.People, "\uF007" },
|
||||
{ WinoIconGlyph.AttachmentNew, "\uF006" },
|
||||
{ WinoIconGlyph.CalendarSettings, "\uF005" },
|
||||
{ WinoIconGlyph.SettingsNew, "\uF004" },
|
||||
{ WinoIconGlyph.ManageAccounts, "\uF003" },
|
||||
{ WinoIconGlyph.SendNew, "\uF002" },
|
||||
{ WinoIconGlyph.CalendarShowAs, "\uF001" },
|
||||
{ WinoIconGlyph.EventDecline, "\uF000" },
|
||||
{ WinoIconGlyph.Dismiss, "\uF008" },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -101,7 +101,15 @@ public enum WinoIconGlyph
|
||||
EventJoinOnline,
|
||||
ViewMessageSource,
|
||||
Apple,
|
||||
Yahoo
|
||||
Yahoo,
|
||||
People,
|
||||
AttachmentNew,
|
||||
CalendarSettings,
|
||||
SettingsNew,
|
||||
ManageAccounts,
|
||||
SendNew,
|
||||
CalendarShowAs,
|
||||
Dismiss
|
||||
}
|
||||
|
||||
public partial class WinoFontIcon : FontIcon
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Mail.WinUI.Selectors;
|
||||
|
||||
public partial class RsvpStatusIconTemplateSelector : DataTemplateSelector
|
||||
{
|
||||
public DataTemplate NotRespondedTemplate { get; set; }
|
||||
public DataTemplate ConfirmedTemplate { get; set; }
|
||||
public DataTemplate TentativeTemplate { get; set; }
|
||||
public DataTemplate CancelledTemplate { get; set; }
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
|
||||
{
|
||||
if (item is CalendarItemStatus status)
|
||||
{
|
||||
return status switch
|
||||
{
|
||||
CalendarItemStatus.NotResponded => NotRespondedTemplate,
|
||||
CalendarItemStatus.Accepted => ConfirmedTemplate,
|
||||
CalendarItemStatus.Tentative => TentativeTemplate,
|
||||
CalendarItemStatus.Cancelled => CancelledTemplate,
|
||||
_ => NotRespondedTemplate
|
||||
};
|
||||
}
|
||||
|
||||
return base.SelectTemplateCore(item, container);
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -8,16 +8,64 @@
|
||||
xmlns:calendarHelpers="using:Wino.Calendar.Helpers"
|
||||
xmlns:calendarViewModels="using:Wino.Calendar.ViewModels"
|
||||
xmlns:coreControls="using:Wino.Mail.WinUI.Controls"
|
||||
xmlns:ctControls="using:CommunityToolkit.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"
|
||||
xmlns:selectors="using:Wino.Mail.WinUI.Selectors"
|
||||
Style="{StaticResource PageStyle}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<selectors:RsvpStatusIconTemplateSelector x:Key="RsvpStatusIconSelector">
|
||||
<selectors:RsvpStatusIconTemplateSelector.NotRespondedTemplate>
|
||||
<DataTemplate>
|
||||
<coreControls:WinoFontIcon
|
||||
FontSize="20"
|
||||
Foreground="{ThemeResource SystemAccentColor}"
|
||||
Icon="EventRespond" />
|
||||
</DataTemplate>
|
||||
</selectors:RsvpStatusIconTemplateSelector.NotRespondedTemplate>
|
||||
<selectors:RsvpStatusIconTemplateSelector.ConfirmedTemplate>
|
||||
<DataTemplate>
|
||||
<coreControls:WinoFontIcon
|
||||
FontSize="20"
|
||||
Foreground="#527257"
|
||||
Icon="EventAccept" />
|
||||
</DataTemplate>
|
||||
</selectors:RsvpStatusIconTemplateSelector.ConfirmedTemplate>
|
||||
<selectors:RsvpStatusIconTemplateSelector.TentativeTemplate>
|
||||
<DataTemplate>
|
||||
<coreControls:WinoFontIcon
|
||||
FontSize="20"
|
||||
Foreground="#805682"
|
||||
Icon="EventTentative" />
|
||||
</DataTemplate>
|
||||
</selectors:RsvpStatusIconTemplateSelector.TentativeTemplate>
|
||||
<selectors:RsvpStatusIconTemplateSelector.CancelledTemplate>
|
||||
<DataTemplate>
|
||||
<coreControls:WinoFontIcon
|
||||
FontSize="20"
|
||||
Foreground="{ThemeResource DeleteBrush}"
|
||||
Icon="EventDecline" />
|
||||
</DataTemplate>
|
||||
</selectors:RsvpStatusIconTemplateSelector.CancelledTemplate>
|
||||
</selectors:RsvpStatusIconTemplateSelector>
|
||||
|
||||
<Style
|
||||
x:Key="TransparentActionButtonStyle"
|
||||
BasedOn="{StaticResource DefaultButtonStyle}"
|
||||
TargetType="Button">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Padding" Value="12,8" />
|
||||
<Setter Property="MinWidth" Value="0" />
|
||||
<Setter Property="MinHeight" Value="0" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ActionBarElementContainerStackStyle" TargetType="StackPanel">
|
||||
<Setter Property="Spacing" Value="6" />
|
||||
<Setter Property="Padding" Value="10,0,4,0" />
|
||||
@@ -25,10 +73,6 @@
|
||||
<Setter Property="Orientation" Value="Horizontal" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="AppBarElementContainer">
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="EventDetailsPanelGridStyle" TargetType="Grid">
|
||||
<Setter Property="Padding" Value="12,6" />
|
||||
<Setter Property="Margin" Value="0,12" />
|
||||
@@ -46,6 +90,7 @@
|
||||
</Page.Resources>
|
||||
<Grid Padding="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
@@ -57,92 +102,100 @@
|
||||
BorderBrush="{StaticResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="7">
|
||||
<CommandBar
|
||||
HorizontalAlignment="Left"
|
||||
Background="Transparent"
|
||||
DefaultLabelPosition="Right"
|
||||
IsSticky="True"
|
||||
OverflowButtonVisibility="Auto">
|
||||
<AppBarToggleButton
|
||||
x:Name="ReadOnlyToggle"
|
||||
Content="{x:Bind domain:Translator.CalendarEventDetails_ReadOnlyEvent}"
|
||||
IsChecked="True" />
|
||||
<AppBarButton Command="{x:Bind ViewModel.SaveCommand}" Label="{x:Bind domain:Translator.Buttons_Save}">
|
||||
<AppBarButton.Icon>
|
||||
<coreControls:WinoFontIcon Icon="Save" />
|
||||
</AppBarButton.Icon>
|
||||
</AppBarButton>
|
||||
<AppBarButton Command="{x:Bind ViewModel.DeleteCommand}" Label="{x:Bind domain:Translator.Buttons_Delete}">
|
||||
<AppBarButton.Icon>
|
||||
<coreControls:WinoFontIcon Icon="Delete" />
|
||||
</AppBarButton.Icon>
|
||||
</AppBarButton>
|
||||
<Grid Padding="8">
|
||||
<ctControls:WrapPanel
|
||||
x:Name="ActionBarWrapGrid"
|
||||
HorizontalAlignment="Left"
|
||||
HorizontalSpacing="4"
|
||||
VerticalSpacing="4">
|
||||
|
||||
<AppBarSeparator />
|
||||
<!-- Read Only Toggle -->
|
||||
<ToggleButton
|
||||
x:Name="ReadOnlyToggle"
|
||||
IsChecked="True"
|
||||
Style="{StaticResource TransparentActionButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<coreControls:WinoFontIcon FontSize="16" Icon="Blocked" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.CalendarEventDetails_ReadOnlyEvent}" />
|
||||
</StackPanel>
|
||||
</ToggleButton>
|
||||
|
||||
<!-- 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>
|
||||
</AppBarButton>
|
||||
<!-- Save -->
|
||||
<Button Command="{x:Bind ViewModel.SaveCommand}" Style="{StaticResource TransparentActionButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<coreControls:WinoFontIcon FontSize="16" Icon="Save" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.Buttons_Save}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<AppBarSeparator />
|
||||
<!-- Delete -->
|
||||
<Button Command="{x:Bind ViewModel.DeleteCommand}" Style="{StaticResource TransparentActionButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<coreControls:WinoFontIcon FontSize="16" Icon="Delete" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.Buttons_Delete}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<!-- Response Options -->
|
||||
<Border
|
||||
Width="1"
|
||||
Height="24"
|
||||
Margin="4,0"
|
||||
VerticalAlignment="Center"
|
||||
Background="{ThemeResource DividerStrokeColorDefaultBrush}" />
|
||||
|
||||
<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>
|
||||
<!-- Join Online -->
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.JoinOnlineCommand}"
|
||||
IsEnabled="{x:Bind calendarHelpers:CalendarXamlHelpers.HasOnlineMeetingLink(ViewModel.CurrentEvent), Mode=OneWay}"
|
||||
Style="{StaticResource TransparentActionButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<coreControls:WinoFontIcon FontSize="16" Icon="EventJoinOnline" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.CalendarEventDetails_JoinOnline}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<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>
|
||||
<Border
|
||||
Width="1"
|
||||
Height="24"
|
||||
Margin="4,0"
|
||||
VerticalAlignment="Center"
|
||||
Background="{ThemeResource DividerStrokeColorDefaultBrush}" />
|
||||
|
||||
<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>
|
||||
</AppBarButton>
|
||||
<!-- RSVP Button -->
|
||||
<Button Command="{x:Bind ViewModel.ToggleRsvpPanelCommand}" Style="{StaticResource TransparentActionButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<ContentControl Content="{x:Bind ViewModel.CurrentRsvpStatus, Mode=OneWay}" ContentTemplateSelector="{StaticResource RsvpStatusIconSelector}" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind ViewModel.CurrentRsvpText, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<AppBarSeparator />
|
||||
<Border
|
||||
Width="1"
|
||||
Height="24"
|
||||
Margin="4,0"
|
||||
VerticalAlignment="Center"
|
||||
Background="{ThemeResource DividerStrokeColorDefaultBrush}" />
|
||||
|
||||
<!-- Show as -->
|
||||
<AppBarElementContainer>
|
||||
<StackPanel Style="{StaticResource ActionBarElementContainerStackStyle}">
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.CalendarEventDetails_ShowAs}" />
|
||||
<!-- Show as -->
|
||||
<StackPanel
|
||||
Padding="12,0"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="8">
|
||||
<coreControls:WinoFontIcon FontSize="20" Icon="CalendarShowAs" />
|
||||
<ComboBox
|
||||
Width="150"
|
||||
VerticalAlignment="Center"
|
||||
ItemsSource="{x:Bind ViewModel.ShowAsOptions}"
|
||||
SelectedItem="{x:Bind ViewModel.SelectedShowAs, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</AppBarElementContainer>
|
||||
|
||||
<!-- Reminder -->
|
||||
<AppBarElementContainer>
|
||||
<Button>
|
||||
<Button.Content>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<coreControls:WinoFontIcon FontSize="16" Icon="Reminder" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.CalendarEventDetails_Reminder}" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
<!-- Reminder -->
|
||||
<Button Style="{StaticResource TransparentActionButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<coreControls:WinoFontIcon FontSize="16" Icon="Reminder" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.CalendarEventDetails_Reminder}" />
|
||||
</StackPanel>
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<ListView
|
||||
@@ -159,24 +212,124 @@
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</AppBarElementContainer>
|
||||
|
||||
<AppBarSeparator Visibility="{x:Bind ViewModel.CanEditSeries, Mode=OneWay}" />
|
||||
<!-- Edit Series -->
|
||||
<Border
|
||||
Width="1"
|
||||
Height="24"
|
||||
Margin="4,0"
|
||||
VerticalAlignment="Center"
|
||||
Background="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||
Visibility="{x:Bind ViewModel.CanEditSeries, Mode=OneWay}" />
|
||||
|
||||
<!-- Edit Series -->
|
||||
<AppBarButton
|
||||
Command="{x:Bind ViewModel.ViewSeriesCommand}"
|
||||
Label="{x:Bind domain:Translator.CalendarEventDetails_EditSeries}"
|
||||
Visibility="{x:Bind ViewModel.CanEditSeries, Mode=OneWay}">
|
||||
<AppBarButton.Icon>
|
||||
<coreControls:WinoFontIcon Icon="EventEditSeries" />
|
||||
</AppBarButton.Icon>
|
||||
</AppBarButton>
|
||||
</CommandBar>
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.ViewSeriesCommand}"
|
||||
Style="{StaticResource TransparentActionButtonStyle}"
|
||||
Visibility="{x:Bind ViewModel.CanEditSeries, Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<coreControls:WinoFontIcon FontSize="16" Icon="EventEditSeries" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.CalendarEventDetails_EditSeries}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</ctControls:WrapPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- RSVP Panel -->
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Margin="0,8,0,0"
|
||||
Padding="16"
|
||||
VerticalAlignment="Top"
|
||||
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
|
||||
BorderBrush="{StaticResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="7"
|
||||
Visibility="{x:Bind ViewModel.IsRsvpPanelVisible, Mode=OneWay}">
|
||||
<Grid RowSpacing="6">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Close Button -->
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Command="{x:Bind ViewModel.CloseRsvpPanelCommand}"
|
||||
Style="{StaticResource TransparentActionButtonStyle}">
|
||||
<coreControls:WinoFontIcon FontSize="16" Icon="Dismiss" />
|
||||
</Button>
|
||||
|
||||
<!-- RSVP Buttons -->
|
||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||
<!-- Accept -->
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.SendRsvpResponseCommand}"
|
||||
CommandParameter="{x:Bind enums:AttendeeStatus.Accepted}"
|
||||
Style="{StaticResource TransparentActionButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<coreControls:WinoFontIcon
|
||||
FontSize="20"
|
||||
Foreground="#527257"
|
||||
Icon="EventAccept" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.CalendarEventRsvpPanel_Accept, Mode=OneTime}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<!-- Tentative -->
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.SendRsvpResponseCommand}"
|
||||
CommandParameter="{x:Bind enums:AttendeeStatus.Tentative}"
|
||||
Style="{StaticResource TransparentActionButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<coreControls:WinoFontIcon
|
||||
FontSize="20"
|
||||
Foreground="#805682"
|
||||
Icon="EventTentative" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.CalendarEventRsvpPanel_Tentative, Mode=OneTime}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<!-- Decline -->
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.SendRsvpResponseCommand}"
|
||||
CommandParameter="{x:Bind enums:AttendeeStatus.Declined}"
|
||||
Style="{StaticResource TransparentActionButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<coreControls:WinoFontIcon
|
||||
FontSize="20"
|
||||
Foreground="{ThemeResource DeleteBrush}"
|
||||
Icon="EventDecline" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.CalendarEventRsvpPanel_Decline, Mode=OneTime}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Reply Message TextBox -->
|
||||
<TextBox
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="2"
|
||||
VerticalAlignment="Center"
|
||||
AcceptsReturn="True"
|
||||
PlaceholderText="{x:Bind domain:Translator.CalendarEventRsvpPanel_AddMessage, Mode=OneTime}"
|
||||
Text="{x:Bind ViewModel.RsvpMessage, Mode=TwoWay}"
|
||||
TextWrapping="Wrap">
|
||||
<TextBox.Header>
|
||||
<StackPanel Orientation="Horizontal" />
|
||||
</TextBox.Header>
|
||||
</TextBox>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- Event details -->
|
||||
<ScrollViewer Grid.Row="1">
|
||||
<ScrollViewer Grid.Row="2">
|
||||
<Grid ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="2*" />
|
||||
@@ -293,18 +446,32 @@
|
||||
Height="40"
|
||||
DisplayName="{x:Bind Name}" />
|
||||
|
||||
<!-- TODO: Organizer -->
|
||||
<Grid Grid.Column="1">
|
||||
<Grid Grid.Column="1" RowSpacing="4">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock FontWeight="SemiBold" Text="{x:Bind Name}" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
FontSize="13"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{x:Bind Email}" />
|
||||
<Border
|
||||
Grid.Row="2"
|
||||
Padding="6,2"
|
||||
HorizontalAlignment="Left"
|
||||
Background="{ThemeResource AccentFillColorDefaultBrush}"
|
||||
CornerRadius="4"
|
||||
Visibility="{x:Bind IsOrganizer}">
|
||||
<TextBlock
|
||||
FontSize="11"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextOnAccentFillColorPrimaryBrush}"
|
||||
Text="{x:Bind domain:Translator.CalendarEventDetails_Organizer}" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
@@ -328,5 +495,50 @@
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="WindowWidthStates">
|
||||
<VisualState x:Name="WideState">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="1200" />
|
||||
</VisualState.StateTriggers>
|
||||
</VisualState>
|
||||
<VisualState x:Name="MediumState">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="800" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- Details takes left side (full height), People and Attachments share right side vertically -->
|
||||
<Setter Target="DetailsGrid.(Grid.Row)" Value="0" />
|
||||
<Setter Target="DetailsGrid.(Grid.Column)" Value="0" />
|
||||
<Setter Target="DetailsGrid.(Grid.RowSpan)" Value="2" />
|
||||
<Setter Target="DetailsGrid.(Grid.ColumnSpan)" Value="1" />
|
||||
<Setter Target="PeopleGrid.(Grid.Row)" Value="0" />
|
||||
<Setter Target="PeopleGrid.(Grid.Column)" Value="1" />
|
||||
<Setter Target="PeopleGrid.(Grid.ColumnSpan)" Value="2" />
|
||||
<Setter Target="AttachmentsGrid.(Grid.Row)" Value="1" />
|
||||
<Setter Target="AttachmentsGrid.(Grid.Column)" Value="1" />
|
||||
<Setter Target="AttachmentsGrid.(Grid.ColumnSpan)" Value="2" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="NarrowState">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="0" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- Stack all panels vertically -->
|
||||
<Setter Target="DetailsGrid.(Grid.Row)" Value="0" />
|
||||
<Setter Target="DetailsGrid.(Grid.Column)" Value="0" />
|
||||
<Setter Target="DetailsGrid.(Grid.ColumnSpan)" Value="3" />
|
||||
<Setter Target="PeopleGrid.(Grid.Row)" Value="1" />
|
||||
<Setter Target="PeopleGrid.(Grid.Column)" Value="0" />
|
||||
<Setter Target="PeopleGrid.(Grid.ColumnSpan)" Value="3" />
|
||||
<Setter Target="AttachmentsGrid.(Grid.Row)" Value="2" />
|
||||
<Setter Target="AttachmentsGrid.(Grid.Column)" Value="0" />
|
||||
<Setter Target="AttachmentsGrid.(Grid.ColumnSpan)" Value="3" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</abstract:EventDetailsPageAbstract>
|
||||
|
||||
@@ -11,6 +11,7 @@ using Itenso.TimePeriod;
|
||||
using Serilog;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Entities.Calendar;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
@@ -253,14 +254,33 @@ public class CalendarService : BaseDatabaseService, ICalendarService
|
||||
return result;
|
||||
}
|
||||
|
||||
public Task<AccountCalendar> GetAccountCalendarAsync(Guid accountCalendarId)
|
||||
=> Connection.GetAsync<AccountCalendar>(accountCalendarId);
|
||||
|
||||
public Task<CalendarItem> GetCalendarItemAsync(Guid id)
|
||||
public async Task<AccountCalendar> GetAccountCalendarAsync(Guid accountCalendarId)
|
||||
{
|
||||
return Connection.FindWithQueryAsync<CalendarItem>(
|
||||
var calendar = await Connection.GetAsync<AccountCalendar>(accountCalendarId);
|
||||
if (calendar != null)
|
||||
{
|
||||
calendar.MailAccount = await Connection.GetAsync<MailAccount>(calendar.AccountId);
|
||||
}
|
||||
return calendar;
|
||||
}
|
||||
|
||||
public async Task<CalendarItem> GetCalendarItemAsync(Guid id)
|
||||
{
|
||||
var calendarItem = await Connection.FindWithQueryAsync<CalendarItem>(
|
||||
"SELECT * FROM CalendarItem WHERE Id = ?",
|
||||
id);
|
||||
|
||||
// Load assigned calendar and account.
|
||||
if (calendarItem != null)
|
||||
{
|
||||
calendarItem.AssignedCalendar = await Connection.GetAsync<AccountCalendar>(calendarItem.CalendarId);
|
||||
if (calendarItem.AssignedCalendar != null)
|
||||
{
|
||||
calendarItem.AssignedCalendar.MailAccount = await Connection.GetAsync<MailAccount>(calendarItem.AssignedCalendar.AccountId);
|
||||
}
|
||||
}
|
||||
|
||||
return calendarItem;
|
||||
}
|
||||
|
||||
public async Task<CalendarItem> GetCalendarItemAsync(Guid accountCalendarId, string remoteEventId)
|
||||
@@ -269,10 +289,14 @@ public class CalendarService : BaseDatabaseService, ICalendarService
|
||||
"SELECT * FROM CalendarItem WHERE CalendarId = ? AND RemoteEventId = ?",
|
||||
accountCalendarId, remoteEventId);
|
||||
|
||||
// Load assigned calendar.
|
||||
// Load assigned calendar and account.
|
||||
if (calendarItem != null)
|
||||
{
|
||||
calendarItem.AssignedCalendar = await Connection.GetAsync<AccountCalendar>(calendarItem.CalendarId);
|
||||
if (calendarItem.AssignedCalendar != null)
|
||||
{
|
||||
calendarItem.AssignedCalendar.MailAccount = await Connection.GetAsync<MailAccount>(calendarItem.AssignedCalendar.AccountId);
|
||||
}
|
||||
}
|
||||
|
||||
return calendarItem;
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -10,6 +10,7 @@
|
||||
<File Path="Directory.Packages.props" />
|
||||
<File Path="nuget.config" />
|
||||
<File Path="Settings.XamlStyler" />
|
||||
<File Path="WinoFontIcomoon.json" />
|
||||
</Folder>
|
||||
<Folder Name="/lib/">
|
||||
<Project Path="Wino.Authentication/Wino.Authentication.csproj">
|
||||
|
||||
Reference in New Issue
Block a user