Initial feature for drag / drop calendar events.
This commit is contained in:
@@ -526,6 +526,59 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
|
|||||||
SelectedEndTimeString = CurrentSettings.GetTimeString(endTime);
|
SelectedEndTimeString = CurrentSettings.GetTimeString(endTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task MoveCalendarItemAsync(CalendarItemViewModel calendarItemViewModel, DateTime targetStart)
|
||||||
|
{
|
||||||
|
if (calendarItemViewModel?.CalendarItem == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var calendarItem = calendarItemViewModel.CalendarItem;
|
||||||
|
|
||||||
|
if (!calendarItem.CanChangeStartAndEndDate)
|
||||||
|
{
|
||||||
|
_dialogService.InfoBarMessage(
|
||||||
|
Translator.CalendarDragDropMoveNotAllowedTitle,
|
||||||
|
Translator.CalendarDragDropMoveNotAllowedMessage,
|
||||||
|
InfoBarMessageType.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var normalizedTargetStart = calendarItem.IsAllDayEvent
|
||||||
|
? targetStart.Date
|
||||||
|
: targetStart;
|
||||||
|
var targetEnd = normalizedTargetStart.AddSeconds(calendarItem.DurationInSeconds);
|
||||||
|
var currentLocalStart = calendarItem.LocalStartDate;
|
||||||
|
var currentLocalEnd = calendarItem.LocalEndDate;
|
||||||
|
|
||||||
|
if (currentLocalStart == normalizedTargetStart && currentLocalEnd == targetEnd)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var originalItem = CloneCalendarItem(calendarItem);
|
||||||
|
var attendees = await _calendarService.GetAttendeesAsync(calendarItem.EventTrackingId).ConfigureAwait(false) ?? [];
|
||||||
|
var originalAttendees = CloneAttendees(attendees);
|
||||||
|
|
||||||
|
await ExecuteUIThread(() =>
|
||||||
|
{
|
||||||
|
calendarItemViewModel.StartDate = normalizedTargetStart;
|
||||||
|
calendarItemViewModel.DurationInSeconds = calendarItem.DurationInSeconds;
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
|
||||||
|
await _calendarService.UpdateCalendarItemAsync(calendarItem, attendees).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var preparationRequest = new CalendarOperationPreparationRequest(
|
||||||
|
CalendarSynchronizerOperation.ChangeStartAndEndDate,
|
||||||
|
calendarItem,
|
||||||
|
attendees,
|
||||||
|
ResponseMessage: null,
|
||||||
|
OriginalItem: originalItem,
|
||||||
|
OriginalAttendees: originalAttendees);
|
||||||
|
|
||||||
|
await _winoRequestDelegator.ExecuteAsync(preparationRequest).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
partial void OnDisplayDetailsCalendarItemViewModelChanged(CalendarItemViewModel value)
|
partial void OnDisplayDetailsCalendarItemViewModelChanged(CalendarItemViewModel value)
|
||||||
=> DetailsShowCalendarItemChanged?.Invoke(this, EventArgs.Empty);
|
=> DetailsShowCalendarItemChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
|
||||||
@@ -872,6 +925,50 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
|
|||||||
source == EntityUpdateSource.ClientUpdated ? new HashSet<Guid> { calendarItem.Id } : null,
|
source == EntityUpdateSource.ClientUpdated ? new HashSet<Guid> { calendarItem.Id } : null,
|
||||||
source);
|
source);
|
||||||
|
|
||||||
|
private static CalendarItem CloneCalendarItem(CalendarItem calendarItem)
|
||||||
|
=> new()
|
||||||
|
{
|
||||||
|
Id = calendarItem.Id,
|
||||||
|
RemoteEventId = calendarItem.RemoteEventId,
|
||||||
|
Title = calendarItem.Title,
|
||||||
|
Description = calendarItem.Description,
|
||||||
|
Location = calendarItem.Location,
|
||||||
|
StartDate = calendarItem.StartDate,
|
||||||
|
StartTimeZone = calendarItem.StartTimeZone,
|
||||||
|
EndTimeZone = calendarItem.EndTimeZone,
|
||||||
|
DurationInSeconds = calendarItem.DurationInSeconds,
|
||||||
|
Recurrence = calendarItem.Recurrence,
|
||||||
|
OrganizerDisplayName = calendarItem.OrganizerDisplayName,
|
||||||
|
OrganizerEmail = calendarItem.OrganizerEmail,
|
||||||
|
RecurringCalendarItemId = calendarItem.RecurringCalendarItemId,
|
||||||
|
IsLocked = calendarItem.IsLocked,
|
||||||
|
IsHidden = calendarItem.IsHidden,
|
||||||
|
CustomEventColorHex = calendarItem.CustomEventColorHex,
|
||||||
|
HtmlLink = calendarItem.HtmlLink,
|
||||||
|
SnoozedUntil = calendarItem.SnoozedUntil,
|
||||||
|
Status = calendarItem.Status,
|
||||||
|
Visibility = calendarItem.Visibility,
|
||||||
|
ShowAs = calendarItem.ShowAs,
|
||||||
|
CreatedAt = calendarItem.CreatedAt,
|
||||||
|
UpdatedAt = calendarItem.UpdatedAt,
|
||||||
|
CalendarId = calendarItem.CalendarId,
|
||||||
|
AssignedCalendar = calendarItem.AssignedCalendar
|
||||||
|
};
|
||||||
|
|
||||||
|
private static List<CalendarEventAttendee> CloneAttendees(IEnumerable<CalendarEventAttendee> attendees)
|
||||||
|
=> attendees?.Select(attendee => new CalendarEventAttendee
|
||||||
|
{
|
||||||
|
Id = attendee.Id,
|
||||||
|
CalendarItemId = attendee.CalendarItemId,
|
||||||
|
Name = attendee.Name,
|
||||||
|
Email = attendee.Email,
|
||||||
|
AttendenceStatus = attendee.AttendenceStatus,
|
||||||
|
IsOrganizer = attendee.IsOrganizer,
|
||||||
|
IsOptionalAttendee = attendee.IsOptionalAttendee,
|
||||||
|
Comment = attendee.Comment,
|
||||||
|
ResolvedContact = attendee.ResolvedContact
|
||||||
|
}).ToList() ?? [];
|
||||||
|
|
||||||
private CalendarItemViewModel CreateCalendarItemViewModel(CalendarItem calendarItem, ISet<Guid> pendingCalendarItemIds, EntityUpdateSource source = EntityUpdateSource.Server)
|
private CalendarItemViewModel CreateCalendarItemViewModel(CalendarItem calendarItem, ISet<Guid> pendingCalendarItemIds, EntityUpdateSource source = EntityUpdateSource.Server)
|
||||||
{
|
{
|
||||||
calendarItem.AssignedCalendar ??= ResolveAssignedCalendar(calendarItem.CalendarId);
|
calendarItem.AssignedCalendar ??= ResolveAssignedCalendar(calendarItem.CalendarId);
|
||||||
|
|||||||
@@ -79,5 +79,9 @@ public partial class AccountCalendarViewModel : ObservableObject, IAccountCalend
|
|||||||
set => SetProperty(AccountCalendar.DefaultShowAs, value, AccountCalendar, (u, s) => u.DefaultShowAs = s);
|
set => SetProperty(AccountCalendar.DefaultShowAs, value, AccountCalendar, (u, s) => u.DefaultShowAs = s);
|
||||||
}
|
}
|
||||||
public Guid Id { get => ((IAccountCalendar)AccountCalendar).Id; set => ((IAccountCalendar)AccountCalendar).Id = value; }
|
public Guid Id { get => ((IAccountCalendar)AccountCalendar).Id; set => ((IAccountCalendar)AccountCalendar).Id = value; }
|
||||||
public MailAccount MailAccount { get => MailAccount; set => MailAccount = value; }
|
public MailAccount MailAccount
|
||||||
|
{
|
||||||
|
get => AccountCalendar.MailAccount ?? Account;
|
||||||
|
set => AccountCalendar.MailAccount = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ public partial class CalendarItemViewModel : ObservableObject, ICalendarItem, IC
|
|||||||
public bool IsRecurringEvent => CalendarItem.IsRecurringEvent;
|
public bool IsRecurringEvent => CalendarItem.IsRecurringEvent;
|
||||||
public bool IsRecurringChild => CalendarItem.IsRecurringChild;
|
public bool IsRecurringChild => CalendarItem.IsRecurringChild;
|
||||||
public bool IsRecurringParent => CalendarItem.IsRecurringParent;
|
public bool IsRecurringParent => CalendarItem.IsRecurringParent;
|
||||||
|
public bool CanDragDrop => CalendarItem.CanChangeStartAndEndDate;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial bool IsSelected { get; set; }
|
public partial bool IsSelected { get; set; }
|
||||||
@@ -157,6 +158,7 @@ public partial class CalendarItemViewModel : ObservableObject, ICalendarItem, IC
|
|||||||
OnPropertyChanged(nameof(IsRecurringEvent));
|
OnPropertyChanged(nameof(IsRecurringEvent));
|
||||||
OnPropertyChanged(nameof(IsRecurringChild));
|
OnPropertyChanged(nameof(IsRecurringChild));
|
||||||
OnPropertyChanged(nameof(IsRecurringParent));
|
OnPropertyChanged(nameof(IsRecurringParent));
|
||||||
|
OnPropertyChanged(nameof(CanDragDrop));
|
||||||
OnPropertyChanged(nameof(AssignedCalendar));
|
OnPropertyChanged(nameof(AssignedCalendar));
|
||||||
OnPropertyChanged(nameof(DisplayTitle));
|
OnPropertyChanged(nameof(DisplayTitle));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,6 +154,24 @@ public class CalendarItem : ICalendarItem
|
|||||||
[Ignore]
|
[Ignore]
|
||||||
public IAccountCalendar AssignedCalendar { get; set; }
|
public IAccountCalendar AssignedCalendar { get; set; }
|
||||||
|
|
||||||
|
[Ignore]
|
||||||
|
public bool CanChangeStartAndEndDate
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (IsLocked)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var accountAddress = AssignedCalendar?.MailAccount?.Address;
|
||||||
|
|
||||||
|
return string.IsNullOrWhiteSpace(OrganizerEmail) ||
|
||||||
|
string.IsNullOrWhiteSpace(accountAddress) ||
|
||||||
|
string.Equals(OrganizerEmail, accountAddress, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Id to load information related to this event (attendees, reminders, etc.).
|
/// Id to load information related to this event (attendees, reminders, etc.).
|
||||||
/// For child events, if they have their own data, use their own Id.
|
/// For child events, if they have their own data, use their own Id.
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ public enum CalendarSynchronizerOperation
|
|||||||
{
|
{
|
||||||
CreateEvent,
|
CreateEvent,
|
||||||
UpdateEvent,
|
UpdateEvent,
|
||||||
|
ChangeStartAndEndDate,
|
||||||
DeleteEvent,
|
DeleteEvent,
|
||||||
AcceptEvent,
|
AcceptEvent,
|
||||||
DeclineEvent,
|
DeclineEvent,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace Wino.Core.Domain.Models.Calendar;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Encapsulates the options for preparing calendar operation requests.
|
/// Encapsulates the options for preparing calendar operation requests.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="Operation">Calendar operation to execute (Create, Update, Delete, Accept, Decline, Tentative).</param>
|
/// <param name="Operation">Calendar operation to execute (Create, Update, ChangeStartAndEndDate, Delete, Accept, Decline, Tentative).</param>
|
||||||
/// <param name="CalendarItem">Calendar item to operate on.</param>
|
/// <param name="CalendarItem">Calendar item to operate on.</param>
|
||||||
/// <param name="Attendees">List of attendees for the calendar event.</param>
|
/// <param name="Attendees">List of attendees for the calendar event.</param>
|
||||||
/// <param name="ResponseMessage">Optional message to include with event responses (Accept, Decline, Tentative).</param>
|
/// <param name="ResponseMessage">Optional message to include with event responses (Accept, Decline, Tentative).</param>
|
||||||
|
|||||||
@@ -210,6 +210,8 @@
|
|||||||
"CalendarItem_DetailsPopup_JoinOnline": "Join online",
|
"CalendarItem_DetailsPopup_JoinOnline": "Join online",
|
||||||
"CalendarItem_DetailsPopup_ViewEventButton": "View event",
|
"CalendarItem_DetailsPopup_ViewEventButton": "View event",
|
||||||
"CalendarItem_DetailsPopup_ViewSeriesButton": "View series",
|
"CalendarItem_DetailsPopup_ViewSeriesButton": "View series",
|
||||||
|
"CalendarDragDropMoveNotAllowedMessage": "Only events you own and can edit can be moved.",
|
||||||
|
"CalendarDragDropMoveNotAllowedTitle": "This event can't be moved",
|
||||||
"CalendarItemAllDay": "all day",
|
"CalendarItemAllDay": "all day",
|
||||||
"CategoriesFolderNameOverride": "Categories",
|
"CategoriesFolderNameOverride": "Categories",
|
||||||
"Center": "Center",
|
"Center": "Center",
|
||||||
|
|||||||
@@ -287,6 +287,30 @@ public class CalendarPageViewModelTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CanChangeStartAndEndDate_ReturnsTrueForOrganizerMatchingAssignedCalendarAccount()
|
||||||
|
{
|
||||||
|
var account = CreateAccount();
|
||||||
|
var calendar = CreateCalendar(account, "Calendar");
|
||||||
|
var accountCalendarViewModel = new AccountCalendarViewModel(account, calendar);
|
||||||
|
var calendarItem = CreateCalendarItem(calendar.Id, new DateTime(2026, 3, 20, 9, 0, 0), "Existing");
|
||||||
|
|
||||||
|
calendarItem.AssignedCalendar = accountCalendarViewModel;
|
||||||
|
calendarItem.OrganizerEmail = account.Address;
|
||||||
|
|
||||||
|
calendarItem.CanChangeStartAndEndDate.Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AccountCalendarViewModel_MailAccount_ExposesUnderlyingAccount()
|
||||||
|
{
|
||||||
|
var account = CreateAccount();
|
||||||
|
var calendar = CreateCalendar(account, "Calendar");
|
||||||
|
var accountCalendarViewModel = new AccountCalendarViewModel(account, calendar);
|
||||||
|
|
||||||
|
accountCalendarViewModel.MailAccount.Should().BeSameAs(account);
|
||||||
|
}
|
||||||
|
|
||||||
private static CalendarPageViewModel CreateViewModel(
|
private static CalendarPageViewModel CreateViewModel(
|
||||||
ICalendarService calendarService,
|
ICalendarService calendarService,
|
||||||
IPreferencesService preferencesService,
|
IPreferencesService preferencesService,
|
||||||
|
|||||||
@@ -79,6 +79,27 @@ public sealed class WinoSynchronizerCalendarRequestTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Change_start_and_end_date_request_should_dispatch_to_matching_handler()
|
||||||
|
{
|
||||||
|
var synchronizer = new TestCalendarSynchronizer(throwDuringRequestExecution: false);
|
||||||
|
var calendarItemId = Guid.NewGuid();
|
||||||
|
var request = new ChangeStartAndEndDateRequest(
|
||||||
|
new CalendarItem { Id = calendarItemId },
|
||||||
|
[]);
|
||||||
|
|
||||||
|
synchronizer.QueueRequest(request);
|
||||||
|
|
||||||
|
var result = await synchronizer.SynchronizeCalendarEventsAsync(new CalendarSynchronizationOptions
|
||||||
|
{
|
||||||
|
AccountId = synchronizer.Account.Id,
|
||||||
|
Type = CalendarSynchronizationType.ExecuteRequests
|
||||||
|
});
|
||||||
|
|
||||||
|
result.CompletedState.Should().Be(SynchronizationCompletedState.Success);
|
||||||
|
synchronizer.ChangeStartAndEndDateInvocationCount.Should().Be(1);
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class SynchronizationActionsCompletedRecipient : IRecipient<SynchronizationActionsCompleted>
|
public sealed class SynchronizationActionsCompletedRecipient : IRecipient<SynchronizationActionsCompleted>
|
||||||
{
|
{
|
||||||
public List<Guid> CompletedAccountIds { get; } = [];
|
public List<Guid> CompletedAccountIds { get; } = [];
|
||||||
@@ -98,6 +119,7 @@ public sealed class WinoSynchronizerCalendarRequestTests
|
|||||||
|
|
||||||
public override uint BatchModificationSize => 1;
|
public override uint BatchModificationSize => 1;
|
||||||
public override uint InitialMessageDownloadCountPerFolder => 0;
|
public override uint InitialMessageDownloadCountPerFolder => 0;
|
||||||
|
public int ChangeStartAndEndDateInvocationCount { get; private set; }
|
||||||
|
|
||||||
public override Task ExecuteNativeRequestsAsync(List<IRequestBundle<object>> batchedRequests, CancellationToken cancellationToken = default)
|
public override Task ExecuteNativeRequestsAsync(List<IRequestBundle<object>> batchedRequests, CancellationToken cancellationToken = default)
|
||||||
=> _throwDuringRequestExecution
|
=> _throwDuringRequestExecution
|
||||||
@@ -107,6 +129,12 @@ public sealed class WinoSynchronizerCalendarRequestTests
|
|||||||
public override List<IRequestBundle<object>> DeleteCalendarEvent(DeleteCalendarEventRequest request)
|
public override List<IRequestBundle<object>> DeleteCalendarEvent(DeleteCalendarEventRequest request)
|
||||||
=> [new TestRequestBundle(new object(), request)];
|
=> [new TestRequestBundle(new object(), request)];
|
||||||
|
|
||||||
|
public override List<IRequestBundle<object>> ChangeStartAndEndDate(ChangeStartAndEndDateRequest request)
|
||||||
|
{
|
||||||
|
ChangeStartAndEndDateInvocationCount++;
|
||||||
|
return [new TestRequestBundle(new object(), request)];
|
||||||
|
}
|
||||||
|
|
||||||
public override Task<List<NewMailItemPackage>> CreateNewMailPackagesAsync(
|
public override Task<List<NewMailItemPackage>> CreateNewMailPackagesAsync(
|
||||||
object message,
|
object message,
|
||||||
Wino.Core.Domain.Entities.Mail.MailItemFolder assignedFolder,
|
Wino.Core.Domain.Entities.Mail.MailItemFolder assignedFolder,
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Wino.Core.Domain.Entities.Calendar;
|
||||||
|
using Wino.Core.Domain.Enums;
|
||||||
|
|
||||||
|
namespace Wino.Core.Requests.Calendar;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Request to move an existing calendar event by changing its start and end dates.
|
||||||
|
/// The item should already be updated in the local database before this request is queued.
|
||||||
|
/// </summary>
|
||||||
|
public record ChangeStartAndEndDateRequest(CalendarItem Item, List<CalendarEventAttendee> Attendees)
|
||||||
|
: UpdateCalendarEventRequest(Item, Attendees)
|
||||||
|
{
|
||||||
|
public override CalendarSynchronizerOperation Operation => CalendarSynchronizerOperation.ChangeStartAndEndDate;
|
||||||
|
}
|
||||||
@@ -177,6 +177,11 @@ public class WinoRequestDelegator : IWinoRequestDelegator
|
|||||||
OriginalItem = calendarPreparationRequest.OriginalItem,
|
OriginalItem = calendarPreparationRequest.OriginalItem,
|
||||||
OriginalAttendees = calendarPreparationRequest.OriginalAttendees
|
OriginalAttendees = calendarPreparationRequest.OriginalAttendees
|
||||||
},
|
},
|
||||||
|
CalendarSynchronizerOperation.ChangeStartAndEndDate => new ChangeStartAndEndDateRequest(calendarPreparationRequest.CalendarItem, calendarPreparationRequest.Attendees)
|
||||||
|
{
|
||||||
|
OriginalItem = calendarPreparationRequest.OriginalItem,
|
||||||
|
OriginalAttendees = calendarPreparationRequest.OriginalAttendees
|
||||||
|
},
|
||||||
_ => throw new NotImplementedException($"Calendar operation {calendarPreparationRequest.Operation} is not implemented yet.")
|
_ => throw new NotImplementedException($"Calendar operation {calendarPreparationRequest.Operation} is not implemented yet.")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2807,6 +2807,9 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
|
|||||||
return [new HttpRequestBundle<IClientServiceRequest>(updateRequest, request)];
|
return [new HttpRequestBundle<IClientServiceRequest>(updateRequest, request)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override List<IRequestBundle<IClientServiceRequest>> ChangeStartAndEndDate(ChangeStartAndEndDateRequest request)
|
||||||
|
=> UpdateCalendarEvent(request);
|
||||||
|
|
||||||
public override List<IRequestBundle<IClientServiceRequest>> DeleteCalendarEvent(DeleteCalendarEventRequest request)
|
public override List<IRequestBundle<IClientServiceRequest>> DeleteCalendarEvent(DeleteCalendarEventRequest request)
|
||||||
{
|
{
|
||||||
var calendarItem = request.Item;
|
var calendarItem = request.Item;
|
||||||
|
|||||||
@@ -334,6 +334,15 @@ public class ImapSynchronizer : WinoSynchronizer<ImapRequest, ImapMessageCreatio
|
|||||||
handler.RequiresConnectedClient);
|
handler.RequiresConnectedClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override List<IRequestBundle<ImapRequest>> ChangeStartAndEndDate(ChangeStartAndEndDateRequest request)
|
||||||
|
{
|
||||||
|
var handler = ResolveCalendarOperationHandler();
|
||||||
|
return CreateCalendarOperationTaskBundle(
|
||||||
|
request,
|
||||||
|
async value => await handler.UpdateCalendarEventAsync(value).ConfigureAwait(false),
|
||||||
|
handler.RequiresConnectedClient);
|
||||||
|
}
|
||||||
|
|
||||||
public override List<IRequestBundle<ImapRequest>> DeleteCalendarEvent(DeleteCalendarEventRequest request)
|
public override List<IRequestBundle<ImapRequest>> DeleteCalendarEvent(DeleteCalendarEventRequest request)
|
||||||
{
|
{
|
||||||
var handler = ResolveCalendarOperationHandler();
|
var handler = ResolveCalendarOperationHandler();
|
||||||
|
|||||||
@@ -2856,6 +2856,9 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
|
|||||||
return [new HttpRequestBundle<RequestInformation>(updateRequest, request)];
|
return [new HttpRequestBundle<RequestInformation>(updateRequest, request)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override List<IRequestBundle<RequestInformation>> ChangeStartAndEndDate(ChangeStartAndEndDateRequest request)
|
||||||
|
=> UpdateCalendarEvent(request);
|
||||||
|
|
||||||
public override List<IRequestBundle<RequestInformation>> DeleteCalendarEvent(DeleteCalendarEventRequest request)
|
public override List<IRequestBundle<RequestInformation>> DeleteCalendarEvent(DeleteCalendarEventRequest request)
|
||||||
{
|
{
|
||||||
var calendarItem = request.Item;
|
var calendarItem = request.Item;
|
||||||
|
|||||||
@@ -437,6 +437,11 @@ public abstract class WinoSynchronizer<TBaseRequest, TMessageType, TCalendarEven
|
|||||||
.OfType<UpdateCalendarEventRequest>()
|
.OfType<UpdateCalendarEventRequest>()
|
||||||
.SelectMany(UpdateCalendarEvent));
|
.SelectMany(UpdateCalendarEvent));
|
||||||
break;
|
break;
|
||||||
|
case CalendarSynchronizerOperation.ChangeStartAndEndDate:
|
||||||
|
nativeRequests.AddRange(group
|
||||||
|
.OfType<ChangeStartAndEndDateRequest>()
|
||||||
|
.SelectMany(ChangeStartAndEndDate));
|
||||||
|
break;
|
||||||
case CalendarSynchronizerOperation.DeleteEvent:
|
case CalendarSynchronizerOperation.DeleteEvent:
|
||||||
nativeRequests.AddRange(group
|
nativeRequests.AddRange(group
|
||||||
.OfType<DeleteCalendarEventRequest>()
|
.OfType<DeleteCalendarEventRequest>()
|
||||||
@@ -600,6 +605,7 @@ public abstract class WinoSynchronizer<TBaseRequest, TMessageType, TCalendarEven
|
|||||||
|
|
||||||
public virtual List<IRequestBundle<TBaseRequest>> CreateCalendarEvent(CreateCalendarEventRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
|
public virtual List<IRequestBundle<TBaseRequest>> CreateCalendarEvent(CreateCalendarEventRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
|
||||||
public virtual List<IRequestBundle<TBaseRequest>> UpdateCalendarEvent(UpdateCalendarEventRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
|
public virtual List<IRequestBundle<TBaseRequest>> UpdateCalendarEvent(UpdateCalendarEventRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
|
||||||
|
public virtual List<IRequestBundle<TBaseRequest>> ChangeStartAndEndDate(ChangeStartAndEndDateRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
|
||||||
public virtual List<IRequestBundle<TBaseRequest>> DeleteCalendarEvent(DeleteCalendarEventRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
|
public virtual List<IRequestBundle<TBaseRequest>> DeleteCalendarEvent(DeleteCalendarEventRequest 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>> 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>> DeclineEvent(DeclineEventRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
<!-- Reading Page Date/Name Group Header Background -->
|
<!-- Reading Page Date/Name Group Header Background -->
|
||||||
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#ecf0f1</SolidColorBrush>
|
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#ecf0f1</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#B2FCFCFC</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#B2FCFCFC</SolidColorBrush>
|
||||||
|
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#260078D4</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#D9ECEFF1</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#D9ECEFF1</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
|
||||||
|
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
<ResourceDictionary x:Name="Dark">
|
<ResourceDictionary x:Name="Dark">
|
||||||
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#2C2C2C</SolidColorBrush>
|
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#2C2C2C</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#662C2C2C</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#662C2C2C</SolidColorBrush>
|
||||||
|
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#33399BFF</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#992C2C2C</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#992C2C2C</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
<ResourceDictionary x:Name="Light">
|
<ResourceDictionary x:Name="Light">
|
||||||
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b2dffc</SolidColorBrush>
|
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b2dffc</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33B2DFFC</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33B2DFFC</SolidColorBrush>
|
||||||
|
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#59B2DFFC</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66B2DFFC</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66B2DFFC</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
<ResourceDictionary x:Name="Dark">
|
<ResourceDictionary x:Name="Dark">
|
||||||
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b2dffc</SolidColorBrush>
|
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b2dffc</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33B2DFFC</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33B2DFFC</SolidColorBrush>
|
||||||
|
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#59B2DFFC</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66B2DFFC</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66B2DFFC</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
<Color x:Key="MainCustomThemeColor">#D9FFFFFF</Color>
|
<Color x:Key="MainCustomThemeColor">#D9FFFFFF</Color>
|
||||||
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.55" />
|
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.55" />
|
||||||
|
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.7" />
|
||||||
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.85" />
|
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.85" />
|
||||||
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@
|
|||||||
|
|
||||||
<Color x:Key="MainCustomThemeColor">#E61F1F1F</Color>
|
<Color x:Key="MainCustomThemeColor">#E61F1F1F</Color>
|
||||||
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.55" />
|
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.55" />
|
||||||
|
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.7" />
|
||||||
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.85" />
|
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.85" />
|
||||||
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
|
||||||
|
|
||||||
|
|||||||
@@ -14,12 +14,14 @@
|
|||||||
<!-- Reading Page Date/Name Group Header Background -->
|
<!-- Reading Page Date/Name Group Header Background -->
|
||||||
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#ecf0f1</SolidColorBrush>
|
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#ecf0f1</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#F7F9FA</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#F7F9FA</SolidColorBrush>
|
||||||
|
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#260078D4</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#DFE4EA</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#DFE4EA</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
<ResourceDictionary x:Name="Dark">
|
<ResourceDictionary x:Name="Dark">
|
||||||
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#1f1f1f</SolidColorBrush>
|
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#1f1f1f</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#1F1F1F</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#1F1F1F</SolidColorBrush>
|
||||||
|
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#33399BFF</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#262626</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#262626</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
@@ -13,12 +13,14 @@
|
|||||||
<ResourceDictionary x:Name="Light">
|
<ResourceDictionary x:Name="Light">
|
||||||
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#A800D608</SolidColorBrush>
|
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#A800D608</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#2200D608</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#2200D608</SolidColorBrush>
|
||||||
|
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#4D00D608</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#4D00D608</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#4D00D608</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
<ResourceDictionary x:Name="Dark">
|
<ResourceDictionary x:Name="Dark">
|
||||||
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#59001C01</SolidColorBrush>
|
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#59001C01</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#22001C01</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#22001C01</SolidColorBrush>
|
||||||
|
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#6600D608</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#59001C01</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#59001C01</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
<ResourceDictionary x:Name="Light">
|
<ResourceDictionary x:Name="Light">
|
||||||
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#dcfad8</SolidColorBrush>
|
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#dcfad8</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#26DCFAD8</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#26DCFAD8</SolidColorBrush>
|
||||||
|
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#59DCFAD8</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#59DCFAD8</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#59DCFAD8</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarSeperatorBrush">#576574</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarSeperatorBrush">#576574</SolidColorBrush>
|
||||||
@@ -22,6 +23,7 @@
|
|||||||
<ResourceDictionary x:Name="Dark">
|
<ResourceDictionary x:Name="Dark">
|
||||||
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#dcfad8</SolidColorBrush>
|
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#dcfad8</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#26576574</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#26576574</SolidColorBrush>
|
||||||
|
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#59576574</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#59576574</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#59576574</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
<!-- Brushes -->
|
<!-- Brushes -->
|
||||||
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#fdcb6e</SolidColorBrush>
|
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#fdcb6e</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33FDCB6E</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33FDCB6E</SolidColorBrush>
|
||||||
|
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#59FDCB6E</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66FDCB6E</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66FDCB6E</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
<!-- Brushes -->
|
<!-- Brushes -->
|
||||||
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#5413191F</SolidColorBrush>
|
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#5413191F</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#2213191F</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#2213191F</SolidColorBrush>
|
||||||
|
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#4D13191F</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#5413191F</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#5413191F</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
<!-- Brushes -->
|
<!-- Brushes -->
|
||||||
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b0c6dd</SolidColorBrush>
|
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b0c6dd</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33B0C6DD</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33B0C6DD</SolidColorBrush>
|
||||||
|
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#59B0C6DD</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66B0C6DD</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66B0C6DD</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
<!-- Brushes -->
|
<!-- Brushes -->
|
||||||
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b0c6dd</SolidColorBrush>
|
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b0c6dd</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33B0C6DD</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33B0C6DD</SolidColorBrush>
|
||||||
|
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#59B0C6DD</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66B0C6DD</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66B0C6DD</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using Wino.Calendar.ViewModels.Data;
|
||||||
|
|
||||||
|
namespace Wino.Calendar.Controls;
|
||||||
|
|
||||||
|
internal sealed class CalendarDragPackage
|
||||||
|
{
|
||||||
|
public CalendarDragPackage(CalendarItemViewModel calendarItemViewModel)
|
||||||
|
{
|
||||||
|
CalendarItemViewModel = calendarItemViewModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CalendarItemViewModel CalendarItemViewModel { get; }
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ using Microsoft.UI.Xaml;
|
|||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Microsoft.UI.Xaml.Input;
|
using Microsoft.UI.Xaml.Input;
|
||||||
using Microsoft.UI.Xaml.Media;
|
using Microsoft.UI.Xaml.Media;
|
||||||
|
using Windows.ApplicationModel.DataTransfer;
|
||||||
using Wino.Calendar.ViewModels.Data;
|
using Wino.Calendar.ViewModels.Data;
|
||||||
using Wino.Calendar.ViewModels.Messages;
|
using Wino.Calendar.ViewModels.Messages;
|
||||||
using Wino.Core.Domain;
|
using Wino.Core.Domain;
|
||||||
@@ -56,6 +57,8 @@ public sealed partial class CalendarItemControl : UserControl
|
|||||||
|
|
||||||
private void UpdateVisualStates()
|
private void UpdateVisualStates()
|
||||||
{
|
{
|
||||||
|
CanDrag = CalendarItem?.CanDragDrop == true;
|
||||||
|
|
||||||
if (CalendarItem == null) return;
|
if (CalendarItem == null) return;
|
||||||
|
|
||||||
if (CalendarItem.IsAllDayEvent)
|
if (CalendarItem.IsAllDayEvent)
|
||||||
@@ -80,7 +83,25 @@ public sealed partial class CalendarItemControl : UserControl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ControlDragStarting(UIElement sender, DragStartingEventArgs args) => IsDragging = true;
|
private void ControlDragStarting(UIElement sender, DragStartingEventArgs args)
|
||||||
|
{
|
||||||
|
if (CalendarItem?.CanDragDrop != true)
|
||||||
|
{
|
||||||
|
args.Cancel = true;
|
||||||
|
IsDragging = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
args.AllowedOperations = DataPackageOperation.Move;
|
||||||
|
|
||||||
|
var dragPackage = new CalendarDragPackage(CalendarItem);
|
||||||
|
|
||||||
|
args.Data.Properties.Add(nameof(CalendarDragPackage), dragPackage);
|
||||||
|
args.Data.SetText(CalendarItem.DisplayTitle);
|
||||||
|
args.Data.Properties.Title = CalendarItem.DisplayTitle;
|
||||||
|
args.DragUI.SetContentFromDataPackage();
|
||||||
|
IsDragging = true;
|
||||||
|
}
|
||||||
|
|
||||||
private void ControlDropped(UIElement sender, DropCompletedEventArgs args) => IsDragging = false;
|
private void ControlDropped(UIElement sender, DropCompletedEventArgs args) => IsDragging = false;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using Wino.Calendar.ViewModels.Data;
|
||||||
|
|
||||||
|
namespace Wino.Calendar.Controls;
|
||||||
|
|
||||||
|
public enum CalendarDropTargetKind
|
||||||
|
{
|
||||||
|
TimedSlot,
|
||||||
|
TimedAllDay,
|
||||||
|
MonthCell
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class CalendarItemDroppedEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public CalendarItemDroppedEventArgs(
|
||||||
|
CalendarItemViewModel calendarItemViewModel,
|
||||||
|
DateTime targetStart,
|
||||||
|
CalendarDropTargetKind targetKind)
|
||||||
|
{
|
||||||
|
CalendarItemViewModel = calendarItemViewModel;
|
||||||
|
TargetStart = targetStart;
|
||||||
|
TargetKind = targetKind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CalendarItemViewModel CalendarItemViewModel { get; }
|
||||||
|
public DateTime TargetStart { get; }
|
||||||
|
public CalendarDropTargetKind TargetKind { get; }
|
||||||
|
}
|
||||||
@@ -103,8 +103,14 @@
|
|||||||
x:Name="TimedAllDayHost"
|
x:Name="TimedAllDayHost"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
|
AllowDrop="True"
|
||||||
|
DragLeave="CalendarDropTargetDragLeave"
|
||||||
|
DragOver="TimedAllDayHostDragOver"
|
||||||
|
Drop="TimedAllDayHostDrop"
|
||||||
Height="{x:Bind TimedAllDayHeight, Mode=OneWay}"
|
Height="{x:Bind TimedAllDayHeight, Mode=OneWay}"
|
||||||
Background="{ThemeResource LayerFillColorDefaultBrush}"
|
Background="{ThemeResource LayerFillColorDefaultBrush}"
|
||||||
|
PointerExited="CalendarDropTargetPointerExited"
|
||||||
|
PointerMoved="TimedAllDayHostPointerMoved"
|
||||||
Visibility="{x:Bind HasTimedAllDayItems, Mode=OneWay}">
|
Visibility="{x:Bind HasTimedAllDayItems, Mode=OneWay}">
|
||||||
<skia:SKXamlCanvas x:Name="TimedAllDayCanvas" PaintSurface="TimedAllDayCanvasPaintSurface" />
|
<skia:SKXamlCanvas x:Name="TimedAllDayCanvas" PaintSurface="TimedAllDayCanvasPaintSurface" />
|
||||||
<Canvas x:Name="TimedAllDayItemsCanvas" />
|
<Canvas x:Name="TimedAllDayItemsCanvas" />
|
||||||
@@ -134,7 +140,13 @@
|
|||||||
<Grid
|
<Grid
|
||||||
x:Name="TimedViewport"
|
x:Name="TimedViewport"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Height="{x:Bind TimelineHeight, Mode=OneWay}">
|
AllowDrop="True"
|
||||||
|
DragLeave="CalendarDropTargetDragLeave"
|
||||||
|
DragOver="TimedViewportDragOver"
|
||||||
|
Drop="TimedViewportDrop"
|
||||||
|
Height="{x:Bind TimelineHeight, Mode=OneWay}"
|
||||||
|
PointerExited="CalendarDropTargetPointerExited"
|
||||||
|
PointerMoved="TimedViewportPointerMoved">
|
||||||
<skia:SKXamlCanvas x:Name="TimedStructureCanvas" PaintSurface="TimedStructureCanvasPaintSurface" />
|
<skia:SKXamlCanvas x:Name="TimedStructureCanvas" PaintSurface="TimedStructureCanvasPaintSurface" />
|
||||||
<Border
|
<Border
|
||||||
x:Name="TimedInteractionLayer"
|
x:Name="TimedInteractionLayer"
|
||||||
@@ -169,7 +181,13 @@
|
|||||||
<Grid
|
<Grid
|
||||||
x:Name="MonthViewport"
|
x:Name="MonthViewport"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
|
AllowDrop="True"
|
||||||
|
DragLeave="CalendarDropTargetDragLeave"
|
||||||
|
DragOver="MonthViewportDragOver"
|
||||||
|
Drop="MonthViewportDrop"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
|
PointerExited="CalendarDropTargetPointerExited"
|
||||||
|
PointerMoved="MonthViewportPointerMoved"
|
||||||
VerticalAlignment="Stretch">
|
VerticalAlignment="Stretch">
|
||||||
<skia:SKXamlCanvas x:Name="MonthStructureCanvas" PaintSurface="MonthStructureCanvasPaintSurface" />
|
<skia:SKXamlCanvas x:Name="MonthStructureCanvas" PaintSurface="MonthStructureCanvasPaintSurface" />
|
||||||
<Border
|
<Border
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ using Microsoft.UI.Xaml.Input;
|
|||||||
using Microsoft.UI.Xaml.Media;
|
using Microsoft.UI.Xaml.Media;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
using SkiaSharp.Views.Windows;
|
using SkiaSharp.Views.Windows;
|
||||||
|
using Windows.ApplicationModel.DataTransfer;
|
||||||
using Windows.Foundation;
|
using Windows.Foundation;
|
||||||
using Windows.UI;
|
using Windows.UI;
|
||||||
using Wino.Calendar.ViewModels.Data;
|
using Wino.Calendar.ViewModels.Data;
|
||||||
@@ -57,6 +58,8 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
|
|||||||
private bool _hasPresentedState;
|
private bool _hasPresentedState;
|
||||||
private bool _refreshPending = true;
|
private bool _refreshPending = true;
|
||||||
private bool _refreshScheduled;
|
private bool _refreshScheduled;
|
||||||
|
private CalendarDropTargetInfo? _hoverTarget;
|
||||||
|
private CalendarDragPackage? _activeDragPackage;
|
||||||
private CalendarDisplayType _lastDisplayMode = CalendarDisplayType.Month;
|
private CalendarDisplayType _lastDisplayMode = CalendarDisplayType.Month;
|
||||||
private DateOnly _lastDisplayDate = DateOnly.FromDateTime(DateTime.Today);
|
private DateOnly _lastDisplayDate = DateOnly.FromDateTime(DateTime.Today);
|
||||||
private DayOfWeek _lastFirstDayOfWeek = DayOfWeek.Monday;
|
private DayOfWeek _lastFirstDayOfWeek = DayOfWeek.Monday;
|
||||||
@@ -83,6 +86,9 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
|
|||||||
[GeneratedDependencyProperty]
|
[GeneratedDependencyProperty]
|
||||||
public partial Brush? SelectedSlotBackground { get; set; }
|
public partial Brush? SelectedSlotBackground { get; set; }
|
||||||
|
|
||||||
|
[GeneratedDependencyProperty]
|
||||||
|
public partial Brush? HoverSlotBackground { get; set; }
|
||||||
|
|
||||||
[GeneratedDependencyProperty]
|
[GeneratedDependencyProperty]
|
||||||
public partial DateTime? SelectedDateTime { get; set; }
|
public partial DateTime? SelectedDateTime { get; set; }
|
||||||
|
|
||||||
@@ -98,6 +104,7 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
|
|||||||
|
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
public event EventHandler<CalendarEmptySlotTappedEventArgs>? EmptySlotTapped;
|
public event EventHandler<CalendarEmptySlotTappedEventArgs>? EmptySlotTapped;
|
||||||
|
public event EventHandler<CalendarItemDroppedEventArgs>? CalendarItemDropped;
|
||||||
|
|
||||||
private ObservableCollection<HeaderTextLayout> TimedHeaderTextsCollection { get; } = [];
|
private ObservableCollection<HeaderTextLayout> TimedHeaderTextsCollection { get; } = [];
|
||||||
private ObservableCollection<HeaderTextLayout> MonthHeaderTextsCollection { get; } = [];
|
private ObservableCollection<HeaderTextLayout> MonthHeaderTextsCollection { get; } = [];
|
||||||
@@ -182,6 +189,7 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
|
|||||||
partial void OnCalendarSettingsChanged(CalendarSettings? newValue) => RequestRefresh();
|
partial void OnCalendarSettingsChanged(CalendarSettings? newValue) => RequestRefresh();
|
||||||
partial void OnTimedHeaderDateFormatChanged(string? newValue) => RequestRefresh();
|
partial void OnTimedHeaderDateFormatChanged(string? newValue) => RequestRefresh();
|
||||||
partial void OnSelectedSlotBackgroundChanged(Brush? newValue) => InvalidateStructureCanvases();
|
partial void OnSelectedSlotBackgroundChanged(Brush? newValue) => InvalidateStructureCanvases();
|
||||||
|
partial void OnHoverSlotBackgroundChanged(Brush? newValue) => InvalidateStructureCanvases();
|
||||||
partial void OnSelectedDateTimeChanged(DateTime? newValue) => InvalidateStructureCanvases();
|
partial void OnSelectedDateTimeChanged(DateTime? newValue) => InvalidateStructureCanvases();
|
||||||
|
|
||||||
partial void OnCalendarItemsChanged(IReadOnlyList<CalendarItemViewModel>? newValue)
|
partial void OnCalendarItemsChanged(IReadOnlyList<CalendarItemViewModel>? newValue)
|
||||||
@@ -263,6 +271,7 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
|
|||||||
|
|
||||||
private void InvalidateStructureCanvases()
|
private void InvalidateStructureCanvases()
|
||||||
{
|
{
|
||||||
|
TimedAllDayCanvas.Invalidate();
|
||||||
TimedStructureCanvas.Invalidate();
|
TimedStructureCanvas.Invalidate();
|
||||||
MonthStructureCanvas.Invalidate();
|
MonthStructureCanvas.Invalidate();
|
||||||
}
|
}
|
||||||
@@ -511,6 +520,7 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
|
|||||||
private void TimedAllDayCanvasPaintSurface(object? sender, SKPaintSurfaceEventArgs e)
|
private void TimedAllDayCanvasPaintSurface(object? sender, SKPaintSurfaceEventArgs e)
|
||||||
{
|
{
|
||||||
using var borderPaint = CreateLinePaint();
|
using var borderPaint = CreateLinePaint();
|
||||||
|
using var hoverFillPaint = CreateFillPaint(HoverSlotBackground ?? new SolidColorBrush(Colors.Transparent));
|
||||||
var canvas = e.Surface.Canvas;
|
var canvas = e.Surface.Canvas;
|
||||||
canvas.Clear(SKColors.Transparent);
|
canvas.Clear(SKColors.Transparent);
|
||||||
|
|
||||||
@@ -525,6 +535,12 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
|
|||||||
var height = e.Info.Height;
|
var height = e.Info.Height;
|
||||||
var dayWidth = (float)(_timedLayout.DayWidth * scaleX);
|
var dayWidth = (float)(_timedLayout.DayWidth * scaleX);
|
||||||
|
|
||||||
|
var hoveredTimedAllDayRect = GetHoveredTimedAllDayRect(dayWidth, height);
|
||||||
|
if (hoveredTimedAllDayRect.HasValue && hoverFillPaint.Color.Alpha > 0)
|
||||||
|
{
|
||||||
|
canvas.DrawRect(hoveredTimedAllDayRect.Value, hoverFillPaint);
|
||||||
|
}
|
||||||
|
|
||||||
for (var index = 1; index < _timedLayout.VisibleDates.Count; index++)
|
for (var index = 1; index < _timedLayout.VisibleDates.Count; index++)
|
||||||
{
|
{
|
||||||
var x = dayWidth * index;
|
var x = dayWidth * index;
|
||||||
@@ -541,6 +557,7 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
|
|||||||
using var defaultFillPaint = CreateFillPaint(DefaultHourBackground ?? new SolidColorBrush(Colors.Transparent));
|
using var defaultFillPaint = CreateFillPaint(DefaultHourBackground ?? new SolidColorBrush(Colors.Transparent));
|
||||||
using var workFillPaint = CreateFillPaint(WorkHourBackground ?? new SolidColorBrush(Colors.Transparent));
|
using var workFillPaint = CreateFillPaint(WorkHourBackground ?? new SolidColorBrush(Colors.Transparent));
|
||||||
using var selectedFillPaint = CreateFillPaint(SelectedSlotBackground ?? new SolidColorBrush(Colors.Transparent));
|
using var selectedFillPaint = CreateFillPaint(SelectedSlotBackground ?? new SolidColorBrush(Colors.Transparent));
|
||||||
|
using var hoverFillPaint = CreateFillPaint(HoverSlotBackground ?? new SolidColorBrush(Colors.Transparent));
|
||||||
var canvas = e.Surface.Canvas;
|
var canvas = e.Surface.Canvas;
|
||||||
canvas.Clear(SKColors.Transparent);
|
canvas.Clear(SKColors.Transparent);
|
||||||
|
|
||||||
@@ -579,6 +596,12 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hoveredTimedSlotRect = GetHoveredTimedSlotRect(dayWidth, intervalHeight, intervalCount);
|
||||||
|
if (hoveredTimedSlotRect.HasValue && hoverFillPaint.Color.Alpha > 0)
|
||||||
|
{
|
||||||
|
canvas.DrawRect(hoveredTimedSlotRect.Value, hoverFillPaint);
|
||||||
|
}
|
||||||
|
|
||||||
var selectedTimedSlotRect = GetSelectedTimedSlotRect(dayWidth, intervalHeight, intervalCount);
|
var selectedTimedSlotRect = GetSelectedTimedSlotRect(dayWidth, intervalHeight, intervalCount);
|
||||||
if (selectedTimedSlotRect.HasValue && selectedFillPaint.Color.Alpha > 0)
|
if (selectedTimedSlotRect.HasValue && selectedFillPaint.Color.Alpha > 0)
|
||||||
{
|
{
|
||||||
@@ -609,6 +632,7 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
|
|||||||
IsAntialias = true
|
IsAntialias = true
|
||||||
};
|
};
|
||||||
using var selectedPaint = CreateFillPaint(SelectedSlotBackground ?? new SolidColorBrush(Colors.Transparent));
|
using var selectedPaint = CreateFillPaint(SelectedSlotBackground ?? new SolidColorBrush(Colors.Transparent));
|
||||||
|
using var hoverPaint = CreateFillPaint(HoverSlotBackground ?? new SolidColorBrush(Colors.Transparent));
|
||||||
|
|
||||||
var canvas = e.Surface.Canvas;
|
var canvas = e.Surface.Canvas;
|
||||||
canvas.Clear(SKColors.Transparent);
|
canvas.Clear(SKColors.Transparent);
|
||||||
@@ -632,6 +656,12 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
|
|||||||
canvas.DrawRect((float)cell.Bounds.X, (float)cell.Bounds.Y, (float)cell.Bounds.Width, (float)cell.Bounds.Height, todayPaint);
|
canvas.DrawRect((float)cell.Bounds.X, (float)cell.Bounds.Y, (float)cell.Bounds.Width, (float)cell.Bounds.Height, todayPaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hoveredMonthCellRect = GetHoveredMonthCellRect();
|
||||||
|
if (hoveredMonthCellRect.HasValue && hoverPaint.Color.Alpha > 0)
|
||||||
|
{
|
||||||
|
canvas.DrawRect(hoveredMonthCellRect.Value, hoverPaint);
|
||||||
|
}
|
||||||
|
|
||||||
var selectedMonthCellRect = GetSelectedMonthCellRect();
|
var selectedMonthCellRect = GetSelectedMonthCellRect();
|
||||||
if (selectedMonthCellRect.HasValue && selectedPaint.Color.Alpha > 0)
|
if (selectedMonthCellRect.HasValue && selectedPaint.Color.Alpha > 0)
|
||||||
{
|
{
|
||||||
@@ -814,6 +844,220 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
|
|||||||
new Size(cell.Bounds.Width, cell.Bounds.Height)));
|
new Size(cell.Bounds.Width, cell.Bounds.Height)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TimedViewportPointerMoved(object sender, PointerRoutedEventArgs e)
|
||||||
|
=> SetHoverTarget(ResolveTimedDropTarget(e.GetCurrentPoint(TimedViewport).Position, _activeDragPackage?.CalendarItemViewModel));
|
||||||
|
|
||||||
|
private void TimedAllDayHostPointerMoved(object sender, PointerRoutedEventArgs e)
|
||||||
|
=> SetHoverTarget(ResolveTimedAllDayDropTarget(e.GetCurrentPoint(TimedAllDayHost).Position, _activeDragPackage?.CalendarItemViewModel));
|
||||||
|
|
||||||
|
private void MonthViewportPointerMoved(object sender, PointerRoutedEventArgs e)
|
||||||
|
=> SetHoverTarget(ResolveMonthDropTarget(e.GetCurrentPoint(MonthViewport).Position, _activeDragPackage?.CalendarItemViewModel));
|
||||||
|
|
||||||
|
private void CalendarDropTargetPointerExited(object sender, PointerRoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (_activeDragPackage == null)
|
||||||
|
{
|
||||||
|
SetHoverTarget(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TimedViewportDragOver(object sender, DragEventArgs e)
|
||||||
|
{
|
||||||
|
if (!TryGetDragPackage(e, out var dragPackage))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hoverTarget = ResolveTimedDropTarget(e.GetPosition(TimedViewport), dragPackage.CalendarItemViewModel);
|
||||||
|
|
||||||
|
UpdateDragOverState(e, dragPackage, hoverTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TimedAllDayHostDragOver(object sender, DragEventArgs e)
|
||||||
|
{
|
||||||
|
if (!TryGetDragPackage(e, out var dragPackage))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hoverTarget = ResolveTimedAllDayDropTarget(e.GetPosition(TimedAllDayHost), dragPackage.CalendarItemViewModel);
|
||||||
|
|
||||||
|
UpdateDragOverState(e, dragPackage, hoverTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MonthViewportDragOver(object sender, DragEventArgs e)
|
||||||
|
{
|
||||||
|
if (!TryGetDragPackage(e, out var dragPackage))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hoverTarget = ResolveMonthDropTarget(e.GetPosition(MonthViewport), dragPackage.CalendarItemViewModel);
|
||||||
|
|
||||||
|
UpdateDragOverState(e, dragPackage, hoverTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TimedViewportDrop(object sender, DragEventArgs e)
|
||||||
|
=> HandleDrop(e, ResolveTimedDropTarget(e.GetPosition(TimedViewport), _activeDragPackage?.CalendarItemViewModel));
|
||||||
|
|
||||||
|
private void TimedAllDayHostDrop(object sender, DragEventArgs e)
|
||||||
|
=> HandleDrop(e, ResolveTimedAllDayDropTarget(e.GetPosition(TimedAllDayHost), _activeDragPackage?.CalendarItemViewModel));
|
||||||
|
|
||||||
|
private void MonthViewportDrop(object sender, DragEventArgs e)
|
||||||
|
=> HandleDrop(e, ResolveMonthDropTarget(e.GetPosition(MonthViewport), _activeDragPackage?.CalendarItemViewModel));
|
||||||
|
|
||||||
|
private void CalendarDropTargetDragLeave(object sender, DragEventArgs e)
|
||||||
|
{
|
||||||
|
_activeDragPackage = null;
|
||||||
|
SetHoverTarget(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetDragPackage(DragEventArgs e, out CalendarDragPackage dragPackage)
|
||||||
|
{
|
||||||
|
dragPackage = null;
|
||||||
|
|
||||||
|
if (!e.DataView.Properties.ContainsKey(nameof(CalendarDragPackage)))
|
||||||
|
{
|
||||||
|
e.AcceptedOperation = DataPackageOperation.None;
|
||||||
|
_activeDragPackage = null;
|
||||||
|
SetHoverTarget(null);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dragPackage = e.DataView.Properties[nameof(CalendarDragPackage)] as CalendarDragPackage;
|
||||||
|
|
||||||
|
if (dragPackage?.CalendarItemViewModel?.CanDragDrop != true)
|
||||||
|
{
|
||||||
|
e.AcceptedOperation = DataPackageOperation.None;
|
||||||
|
_activeDragPackage = null;
|
||||||
|
SetHoverTarget(null);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateDragOverState(DragEventArgs e, CalendarDragPackage dragPackage, CalendarDropTargetInfo? hoverTarget)
|
||||||
|
{
|
||||||
|
_activeDragPackage = dragPackage;
|
||||||
|
SetHoverTarget(hoverTarget);
|
||||||
|
|
||||||
|
if (hoverTarget.HasValue)
|
||||||
|
{
|
||||||
|
e.AcceptedOperation = DataPackageOperation.Move;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
e.AcceptedOperation = DataPackageOperation.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleDrop(DragEventArgs e, CalendarDropTargetInfo? hoverTarget)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_activeDragPackage?.CalendarItemViewModel?.CanDragDrop != true || !hoverTarget.HasValue)
|
||||||
|
{
|
||||||
|
e.AcceptedOperation = DataPackageOperation.None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.AcceptedOperation = DataPackageOperation.Move;
|
||||||
|
CalendarItemDropped?.Invoke(
|
||||||
|
this,
|
||||||
|
new CalendarItemDroppedEventArgs(
|
||||||
|
_activeDragPackage.CalendarItemViewModel,
|
||||||
|
hoverTarget.Value.TargetStart,
|
||||||
|
hoverTarget.Value.Kind));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_activeDragPackage = null;
|
||||||
|
SetHoverTarget(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private CalendarDropTargetInfo? ResolveTimedDropTarget(Point position, CalendarItemViewModel? draggedItem)
|
||||||
|
{
|
||||||
|
if (draggedItem?.IsAllDayEvent == true ||
|
||||||
|
_timedLayout.VisibleDates.Count == 0 ||
|
||||||
|
_timedLayout.DayWidth <= 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dayIndex = Math.Clamp((int)(position.X / _timedLayout.DayWidth), 0, _timedLayout.VisibleDates.Count - 1);
|
||||||
|
var intervalHeight = GetTimedSelectionIntervalHeight();
|
||||||
|
var slotIndex = Math.Clamp((int)(position.Y / intervalHeight), 0, (int)((24d * 60d / TimedSelectionIntervalMinutes) - 1));
|
||||||
|
var date = _timedLayout.VisibleDates[dayIndex];
|
||||||
|
var slotStart = TimeSpan.FromMinutes(slotIndex * TimedSelectionIntervalMinutes);
|
||||||
|
|
||||||
|
return new CalendarDropTargetInfo(
|
||||||
|
CalendarDropTargetKind.TimedSlot,
|
||||||
|
date,
|
||||||
|
dayIndex,
|
||||||
|
slotIndex,
|
||||||
|
date.ToDateTime(TimeOnly.MinValue).Add(slotStart));
|
||||||
|
}
|
||||||
|
|
||||||
|
private CalendarDropTargetInfo? ResolveTimedAllDayDropTarget(Point position, CalendarItemViewModel? draggedItem)
|
||||||
|
{
|
||||||
|
if (draggedItem is { IsAllDayEvent: false } ||
|
||||||
|
_timedLayout.VisibleDates.Count == 0 ||
|
||||||
|
_timedLayout.DayWidth <= 0 ||
|
||||||
|
TimedAllDayHeight <= 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dayIndex = Math.Clamp((int)(position.X / _timedLayout.DayWidth), 0, _timedLayout.VisibleDates.Count - 1);
|
||||||
|
var date = _timedLayout.VisibleDates[dayIndex];
|
||||||
|
|
||||||
|
return new CalendarDropTargetInfo(
|
||||||
|
CalendarDropTargetKind.TimedAllDay,
|
||||||
|
date,
|
||||||
|
dayIndex,
|
||||||
|
-1,
|
||||||
|
date.ToDateTime(TimeOnly.MinValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
private CalendarDropTargetInfo? ResolveMonthDropTarget(Point position, CalendarItemViewModel? draggedItem)
|
||||||
|
{
|
||||||
|
if (_monthLayout.Cells.Count == 0 || _monthLayout.CellWidth <= 0 || _monthLayout.CellHeight <= 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var column = Math.Clamp((int)(position.X / _monthLayout.CellWidth), 0, MonthCalendarLayoutCalculator.ColumnCount - 1);
|
||||||
|
var row = Math.Clamp((int)(position.Y / _monthLayout.CellHeight), 0, MonthCalendarLayoutCalculator.RowCount - 1);
|
||||||
|
var cellIndex = Math.Clamp((row * MonthCalendarLayoutCalculator.ColumnCount) + column, 0, _monthLayout.Cells.Count - 1);
|
||||||
|
var cell = _monthLayout.Cells[cellIndex];
|
||||||
|
var targetStart = cell.Date.ToDateTime(TimeOnly.MinValue);
|
||||||
|
|
||||||
|
if (draggedItem is { IsAllDayEvent: false })
|
||||||
|
{
|
||||||
|
targetStart = targetStart.Add(draggedItem.StartDate.TimeOfDay);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CalendarDropTargetInfo(
|
||||||
|
CalendarDropTargetKind.MonthCell,
|
||||||
|
cell.Date,
|
||||||
|
-1,
|
||||||
|
-1,
|
||||||
|
targetStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetHoverTarget(CalendarDropTargetInfo? hoverTarget)
|
||||||
|
{
|
||||||
|
if (_hoverTarget == hoverTarget)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_hoverTarget = hoverTarget;
|
||||||
|
InvalidateStructureCanvases();
|
||||||
|
}
|
||||||
|
|
||||||
private SKRect? GetSelectedTimedSlotRect(float dayWidth, float intervalHeight, int intervalCount)
|
private SKRect? GetSelectedTimedSlotRect(float dayWidth, float intervalHeight, int intervalCount)
|
||||||
{
|
{
|
||||||
if (SelectedDateTime is not DateTime selectedDateTime || _timedLayout.VisibleDates.Count == 0)
|
if (SelectedDateTime is not DateTime selectedDateTime || _timedLayout.VisibleDates.Count == 0)
|
||||||
@@ -835,6 +1079,30 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
|
|||||||
return new SKRect(x, y, x + dayWidth, y + intervalHeight);
|
return new SKRect(x, y, x + dayWidth, y + intervalHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SKRect? GetHoveredTimedSlotRect(float dayWidth, float intervalHeight, int intervalCount)
|
||||||
|
{
|
||||||
|
if (_hoverTarget is not { Kind: CalendarDropTargetKind.TimedSlot } hoverTarget)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var slotIndex = Math.Clamp(hoverTarget.SlotIndex, 0, intervalCount - 1);
|
||||||
|
var x = hoverTarget.DayIndex * dayWidth;
|
||||||
|
var y = slotIndex * intervalHeight;
|
||||||
|
return new SKRect(x, y, x + dayWidth, y + intervalHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SKRect? GetHoveredTimedAllDayRect(float dayWidth, float height)
|
||||||
|
{
|
||||||
|
if (_hoverTarget is not { Kind: CalendarDropTargetKind.TimedAllDay } hoverTarget)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var x = hoverTarget.DayIndex * dayWidth;
|
||||||
|
return new SKRect(x, 0, x + dayWidth, height);
|
||||||
|
}
|
||||||
|
|
||||||
private SKRect? GetSelectedMonthCellRect()
|
private SKRect? GetSelectedMonthCellRect()
|
||||||
{
|
{
|
||||||
if (SelectedDateTime is not DateTime selectedDateTime)
|
if (SelectedDateTime is not DateTime selectedDateTime)
|
||||||
@@ -860,6 +1128,30 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SKRect? GetHoveredMonthCellRect()
|
||||||
|
{
|
||||||
|
if (_hoverTarget is not { Kind: CalendarDropTargetKind.MonthCell } hoverTarget)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var cell in _monthLayout.Cells)
|
||||||
|
{
|
||||||
|
if (cell.Date != hoverTarget.Date)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SKRect(
|
||||||
|
(float)cell.Bounds.X,
|
||||||
|
(float)cell.Bounds.Y,
|
||||||
|
(float)(cell.Bounds.X + cell.Bounds.Width),
|
||||||
|
(float)(cell.Bounds.Y + cell.Bounds.Height));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private int FindVisibleDateIndex(DateOnly date)
|
private int FindVisibleDateIndex(DateOnly date)
|
||||||
{
|
{
|
||||||
for (var index = 0; index < _timedLayout.VisibleDates.Count; index++)
|
for (var index = 0; index < _timedLayout.VisibleDates.Count; index++)
|
||||||
@@ -1229,6 +1521,13 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
|
|||||||
|
|
||||||
private readonly record struct CalendarTransitionInfo(CalendarTransitionKind Kind, int Direction);
|
private readonly record struct CalendarTransitionInfo(CalendarTransitionKind Kind, int Direction);
|
||||||
|
|
||||||
|
private readonly record struct CalendarDropTargetInfo(
|
||||||
|
CalendarDropTargetKind Kind,
|
||||||
|
DateOnly Date,
|
||||||
|
int DayIndex,
|
||||||
|
int SlotIndex,
|
||||||
|
DateTime TargetStart);
|
||||||
|
|
||||||
private enum CalendarTransitionKind
|
private enum CalendarTransitionKind
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
|||||||
@@ -41,8 +41,10 @@
|
|||||||
x:Name="CalendarSurface"
|
x:Name="CalendarSurface"
|
||||||
CalendarItems="{x:Bind ViewModel.CalendarItems, Mode=OneWay}"
|
CalendarItems="{x:Bind ViewModel.CalendarItems, Mode=OneWay}"
|
||||||
CalendarSettings="{x:Bind ViewModel.CurrentSettings, Mode=OneWay}"
|
CalendarSettings="{x:Bind ViewModel.CurrentSettings, Mode=OneWay}"
|
||||||
|
CalendarItemDropped="CalendarSurfaceCalendarItemDropped"
|
||||||
DefaultHourBackground="{ThemeResource CalendarDefaultHourBackgroundBrush}"
|
DefaultHourBackground="{ThemeResource CalendarDefaultHourBackgroundBrush}"
|
||||||
EmptySlotTapped="CalendarSurfaceEmptySlotTapped"
|
EmptySlotTapped="CalendarSurfaceEmptySlotTapped"
|
||||||
|
HoverSlotBackground="{ThemeResource CalendarHoverHourBackgroundBrush}"
|
||||||
IsEnabled="{x:Bind ViewModel.IsCalendarEnabled, Mode=OneWay}"
|
IsEnabled="{x:Bind ViewModel.IsCalendarEnabled, Mode=OneWay}"
|
||||||
SelectedDateTime="{x:Bind ViewModel.SelectedQuickEventDate, Mode=OneWay}"
|
SelectedDateTime="{x:Bind ViewModel.SelectedQuickEventDate, Mode=OneWay}"
|
||||||
SelectedSlotBackground="{ThemeResource CalendarSelectedHourBackgroundBrush}"
|
SelectedSlotBackground="{ThemeResource CalendarSelectedHourBackgroundBrush}"
|
||||||
|
|||||||
@@ -173,6 +173,9 @@ public sealed partial class CalendarPage : CalendarPageAbstract, ITitleBarSearch
|
|||||||
_suppressSelectionResetOnPopupClose = false;
|
_suppressSelectionResetOnPopupClose = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void CalendarSurfaceCalendarItemDropped(object sender, CalendarItemDroppedEventArgs e)
|
||||||
|
=> await ViewModel.MoveCalendarItemAsync(e.CalendarItemViewModel, e.TargetStart);
|
||||||
|
|
||||||
private void QuickEventAccountSelectorSelectionChanged(object sender, SelectionChangedEventArgs e)
|
private void QuickEventAccountSelectorSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
=> QuickEventAccountSelectorFlyout.Hide();
|
=> QuickEventAccountSelectorFlyout.Hide();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user