Handle read-only calendars
This commit is contained in:
@@ -86,6 +86,7 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
|
||||
get
|
||||
{
|
||||
if (SelectedQuickEventAccountCalendar == null ||
|
||||
SelectedQuickEventAccountCalendar.IsReadOnly ||
|
||||
SelectedQuickEventDate == null ||
|
||||
string.IsNullOrWhiteSpace(EventName) ||
|
||||
string.IsNullOrWhiteSpace(SelectedStartTimeString) ||
|
||||
@@ -204,6 +205,12 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
|
||||
if (DisplayDetailsCalendarItemViewModel?.CalendarItem == null)
|
||||
return;
|
||||
|
||||
if (DisplayDetailsCalendarItemViewModel.AssignedCalendar?.IsReadOnly == true)
|
||||
{
|
||||
_dialogService.ShowReadOnlyCalendarMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
if (DisplayDetailsCalendarItemViewModel.CalendarItem.IsRecurringParent)
|
||||
{
|
||||
var confirmed = await _dialogService.ShowConfirmationDialogAsync(
|
||||
@@ -460,6 +467,12 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
|
||||
[RelayCommand(AllowConcurrentExecutions = false, CanExecute = nameof(CanSaveQuickEvent))]
|
||||
private async Task SaveQuickEventAsync()
|
||||
{
|
||||
if (SelectedQuickEventAccountCalendar?.IsReadOnly == true)
|
||||
{
|
||||
_dialogService.ShowReadOnlyCalendarMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
var startDate = IsAllDay ? SelectedQuickEventDate.Value.Date : QuickEventStartTime;
|
||||
var endDate = IsAllDay ? SelectedQuickEventDate.Value.Date.AddDays(1) : QuickEventEndTime;
|
||||
var composeResult = new CalendarEventComposeResult
|
||||
@@ -553,6 +566,12 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
|
||||
return;
|
||||
}
|
||||
|
||||
if (calendarItem.AssignedCalendar?.IsReadOnly == true)
|
||||
{
|
||||
_dialogService.ShowReadOnlyCalendarMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
var normalizedTargetStart = calendarItem.IsAllDayEvent
|
||||
? targetStart.Date
|
||||
: targetStart;
|
||||
@@ -1195,6 +1214,12 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
|
||||
if (targetItem == null)
|
||||
return;
|
||||
|
||||
if (targetItem.AssignedCalendar?.IsReadOnly == true)
|
||||
{
|
||||
_dialogService.ShowReadOnlyCalendarMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetItem.IsRecurringParent)
|
||||
{
|
||||
var confirmed = await _dialogService.ShowConfirmationDialogAsync(
|
||||
@@ -1221,6 +1246,12 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
|
||||
if (targetItem == null || targetItem.ShowAs == showAs)
|
||||
return;
|
||||
|
||||
if (targetItem.AssignedCalendar?.IsReadOnly == true)
|
||||
{
|
||||
_dialogService.ShowReadOnlyCalendarMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
var originalItem = await _calendarService.GetCalendarItemAsync(targetItem.Id).ConfigureAwait(false);
|
||||
var attendees = await _calendarService.GetAttendeesAsync(targetItem.Id).ConfigureAwait(false);
|
||||
|
||||
@@ -1245,6 +1276,12 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
|
||||
if (targetItem == null)
|
||||
return;
|
||||
|
||||
if (targetItem.AssignedCalendar?.IsReadOnly == true)
|
||||
{
|
||||
_dialogService.ShowReadOnlyCalendarMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
var operation = responseStatus switch
|
||||
{
|
||||
CalendarItemStatus.Accepted => CalendarSynchronizerOperation.AcceptEvent,
|
||||
|
||||
@@ -55,6 +55,12 @@ public partial class AccountCalendarViewModel : ObservableObject, IAccountCalend
|
||||
set => SetProperty(AccountCalendar.IsPrimary, value, AccountCalendar, (u, i) => u.IsPrimary = i);
|
||||
}
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get => AccountCalendar.IsReadOnly;
|
||||
set => SetProperty(AccountCalendar.IsReadOnly, value, AccountCalendar, (u, i) => u.IsReadOnly = i);
|
||||
}
|
||||
|
||||
public bool IsSynchronizationEnabled
|
||||
{
|
||||
get => AccountCalendar.IsSynchronizationEnabled;
|
||||
|
||||
@@ -440,6 +440,11 @@ public partial class EventDetailsPageViewModel : CalendarBaseViewModel
|
||||
private async Task SaveAsync()
|
||||
{
|
||||
if (CurrentEvent == null) return;
|
||||
if (CurrentEvent.AssignedCalendar?.IsReadOnly == true)
|
||||
{
|
||||
_dialogService.ShowReadOnlyCalendarMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -506,6 +511,11 @@ public partial class EventDetailsPageViewModel : CalendarBaseViewModel
|
||||
private async Task DeleteAsync()
|
||||
{
|
||||
if (CurrentEvent == null) return;
|
||||
if (CurrentEvent.AssignedCalendar?.IsReadOnly == true)
|
||||
{
|
||||
_dialogService.ShowReadOnlyCalendarMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
// If the event is a master recurring event, ask for confirmation
|
||||
if (CurrentEvent.IsRecurringParent)
|
||||
@@ -610,6 +620,11 @@ public partial class EventDetailsPageViewModel : CalendarBaseViewModel
|
||||
private async Task SendRsvpResponse(AttendeeStatus status)
|
||||
{
|
||||
if (CurrentEvent == null) return;
|
||||
if (CurrentEvent.AssignedCalendar?.IsReadOnly == true)
|
||||
{
|
||||
_dialogService.ShowReadOnlyCalendarMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -16,6 +16,7 @@ public class AccountCalendar : IAccountCalendar
|
||||
public string SynchronizationDeltaToken { get; set; }
|
||||
public string Name { get; set; }
|
||||
public bool IsPrimary { get; set; }
|
||||
public bool IsReadOnly { get; set; }
|
||||
public bool IsSynchronizationEnabled { get; set; } = true;
|
||||
public bool IsExtended { get; set; } = true;
|
||||
public CalendarItemShowAs DefaultShowAs { get; set; } = CalendarItemShowAs.Busy;
|
||||
|
||||
@@ -10,6 +10,7 @@ public interface IAccountCalendar
|
||||
string TextColorHex { get; set; }
|
||||
string BackgroundColorHex { get; set; }
|
||||
bool IsPrimary { get; set; }
|
||||
bool IsReadOnly { get; set; }
|
||||
bool IsSynchronizationEnabled { get; set; }
|
||||
Guid AccountId { get; set; }
|
||||
string RemoteCalendarId { get; set; }
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace Wino.Core.Domain.Interfaces;
|
||||
|
||||
public interface IMailDialogService : IDialogServiceBase
|
||||
{
|
||||
void ShowReadOnlyCalendarMessage();
|
||||
Task<bool> ShowHardDeleteConfirmationAsync();
|
||||
Task HandleSystemFolderConfigurationDialogAsync(Guid accountId, IFolderService folderService);
|
||||
|
||||
|
||||
@@ -213,6 +213,8 @@
|
||||
"CalendarEventDetails_Organizer": "Organizer",
|
||||
"CalendarEventDetails_People": "People",
|
||||
"CalendarEventDetails_ReadOnlyEvent": "Read-only event",
|
||||
"CalendarReadOnly_Title": "Read-only calendar",
|
||||
"CalendarReadOnly_Message": "You can't update this calendar or its events. This calendar is read-only.",
|
||||
"CalendarContextMenu_Respond": "Respond",
|
||||
"CalendarEventDetails_Reminder": "Reminder",
|
||||
"CalendarReminder_StartedHoursAgo": "Started {0} hours ago",
|
||||
|
||||
@@ -145,6 +145,8 @@ public static class GoogleIntegratorExtensions
|
||||
Id = Guid.NewGuid(),
|
||||
TimeZone = calendarListEntry.TimeZone,
|
||||
IsPrimary = calendarListEntry.Primary.GetValueOrDefault(),
|
||||
IsReadOnly = !string.Equals(calendarListEntry.AccessRole, "owner", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(calendarListEntry.AccessRole, "writer", StringComparison.OrdinalIgnoreCase),
|
||||
IsSynchronizationEnabled = true,
|
||||
};
|
||||
|
||||
|
||||
@@ -190,6 +190,7 @@ public static class OutlookIntegratorExtensions
|
||||
Id = Guid.NewGuid(),
|
||||
RemoteCalendarId = outlookCalendar.Id,
|
||||
IsPrimary = outlookCalendar.IsDefaultCalendar.GetValueOrDefault(),
|
||||
IsReadOnly = !outlookCalendar.CanEdit.GetValueOrDefault(true),
|
||||
Name = outlookCalendar.Name,
|
||||
IsSynchronizationEnabled = true,
|
||||
IsExtended = true,
|
||||
|
||||
@@ -165,6 +165,13 @@ public class WinoRequestDelegator : IWinoRequestDelegator
|
||||
if (calendarPreparationRequest == null)
|
||||
return;
|
||||
|
||||
var resolvedCalendar = await ResolveCalendarAsync(calendarPreparationRequest).ConfigureAwait(false);
|
||||
if (resolvedCalendar?.IsReadOnly == true)
|
||||
{
|
||||
_dialogService.ShowReadOnlyCalendarMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
IRequestBase request = calendarPreparationRequest.Operation switch
|
||||
{
|
||||
CalendarSynchronizerOperation.CreateEvent => await CreateCalendarEventRequestAsync(calendarPreparationRequest).ConfigureAwait(false),
|
||||
@@ -212,6 +219,25 @@ public class WinoRequestDelegator : IWinoRequestDelegator
|
||||
return new CreateCalendarEventRequest(composeResult, assignedCalendar);
|
||||
}
|
||||
|
||||
private async Task<AccountCalendar> ResolveCalendarAsync(CalendarOperationPreparationRequest calendarPreparationRequest)
|
||||
{
|
||||
if (calendarPreparationRequest.Operation == CalendarSynchronizerOperation.CreateEvent)
|
||||
{
|
||||
var calendarId = calendarPreparationRequest.ComposeResult?.CalendarId ?? Guid.Empty;
|
||||
return calendarId == Guid.Empty
|
||||
? null
|
||||
: await _calendarService.GetAccountCalendarAsync(calendarId).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (calendarPreparationRequest.CalendarItem?.AssignedCalendar is AccountCalendar assignedCalendar)
|
||||
return assignedCalendar;
|
||||
|
||||
var fallbackCalendarId = calendarPreparationRequest.CalendarItem?.CalendarId ?? Guid.Empty;
|
||||
return fallbackCalendarId == Guid.Empty
|
||||
? null
|
||||
: await _calendarService.GetAccountCalendarAsync(fallbackCalendarId).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private IRequestBase CreateDeclineRequest(CalendarItem calendarItem, string responseMessage)
|
||||
{
|
||||
// For Outlook accounts, declined events are deleted by the server after synchronization.
|
||||
|
||||
@@ -759,6 +759,8 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
|
||||
existingLocalCalendar.BackgroundColorHex = resolvedColor;
|
||||
existingLocalCalendar.TextColorHex = ColorHelpers.GetReadableTextColorHex(existingLocalCalendar.BackgroundColorHex);
|
||||
existingLocalCalendar.IsPrimary = string.Equals(existingLocalCalendar.RemoteCalendarId, remotePrimaryCalendarId, StringComparison.OrdinalIgnoreCase);
|
||||
existingLocalCalendar.IsReadOnly = !string.Equals(calendar.AccessRole, "owner", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(calendar.AccessRole, "writer", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
updatedCalendars.Add(existingLocalCalendar);
|
||||
}
|
||||
@@ -940,14 +942,17 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
|
||||
var remoteBackgroundColor = ResolveSynchronizedCalendarBackgroundColor(GetRemoteGmailCalendarBackgroundColor(calendarListEntry), accountCalendar);
|
||||
var remoteTextColor = ColorHelpers.GetReadableTextColorHex(remoteBackgroundColor);
|
||||
var remoteIsPrimary = string.Equals(calendarListEntry.Id, remotePrimaryCalendarId, StringComparison.OrdinalIgnoreCase);
|
||||
var remoteIsReadOnly = !string.Equals(calendarListEntry.AccessRole, "owner", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(calendarListEntry.AccessRole, "writer", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
bool isNameChanged = !string.Equals(accountCalendar.Name, remoteCalendarName, StringComparison.OrdinalIgnoreCase);
|
||||
bool isTimeZoneChanged = !string.Equals(accountCalendar.TimeZone, remoteTimeZone, StringComparison.OrdinalIgnoreCase);
|
||||
bool isBackgroundColorChanged = !string.Equals(accountCalendar.BackgroundColorHex, remoteBackgroundColor, StringComparison.OrdinalIgnoreCase);
|
||||
bool isTextColorChanged = !string.Equals(accountCalendar.TextColorHex, remoteTextColor, StringComparison.OrdinalIgnoreCase);
|
||||
bool isPrimaryChanged = accountCalendar.IsPrimary != remoteIsPrimary;
|
||||
bool isReadOnlyChanged = accountCalendar.IsReadOnly != remoteIsReadOnly;
|
||||
|
||||
return isNameChanged || isTimeZoneChanged || isBackgroundColorChanged || isTextColorChanged || isPrimaryChanged;
|
||||
return isNameChanged || isTimeZoneChanged || isBackgroundColorChanged || isTextColorChanged || isPrimaryChanged || isReadOnlyChanged;
|
||||
}
|
||||
|
||||
private static string GetRemoteGmailCalendarBackgroundColor(CalendarListEntry calendarListEntry)
|
||||
|
||||
@@ -2674,6 +2674,7 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
|
||||
{
|
||||
existingLocalCalendar.Name = calendar.Name;
|
||||
existingLocalCalendar.IsPrimary = string.Equals(existingLocalCalendar.RemoteCalendarId, remotePrimaryCalendarId, StringComparison.OrdinalIgnoreCase);
|
||||
existingLocalCalendar.IsReadOnly = !calendar.CanEdit.GetValueOrDefault(true);
|
||||
existingLocalCalendar.BackgroundColorHex = resolvedColor;
|
||||
existingLocalCalendar.TextColorHex = ColorHelpers.GetReadableTextColorHex(existingLocalCalendar.BackgroundColorHex);
|
||||
|
||||
@@ -2712,12 +2713,14 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
|
||||
var remoteCalendarName = calendar.Name;
|
||||
var remoteBackgroundColor = ResolveSynchronizedCalendarBackgroundColor(GetRemoteOutlookCalendarBackgroundColor(calendar), accountCalendar);
|
||||
var remoteIsPrimary = string.Equals(calendar.Id, remotePrimaryCalendarId, StringComparison.OrdinalIgnoreCase);
|
||||
var remoteIsReadOnly = !calendar.CanEdit.GetValueOrDefault(true);
|
||||
|
||||
bool isNameChanged = !string.Equals(accountCalendar.Name, remoteCalendarName, StringComparison.OrdinalIgnoreCase);
|
||||
bool isBackgroundColorChanged = !string.Equals(accountCalendar.BackgroundColorHex, remoteBackgroundColor, StringComparison.OrdinalIgnoreCase);
|
||||
bool isPrimaryChanged = accountCalendar.IsPrimary != remoteIsPrimary;
|
||||
bool isReadOnlyChanged = accountCalendar.IsReadOnly != remoteIsReadOnly;
|
||||
|
||||
return isNameChanged || isBackgroundColorChanged || isPrimaryChanged;
|
||||
return isNameChanged || isBackgroundColorChanged || isPrimaryChanged || isReadOnlyChanged;
|
||||
}
|
||||
|
||||
private static string GetRemoteOutlookCalendarBackgroundColor(Calendar calendar)
|
||||
|
||||
@@ -40,6 +40,12 @@ public class DialogService : DialogServiceBase, IMailDialogService
|
||||
_winoAccountDataSyncService = winoAccountDataSyncService;
|
||||
}
|
||||
|
||||
public void ShowReadOnlyCalendarMessage()
|
||||
=> InfoBarMessage(
|
||||
Translator.CalendarReadOnly_Title,
|
||||
Translator.CalendarReadOnly_Message,
|
||||
InfoBarMessageType.Warning);
|
||||
|
||||
public async Task<ICreateAccountAliasDialog> ShowCreateAccountAliasDialogAsync()
|
||||
{
|
||||
var createAccountAliasDialog = new CreateAccountAliasDialog()
|
||||
|
||||
@@ -168,6 +168,13 @@ public class DatabaseService : IDatabaseService
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (!accountCalendarColumns.Any(c => c.Name == nameof(AccountCalendar.IsReadOnly)))
|
||||
{
|
||||
await Connection
|
||||
.ExecuteAsync($"ALTER TABLE {nameof(AccountCalendar)} ADD COLUMN {nameof(AccountCalendar.IsReadOnly)} INTEGER NOT NULL DEFAULT 0")
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await Connection.ExecuteAsync("DROP TABLE IF EXISTS WinoAccountAddOnCache").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user