Files
Wino-Mail/Wino.Core/Extensions/GoogleIntegratorExtensions.cs
T

204 lines
8.1 KiB
C#
Raw Normal View History

2024-04-18 01:44:37 +02:00
using System;
using System.Collections.Generic;
using System.Linq;
using Google.Apis.Calendar.v3.Data;
2024-04-18 01:44:37 +02:00
using Google.Apis.Gmail.v1.Data;
2024-12-28 23:17:16 +01:00
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Calendar;
2024-11-10 23:28:25 +01:00
using Wino.Core.Domain.Entities.Mail;
2024-04-18 01:44:37 +02:00
using Wino.Core.Domain.Enums;
using Wino.Core.Misc;
using Wino.Services;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
namespace Wino.Core.Extensions;
public static class GoogleIntegratorExtensions
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
private static string GetNormalizedLabelName(string labelName)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
// 1. Remove CATEGORY_ prefix.
var normalizedLabelName = labelName.Replace(ServiceConstants.CATEGORY_PREFIX, string.Empty);
2025-02-16 11:54:23 +01:00
// 2. Normalize label name by capitalizing first letter.
normalizedLabelName = char.ToUpper(normalizedLabelName[0]) + normalizedLabelName.Substring(1).ToLower();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
return normalizedLabelName;
}
2025-02-16 11:54:23 +01:00
public static MailItemFolder GetLocalFolder(this Label label, ListLabelsResponse labelsResponse, Guid accountId)
{
var normalizedLabelName = GetFolderName(label.Name);
2025-02-16 11:54:23 +01:00
// Even though we normalize the label name, check is done by capitalizing the label name.
var capitalNormalizedLabelName = normalizedLabelName.ToUpper();
2025-02-16 11:54:23 +01:00
bool isSpecialFolder = ServiceConstants.KnownFolderDictionary.ContainsKey(capitalNormalizedLabelName);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
var specialFolderType = isSpecialFolder ? ServiceConstants.KnownFolderDictionary[capitalNormalizedLabelName] : SpecialFolderType.Other;
2025-02-16 11:54:23 +01:00
// We used to support FOLDER_HIDE_IDENTIFIER to hide invisible folders.
// However, a lot of people complained that they don't see their folders after the initial sync
// without realizing that they are hidden in Gmail settings. Therefore, it makes more sense to ignore Gmail's configuration
// since Wino allows folder visibility configuration separately.
2025-02-16 11:54:23 +01:00
// Overridden hidden labels are shown in the UI.
// Also Gmail does not support folder sync enable/disable options due to history changes.
// By default all folders will be enabled for synchronization.
2025-02-16 11:54:23 +01:00
bool isHidden = false;
2025-02-16 11:54:23 +01:00
bool isChildOfCategoryFolder = label.Name.StartsWith(ServiceConstants.CATEGORY_PREFIX);
bool isSticky = isSpecialFolder && specialFolderType != SpecialFolderType.Category && !isChildOfCategoryFolder;
2025-02-16 11:54:23 +01:00
// By default, all special folders update unread count in the UI except Trash.
bool shouldShowUnreadCount = specialFolderType != SpecialFolderType.Deleted || specialFolderType != SpecialFolderType.Other;
2025-02-16 11:54:23 +01:00
bool isSystemFolder = label.Type == ServiceConstants.SYSTEM_FOLDER_IDENTIFIER;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
var localFolder = new MailItemFolder()
{
TextColorHex = label.Color?.TextColor,
BackgroundColorHex = label.Color?.BackgroundColor,
FolderName = normalizedLabelName,
RemoteFolderId = label.Id,
Id = Guid.NewGuid(),
MailAccountId = accountId,
IsSynchronizationEnabled = true,
SpecialFolderType = specialFolderType,
IsSystemFolder = isSystemFolder,
IsSticky = isSticky,
IsHidden = isHidden,
ShowUnreadCount = shouldShowUnreadCount,
};
localFolder.ParentRemoteFolderId = isChildOfCategoryFolder ? string.Empty : GetParentFolderRemoteId(label.Name, labelsResponse);
return localFolder;
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public static bool GetIsDraft(this Message message)
=> message?.LabelIds?.Any(a => a == ServiceConstants.DRAFT_LABEL_ID) ?? false;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public static bool GetIsUnread(this Message message)
=> message?.LabelIds?.Any(a => a == ServiceConstants.UNREAD_LABEL_ID) ?? false;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public static bool GetIsFocused(this Message message)
=> message?.LabelIds?.Any(a => a == ServiceConstants.IMPORTANT_LABEL_ID) ?? false;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public static bool GetIsFlagged(this Message message)
=> message?.LabelIds?.Any(a => a == ServiceConstants.STARRED_LABEL_ID) ?? false;
2025-02-16 11:54:23 +01:00
private static string GetParentFolderRemoteId(string fullLabelName, ListLabelsResponse labelsResponse)
{
if (string.IsNullOrEmpty(fullLabelName)) return string.Empty;
2025-02-16 11:54:23 +01:00
// Find the last index of '/'
int lastIndex = fullLabelName.LastIndexOf('/');
2025-02-16 11:54:23 +01:00
// If '/' not found or it's at the start, return the empty string.
if (lastIndex <= 0) return string.Empty;
2025-02-16 11:54:23 +01:00
// Extract the parent label
var parentLabelName = fullLabelName.Substring(0, lastIndex);
2025-02-16 11:54:23 +01:00
return labelsResponse.Labels.FirstOrDefault(a => a.Name == parentLabelName)?.Id ?? string.Empty;
}
2025-02-16 11:54:23 +01:00
public static string GetFolderName(string fullFolderName)
{
if (string.IsNullOrEmpty(fullFolderName)) return string.Empty;
2025-02-16 11:54:23 +01:00
// Folders with "//" at the end has "/" as the name.
if (fullFolderName.EndsWith(ServiceConstants.FOLDER_SEPERATOR_STRING)) return ServiceConstants.FOLDER_SEPERATOR_STRING;
2025-02-16 11:54:23 +01:00
string[] parts = fullFolderName.Split(ServiceConstants.FOLDER_SEPERATOR_CHAR);
2025-02-16 11:54:23 +01:00
var lastPart = parts[parts.Length - 1];
2025-02-16 11:54:23 +01:00
return GetNormalizedLabelName(lastPart);
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public static List<RemoteAccountAlias> GetRemoteAliases(this ListSendAsResponse response)
{
return response?.SendAs?.Select(a => new RemoteAccountAlias()
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
AliasAddress = a.SendAsEmail,
IsRootAlias = a.IsDefault.GetValueOrDefault(),
IsPrimary = a.IsPrimary.GetValueOrDefault(),
ReplyToAddress = a.ReplyToAddress,
AliasSenderName = a.DisplayName,
IsVerified = a.VerificationStatus == "accepted" || a.IsDefault.GetValueOrDefault(),
}).ToList();
}
2025-02-16 11:54:23 +01:00
public static AccountCalendar AsCalendar(this CalendarListEntry calendarListEntry, Guid accountId)
{
var calendar = new AccountCalendar()
{
2025-02-16 11:54:23 +01:00
RemoteCalendarId = calendarListEntry.Id,
AccountId = accountId,
Name = calendarListEntry.Summary,
Id = Guid.NewGuid(),
TimeZone = calendarListEntry.TimeZone,
IsPrimary = calendarListEntry.Primary.GetValueOrDefault(),
IsSynchronizationEnabled = true,
2025-02-16 11:54:23 +01:00
};
2025-02-16 11:54:23 +01:00
// Bg color must present. Generate one if doesnt exists.
// Text color is optional. It'll be overriden by UI for readibility.
2025-02-16 11:54:23 +01:00
calendar.BackgroundColorHex = string.IsNullOrEmpty(calendarListEntry.BackgroundColor) ? ColorHelpers.GenerateFlatColorHex() : calendarListEntry.BackgroundColor;
calendar.TextColorHex = string.IsNullOrEmpty(calendarListEntry.ForegroundColor) ? "#000000" : calendarListEntry.ForegroundColor;
2025-02-16 11:54:23 +01:00
return calendar;
}
2025-02-16 11:54:23 +01:00
public static DateTimeOffset? GetEventDateTimeOffset(EventDateTime calendarEvent)
{
if (calendarEvent != null)
{
2025-02-16 11:54:23 +01:00
if (calendarEvent.DateTimeDateTimeOffset != null)
{
return calendarEvent.DateTimeDateTimeOffset.Value;
}
else if (calendarEvent.Date != null)
{
2025-02-16 11:54:23 +01:00
if (DateTime.TryParse(calendarEvent.Date, out DateTime eventDateTime))
{
2025-02-16 11:54:23 +01:00
// Date-only events are treated as UTC midnight
return new DateTimeOffset(eventDateTime, TimeSpan.Zero);
}
2025-02-16 11:54:23 +01:00
else
{
2025-02-16 11:54:23 +01:00
throw new Exception("Invalid date format in Google Calendar event date.");
}
}
}
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
return null;
}
2025-02-16 11:43:30 +01:00
2025-12-26 20:46:48 +01:00
/// <summary>
/// Extracts the timezone string from EventDateTime.
/// Returns null for all-day events or if timezone is not specified.
/// </summary>
public static string GetEventTimeZone(EventDateTime eventDateTime)
{
return eventDateTime?.TimeZone;
}
2025-02-16 11:54:23 +01:00
/// <summary>
/// RRULE, EXRULE, RDATE and EXDATE lines for a recurring event, as specified in RFC5545.
/// </summary>
/// <returns>___ separated lines.</returns>
public static string GetRecurrenceString(this Event calendarEvent)
{
if (calendarEvent == null || calendarEvent.Recurrence == null || !calendarEvent.Recurrence.Any())
{
return null;
2025-02-16 11:43:30 +01:00
}
2025-02-16 11:54:23 +01:00
return string.Join(Constants.CalendarEventRecurrenceRuleSeperator, calendarEvent.Recurrence);
2024-04-18 01:44:37 +02:00
}
}