UI visuals for mail calendar items, calendar reminders.

This commit is contained in:
Burak Kaan Köse
2026-02-11 01:49:29 +01:00
parent 870a5e2bf6
commit 52ee5f1d8a
21 changed files with 639 additions and 77 deletions
+3
View File
@@ -13,6 +13,9 @@ public static class Constants
public const string ToastMailUniqueIdKey = nameof(ToastMailUniqueIdKey);
public const string ToastActionKey = nameof(ToastActionKey);
public const string ToastMailAccountIdKey = nameof(ToastMailAccountIdKey);
public const string ToastCalendarItemIdKey = nameof(ToastCalendarItemIdKey);
public const string ToastCalendarActionKey = nameof(ToastCalendarActionKey);
public const string ToastCalendarNavigateAction = nameof(ToastCalendarNavigateAction);
public const string ClientLogFile = "Client_.log";
public const string ServerLogFile = "Server_.log";
@@ -3,6 +3,7 @@ using System.Diagnostics;
using Itenso.TimePeriod;
using SQLite;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Extensions;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Calendar;
@@ -168,28 +169,7 @@ public class CalendarItem : ICalendarItem
{
get
{
if (string.IsNullOrEmpty(StartTimeZone))
{
// No timezone info, return as-is
return StartDate;
}
try
{
var sourceTimeZone = TimeZoneInfo.FindSystemTimeZoneById(StartTimeZone);
var localTimeZone = TimeZoneInfo.Local;
// Ensure DateTime is Unspecified kind before conversion
var unspecifiedDateTime = DateTime.SpecifyKind(StartDate, DateTimeKind.Unspecified);
// Convert from source timezone to local timezone
return TimeZoneInfo.ConvertTime(unspecifiedDateTime, sourceTimeZone, localTimeZone);
}
catch
{
// If timezone lookup fails, return as-is
return StartDate;
}
return this.GetLocalStartDate();
}
}
@@ -202,28 +182,7 @@ public class CalendarItem : ICalendarItem
{
get
{
if (string.IsNullOrEmpty(EndTimeZone))
{
// No timezone info, return as-is
return EndDate;
}
try
{
var sourceTimeZone = TimeZoneInfo.FindSystemTimeZoneById(EndTimeZone);
var localTimeZone = TimeZoneInfo.Local;
// Ensure DateTime is Unspecified kind before conversion
var unspecifiedDateTime = DateTime.SpecifyKind(EndDate, DateTimeKind.Unspecified);
// Convert from source timezone to local timezone
return TimeZoneInfo.ConvertTime(unspecifiedDateTime, sourceTimeZone, localTimeZone);
}
catch
{
// If timezone lookup fails, return as-is
return EndDate;
}
return this.GetLocalEndDate();
}
}
@@ -1,4 +1,5 @@
using System;
using System;
using Wino.Core.Domain.Entities.Calendar;
using Wino.Core.Domain.Models.Calendar;
namespace Wino.Core.Domain.Extensions;
@@ -29,4 +30,56 @@ public static class DateTimeExtensions
// Start loading from this date instead of visible date.
return date.AddDays(-diff).Date;
}
/// <summary>
/// Converts a datetime from source timezone into local timezone.
/// If timezone lookup fails, returns original value.
/// </summary>
public static DateTime ToLocalTimeFromTimeZone(this DateTime dateTime, string sourceTimeZoneId)
{
if (string.IsNullOrWhiteSpace(sourceTimeZoneId))
return dateTime;
try
{
var sourceTimeZone = TimeZoneInfo.FindSystemTimeZoneById(sourceTimeZoneId);
var localTimeZone = TimeZoneInfo.Local;
var unspecifiedDateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
return TimeZoneInfo.ConvertTime(unspecifiedDateTime, sourceTimeZone, localTimeZone);
}
catch
{
return dateTime;
}
}
/// <summary>
/// Converts local datetime into target timezone.
/// If timezone lookup fails, returns original value.
/// </summary>
public static DateTime ToTimeZoneFromLocal(this DateTime localDateTime, string targetTimeZoneId)
{
if (string.IsNullOrWhiteSpace(targetTimeZoneId))
return localDateTime;
try
{
var sourceTimeZone = TimeZoneInfo.Local;
var targetTimeZone = TimeZoneInfo.FindSystemTimeZoneById(targetTimeZoneId);
var unspecifiedDateTime = DateTime.SpecifyKind(localDateTime, DateTimeKind.Unspecified);
return TimeZoneInfo.ConvertTime(unspecifiedDateTime, sourceTimeZone, targetTimeZone);
}
catch
{
return localDateTime;
}
}
public static DateTime GetLocalStartDate(this CalendarItem calendarItem)
=> calendarItem.StartDate.ToLocalTimeFromTimeZone(calendarItem.StartTimeZone);
public static DateTime GetLocalEndDate(this CalendarItem calendarItem)
=> calendarItem.EndDate.ToLocalTimeFromTimeZone(calendarItem.EndTimeZone);
}
@@ -1,5 +1,6 @@
using System;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Itenso.TimePeriod;
using Wino.Core.Domain.Entities.Calendar;
@@ -18,7 +19,7 @@ public interface ICalendarService
Task InsertAccountCalendarAsync(AccountCalendar accountCalendar);
Task UpdateAccountCalendarAsync(AccountCalendar accountCalendar);
Task CreateNewCalendarItemAsync(CalendarItem calendarItem, List<CalendarEventAttendee> attendees);
/// <summary>
/// Retrieves calendar events for a given calendar within the specified time period.
/// </summary>
@@ -26,7 +27,7 @@ public interface ICalendarService
/// <param name="period">The time period to query events for.</param>
/// <returns>List of calendar items that fall within the requested period.</returns>
Task<List<CalendarItem>> GetCalendarEventsAsync(IAccountCalendar calendar, ITimePeriod period);
Task<CalendarItem> GetCalendarItemAsync(Guid accountCalendarId, string remoteEventId);
Task UpdateCalendarDeltaSynchronizationToken(Guid calendarId, string deltaToken);
@@ -42,6 +43,11 @@ public interface ICalendarService
Task<List<Reminder>> GetRemindersAsync(Guid calendarItemId);
Task SaveRemindersAsync(Guid calendarItemId, List<Reminder> reminders);
/// <summary>
/// Checks due reminder windows and returns reminder notifications that should trigger now.
/// </summary>
Task<List<CalendarReminderNotificationRequest>> CheckAndNotifyAsync(DateTime lastCheckLocal, DateTime nowLocal, ISet<string> sentReminderKeys, CancellationToken cancellationToken = default);
/// <summary>
/// Gets predefined reminder options in minutes (1 Hour, 30 Min, 15 Min, 5 Min, 1 Min).
/// </summary>
@@ -17,6 +17,7 @@ public interface IMailItemDisplayInformation : INotifyPropertyChanged
bool IsRead { get; }
bool IsDraft { get; }
bool HasAttachments { get; }
bool IsCalendarEvent { get; }
bool IsFlagged { get; }
DateTime CreationDate { get; }
string Base64ContactPicture { get; }
@@ -1,6 +1,7 @@
using System;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Wino.Core.Domain.Entities.Calendar;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared;
@@ -29,4 +30,9 @@ public interface INotificationBuilder
/// </summary>
/// <param name="account">Account that needs attention.</param>
void CreateAttentionRequiredNotification(MailAccount account);
/// <summary>
/// Creates a calendar reminder toast for the specified calendar item.
/// </summary>
Task CreateCalendarReminderNotificationAsync(CalendarItem calendarItem, long reminderDurationInSeconds);
}
@@ -0,0 +1,10 @@
using Wino.Core.Domain.Entities.Calendar;
namespace Wino.Core.Domain.Models.Calendar;
public sealed class CalendarReminderNotificationRequest
{
public CalendarItem CalendarItem { get; init; } = null!;
public long ReminderDurationInSeconds { get; init; }
public string ReminderKey { get; init; } = string.Empty;
}