Merged feature/vNext. Initial commit for Wino Mail 2.0

This commit is contained in:
Burak Kaan Köse
2026-04-05 16:30:26 +02:00
1513 changed files with 93788 additions and 26896 deletions
@@ -1,4 +1,6 @@
using System;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Interfaces;
@@ -8,8 +10,11 @@ public interface IAccountCalendar
string TextColorHex { get; set; }
string BackgroundColorHex { get; set; }
bool IsPrimary { get; set; }
bool IsSynchronizationEnabled { get; set; }
Guid AccountId { get; set; }
string RemoteCalendarId { get; set; }
bool IsExtended { get; set; }
CalendarItemShowAs DefaultShowAs { get; set; }
Guid Id { get; set; }
MailAccount MailAccount { get; set; }
}
@@ -7,7 +7,27 @@ namespace Wino.Core.Domain.Interfaces;
public interface IAccountMenuItem : IMenuItem
{
bool IsEnabled { get; set; }
double SynchronizationProgress { get; set; }
/// <summary>
/// Calculated synchronization progress percentage (0-100). -1 for indeterminate.
/// </summary>
double SynchronizationProgress { get; }
/// <summary>
/// Total items to sync. 0 for indeterminate progress.
/// </summary>
int TotalItemsToSync { get; set; }
/// <summary>
/// Remaining items to sync.
/// </summary>
int RemainingItemsToSync { get; set; }
/// <summary>
/// Current synchronization status message.
/// </summary>
string SynchronizationStatus { get; set; }
int UnreadItemCount { get; set; }
IEnumerable<MailAccount> HoldingAccounts { get; }
void UpdateAccount(MailAccount account);
@@ -171,4 +171,19 @@ public interface IAccountService
/// <returns>Whether the notifications should be created after sync or not.</returns>
Task<bool> IsNotificationsEnabled(Guid accountId);
Task UpdateAccountCustomServerInformationAsync(CustomServerInformation customServerInformation);
/// <summary>
/// Updates the last folder structure sync date for the given account.
/// Used for optimization to skip folder sync if it was done recently.
/// </summary>
/// <param name="accountId">Account id.</param>
Task UpdateLastFolderStructureSyncDateAsync(Guid accountId);
/// <summary>
/// Checks if folder structure should be synced based on the configured interval.
/// Returns true if LastFolderStructureSyncDate is null or older than the interval.
/// </summary>
/// <param name="accountId">Account id.</param>
/// <param name="syncInterval">Minimum interval between folder syncs.</param>
Task<bool> ShouldSyncFolderStructureAsync(Guid accountId, TimeSpan syncInterval);
}
@@ -0,0 +1,10 @@
using System.Collections.Generic;
using Wino.Core.Domain.Models.Ai;
namespace Wino.Core.Domain.Interfaces;
public interface IAiActionOptionsService
{
IReadOnlyList<AiTranslateLanguageOption> GetTranslateLanguageOptions();
IReadOnlyList<AiRewriteModeOption> GetRewriteModeOptions();
}
@@ -1,17 +1,22 @@
using System.Threading.Tasks;
using System;
using System.Threading;
using System.Threading.Tasks;
using Wino.Core.Domain.Models.AutoDiscovery;
namespace Wino.Core.Domain.Interfaces;
/// <summary>
/// Searches for Auto Discovery settings for custom mail accounts.
/// Searches for auto-discovery settings for custom mail accounts.
/// </summary>
public interface IAutoDiscoveryService
{
/// <summary>
/// Tries to return the best mail server settings using different techniques.
/// </summary>
/// <param name="mailAddress">Address to search settings for.</param>
/// <returns>CustomServerInformation with only settings applied.</returns>
Task<AutoDiscoverySettings> GetAutoDiscoverySettings(AutoDiscoveryMinimalSettings autoDiscoveryMinimalSettings);
/// <summary>
/// Tries to resolve a CalDAV endpoint for the mailbox address.
/// </summary>
Task<Uri> DiscoverCalDavServiceUriAsync(string mailAddress, CancellationToken cancellationToken = default);
}
@@ -1,17 +0,0 @@
using System.Threading.Tasks;
namespace Wino.Core.Domain.Interfaces;
public interface IBackgroundTaskService
{
/// <summary>
/// Unregisters all background tasks once.
/// This is used to clean up the background tasks when the app is updated.
/// </summary>
void UnregisterAllBackgroundTask();
/// <summary>
/// Registers required background tasks.
/// </summary>
Task RegisterBackgroundTasksAsync();
}
@@ -1,4 +1,6 @@
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Accounts;
@@ -23,6 +25,23 @@ public interface IBaseSynchronizer
/// <param name="request">Request to queue.</param>
void QueueRequest(IRequestBase request);
/// <summary>
/// Returns whether there is an in-progress (queued or currently executing) operation for the given mail unique id.
/// </summary>
/// <param name="mailUniqueId">Mail unique id to check.</param>
bool HasPendingOperation(Guid mailUniqueId);
/// <summary>
/// Returns mail unique ids that currently have queued or executing operations.
/// </summary>
IReadOnlyCollection<Guid> GetPendingOperationUniqueIds();
/// <summary>
/// Returns whether there is an in-progress (queued or currently executing) operation for the given calendar item id.
/// </summary>
/// <param name="calendarItemId">Calendar item id to check.</param>
bool HasPendingCalendarOperation(Guid calendarItemId);
/// <summary>
/// Synchronizes profile information with the server.
/// Sender name and Profile picture are updated.
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Wino.Core.Domain.Models.Calendar;
namespace Wino.Core.Domain.Interfaces;
public interface ICalDavClient
{
Task<IReadOnlyList<CalDavCalendar>> DiscoverCalendarsAsync(
CalDavConnectionSettings connectionSettings,
CancellationToken cancellationToken = default);
Task<IReadOnlyList<CalDavCalendarEvent>> GetCalendarEventsAsync(
CalDavConnectionSettings connectionSettings,
CalDavCalendar calendar,
DateTimeOffset startUtc,
DateTimeOffset endUtc,
CancellationToken cancellationToken = default);
Task UpsertCalendarEventAsync(
CalDavConnectionSettings connectionSettings,
CalDavCalendar calendar,
string remoteEventId,
string icsContent,
CancellationToken cancellationToken = default);
Task DeleteCalendarEventAsync(
CalDavConnectionSettings connectionSettings,
CalDavCalendar calendar,
string remoteEventId,
CancellationToken cancellationToken = default);
}
@@ -1,5 +0,0 @@
namespace Wino.Core.Domain.Interfaces;
public interface ICalendarDialogService : IDialogServiceBase
{
}
@@ -0,0 +1,15 @@
using System;
using System.Threading.Tasks;
namespace Wino.Core.Domain.Interfaces;
/// <summary>
/// Persists CalDAV ICS payloads on disk for IMAP accounts.
/// </summary>
public interface ICalendarIcsFileService
{
Task SaveCalendarItemIcsAsync(Guid accountId, Guid calendarId, Guid calendarItemId, string remoteEventId, string remoteResourceHref, string eTag, string icsContent);
Task<string> GetCalendarItemIcsETagAsync(Guid accountId, Guid calendarId, Guid calendarItemId);
Task DeleteCalendarItemIcsAsync(Guid accountId, Guid calendarItemId);
Task DeleteCalendarIcsForCalendarAsync(Guid accountId, Guid calendarId);
}
@@ -1,5 +1,6 @@
using System;
using Itenso.TimePeriod;
using Wino.Core.Domain.Models.Calendar;
namespace Wino.Core.Domain.Interfaces;
@@ -19,4 +20,13 @@ public interface ICalendarItem
bool IsRecurringChild { get; }
bool IsRecurringParent { get; }
bool IsRecurringEvent { get; }
/// <summary>
/// Gets the display title for this calendar item when rendered in a specific day.
/// For multi-day events, includes start/end time indicators.
/// </summary>
/// <param name="displayingPeriod">The period of the day where this item is being rendered.</param>
/// <param name="calendarSettings">Calendar settings for time formatting.</param>
/// <returns>The formatted title string.</returns>
string GetDisplayTitle(ITimePeriod displayingPeriod, CalendarSettings calendarSettings);
}
@@ -1,4 +1,8 @@
namespace Wino.Core.Domain.Interfaces;
using Itenso.TimePeriod;
using Wino.Core.Domain.Entities.Calendar;
using Wino.Core.Domain.Models.Calendar;
namespace Wino.Core.Domain.Interfaces;
/// <summary>
/// Temporarily to enforce CalendarItemViewModel. Used in CalendarEventCollection.
@@ -6,4 +10,23 @@
public interface ICalendarItemViewModel
{
bool IsSelected { get; set; }
bool IsBusy { get; set; }
/// <summary>
/// The period of the day where this item is currently being displayed.
/// </summary>
ITimePeriod DisplayingPeriod { get; set; }
/// <summary>
/// Calendar settings for time formatting.
/// </summary>
CalendarSettings CalendarSettings { get; set; }
/// <summary>
/// Updates the view model's underlying CalendarItem from new data.
/// This allows in-place updates without removing and re-adding items.
/// </summary>
/// <param name="calendarItem">The updated calendar item data.</param>
void UpdateFrom(CalendarItem calendarItem);
}
@@ -1,6 +1,8 @@
using System;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Itenso.TimePeriod;
using Wino.Core.Domain.Entities.Calendar;
using Wino.Core.Domain.Models.Calendar;
@@ -11,12 +13,22 @@ public interface ICalendarService
Task<List<AccountCalendar>> GetAccountCalendarsAsync(Guid accountId);
Task<AccountCalendar> GetAccountCalendarAsync(Guid accountCalendarId);
Task DeleteCalendarItemAsync(Guid calendarItemId);
Task DeleteCalendarItemAsync(string calendarRemoteEventId, Guid calendarId);
Task DeleteAccountCalendarAsync(AccountCalendar accountCalendar);
Task InsertAccountCalendarAsync(AccountCalendar accountCalendar);
Task UpdateAccountCalendarAsync(AccountCalendar accountCalendar);
Task SetPrimaryCalendarAsync(Guid accountId, Guid accountCalendarId);
Task CreateNewCalendarItemAsync(CalendarItem calendarItem, List<CalendarEventAttendee> attendees);
Task<List<CalendarItem>> GetCalendarEventsAsync(IAccountCalendar calendar, DayRangeRenderModel dayRangeRenderModel);
/// <summary>
/// Retrieves calendar events for a given calendar within the specified time period.
/// </summary>
/// <param name="calendar">The calendar to retrieve events from.</param>
/// <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);
@@ -28,4 +40,43 @@ public interface ICalendarService
Task<CalendarItem> GetCalendarItemAsync(Guid id);
Task<List<CalendarEventAttendee>> GetAttendeesAsync(Guid calendarEventTrackingId);
Task<List<CalendarEventAttendee>> ManageEventAttendeesAsync(Guid calendarItemId, List<CalendarEventAttendee> allAttendees);
Task UpdateCalendarItemAsync(CalendarItem calendarItem, List<CalendarEventAttendee> attendees);
Task<List<CalendarItem>> SearchCalendarItemsAsync(string searchQuery, int limit, CancellationToken cancellationToken = default);
Task<List<Reminder>> GetRemindersAsync(Guid calendarItemId);
Task SaveRemindersAsync(Guid calendarItemId, List<Reminder> reminders);
Task SnoozeCalendarItemAsync(Guid calendarItemId, DateTime snoozedUntilLocal);
/// <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>
int[] GetPredefinedReminderMinutes();
#region Attachments
/// <summary>
/// Gets all attachments for a calendar event.
/// </summary>
Task<List<CalendarAttachment>> GetAttachmentsAsync(Guid calendarItemId);
/// <summary>
/// Inserts or updates calendar attachments.
/// </summary>
Task InsertOrReplaceAttachmentsAsync(List<CalendarAttachment> attachments);
/// <summary>
/// Marks an attachment as downloaded and updates its local file path.
/// </summary>
Task MarkAttachmentDownloadedAsync(Guid attachmentId, string localFilePath);
/// <summary>
/// Deletes all attachments for a calendar item.
/// </summary>
Task DeleteAttachmentsAsync(Guid calendarItemId);
#endregion
}
@@ -0,0 +1,26 @@
using System;
using System.Threading.Tasks;
namespace Wino.Core.Domain.Interfaces;
/// <summary>
/// Manages contact picture files stored on disk instead of as base64 in SQLite,
/// eliminating DB bloat and enabling native WIC hardware-accelerated image loading.
/// </summary>
public interface IContactPictureFileService
{
/// <summary>
/// Returns the full file path for the given file ID, or null if the file does not exist on disk.
/// </summary>
string GetContactPicturePath(Guid fileId);
/// <summary>
/// Saves raw image bytes to disk and returns the new file ID.
/// </summary>
Task<Guid> SaveContactPictureAsync(byte[] imageData);
/// <summary>
/// Deletes the picture file for the given file ID if it exists.
/// </summary>
Task DeleteContactPictureAsync(Guid fileId);
}
+28 -1
View File
@@ -1,7 +1,10 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MimeKit;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Models.Contacts;
namespace Wino.Core.Domain.Interfaces;
@@ -9,6 +12,30 @@ public interface IContactService
{
Task<List<AccountContact>> GetAddressInformationAsync(string queryText);
Task<AccountContact> GetAddressInformationByAddressAsync(string address);
Task<List<AccountContact>> GetContactsByAddressesAsync(IEnumerable<string> addresses);
Task SaveAddressInformationAsync(MimeMessage message);
Task SaveAddressInformationAsync(IEnumerable<AccountContact> contacts);
Task<AccountContact> CreateNewContactAsync(string address, string displayName);
// Paged contact queries for ContactsPage
Task<List<AccountContact>> GetAllContactsAsync();
Task<List<AccountContact>> SearchContactsAsync(string searchQuery);
Task<PagedContactsResult> GetContactsPageAsync(int offset, int pageSize, string searchQuery = null, bool excludeRootContacts = false);
Task<AccountContact> UpdateContactAsync(AccountContact contact);
Task DeleteContactAsync(string address);
Task DeleteContactsAsync(IEnumerable<string> addresses);
// Group / distribution list support
Task<List<ContactGroup>> GetGroupsAsync();
Task<ContactGroup> CreateGroupAsync(string name, string description = null);
Task DeleteGroupAsync(Guid groupId);
Task<List<AccountContact>> GetGroupMembersAsync(Guid groupId);
Task AddGroupMemberAsync(Guid groupId, string memberAddress);
Task RemoveGroupMemberAsync(Guid groupId, string memberAddress);
/// <summary>
/// Expands a contact group to the individual <see cref="AccountContact"/> entries of its members.
/// Returns an empty list if the group does not exist or has no members.
/// </summary>
Task<List<AccountContact>> ExpandGroupAsync(Guid groupId);
}
@@ -1,6 +1,6 @@
using System.Collections.Generic;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Models.Folders;
using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Menus;
namespace Wino.Core.Domain.Interfaces;
@@ -8,6 +8,6 @@ namespace Wino.Core.Domain.Interfaces;
public interface IContextMenuItemService
{
IEnumerable<FolderOperationMenuItem> GetFolderContextMenuActions(IBaseFolderMenuItem folderInformation);
IEnumerable<MailOperationMenuItem> GetMailItemContextMenuActions(IEnumerable<IMailItem> selectedMailItems);
IEnumerable<MailOperationMenuItem> GetMailItemRenderMenuActions(IMailItem mailItem, bool isDarkEditor);
IEnumerable<MailOperationMenuItem> GetMailItemContextMenuActions(IEnumerable<MailCopy> selectedMailItems);
IEnumerable<MailOperationMenuItem> GetMailItemRenderMenuActions(MailCopy mailItem, bool isDarkEditor);
}
@@ -1,6 +1,6 @@
using System.Collections.Generic;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Models.Folders;
using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Menus;
namespace Wino.Core.Domain.Interfaces;
@@ -18,12 +18,12 @@ public interface IContextMenuProvider
/// </summary>
/// <param name="folderInformation">Current folder that asks for the menu items.</param>
/// <param name="selectedMailItems">Selected menu items in the given folder.</param>
IEnumerable<MailOperationMenuItem> GetMailItemContextMenuActions(IMailItemFolder folderInformation, IEnumerable<IMailItem> selectedMailItems);
IEnumerable<MailOperationMenuItem> GetMailItemContextMenuActions(IMailItemFolder folderInformation, IEnumerable<MailCopy> selectedMailItems);
/// <summary>
/// Calculates and returns available mail operations for mail rendering CommandBar.
/// </summary>
/// <param name="mailItem">Rendered mail item.</param>
/// <param name="activeFolder">Folder that mail item belongs to.</param>
IEnumerable<MailOperationMenuItem> GetMailItemRenderMenuActions(IMailItem mailItem, IMailItemFolder activeFolder, bool isDarkEditor);
IEnumerable<MailOperationMenuItem> GetMailItemRenderMenuActions(MailCopy mailItem, IMailItemFolder activeFolder, bool isDarkEditor);
}
@@ -4,6 +4,7 @@ using System.Threading.Tasks;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Accounts;
using Wino.Core.Domain.Models.Common;
using Wino.Core.Domain.Models.Printing;
namespace Wino.Core.Domain.Interfaces;
@@ -27,5 +28,7 @@ public interface IDialogServiceBase
Task<AccountCreationDialogResult> ShowAccountProviderSelectionDialogAsync(List<IProviderDetail> availableProviders);
IAccountCreationDialog GetAccountCreationDialog(AccountCreationDialogResult accountCreationDialogResult);
Task<List<SharedFile>> PickFilesAsync(params object[] typeFilters);
Task<List<PickedFileMetadata>> PickFilesMetadataAsync(params object[] typeFilters);
Task<string> PickFilePathAsync(string saveFileName);
Task<WebView2PrintSettingsModel> ShowPrintDialogAsync(WebView2PrintSettingsModel initialSettings = null);
}
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Wino.Core.Domain.Entities.Mail;
namespace Wino.Core.Domain.Interfaces;
public interface IEmailTemplateService
{
Task<List<EmailTemplate>> GetEmailTemplatesAsync();
Task<EmailTemplate> GetEmailTemplateAsync(Guid templateId);
Task<EmailTemplate> CreateEmailTemplateAsync(EmailTemplate template);
Task<EmailTemplate> UpdateEmailTemplateAsync(EmailTemplate template);
Task<EmailTemplate> DeleteEmailTemplateAsync(EmailTemplate template);
}
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Folders;
@@ -20,7 +21,7 @@ public interface IBaseFolderMenuItem : IMenuItem
int UnreadItemCount { get; set; }
SpecialFolderType SpecialFolderType { get; }
IEnumerable<IMailItemFolder> HandlingFolders { get; }
IEnumerable<IMenuItem> SubMenuItems { get; }
ObservableCollection<IMenuItem> SubMenuItems { get; }
bool IsMoveTarget { get; }
bool IsSticky { get; }
bool IsSystemFolder { get; }
@@ -79,6 +79,13 @@ public interface IFolderService
/// <param name="folder">Folder to update.</param>
Task UpdateFolderAsync(MailItemFolder folder);
/// <summary>
/// Updates only IMAP HighestModeSeq for the given folder.
/// </summary>
/// <param name="folderId">Folder id to update.</param>
/// <param name="highestModeSeq">Latest known mod-seq value.</param>
Task UpdateFolderHighestModeSeqAsync(Guid folderId, long highestModeSeq);
/// <summary>
/// Returns the active folder menu items for the given account for UI.
/// </summary>
@@ -1,3 +0,0 @@
namespace Wino.Core.Domain.Interfaces;
public interface IGmailThreadingStrategy : IThreadingStrategy { }
@@ -1,24 +0,0 @@
using System.Threading.Tasks;
using Wino.Core.Domain.Entities.Shared;
namespace Wino.Core.Domain.Interfaces;
public interface IImapAccountCreationDialog : IAccountCreationDialog
{
/// <summary>
/// Returns the custom server information from the dialog..
/// </summary>
/// <returns>Null if canceled.</returns>
Task<CustomServerInformation> GetCustomServerInformationAsync();
/// <summary>
/// Displays preparing folders page.
/// </summary>
void ShowPreparingFolders();
/// <summary>
/// Updates account properties for the welcome imap setup dialog and starts the setup.
/// </summary>
/// <param name="account">Account properties.</param>
void StartImapConnectionSetup(MailAccount account);
}
@@ -1,11 +0,0 @@
using MailKit.Net.Imap;
namespace Wino.Core.Domain.Interfaces;
/// <summary>
/// Provides a synchronization strategy for synchronizing IMAP folders based on the server capabilities.
/// </summary>
public interface IImapSynchronizationStrategyProvider
{
IImapSynchronizerStrategy GetSynchronizationStrategy(IImapClient client);
}
@@ -1,40 +0,0 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MailKit;
using MailKit.Net.Imap;
using Wino.Core.Domain.Entities.Mail;
namespace Wino.Core.Domain.Interfaces;
public interface IImapSynchronizerStrategy
{
/// <summary>
/// Synchronizes given folder with the ImapClient client from the client pool.
/// </summary>
/// <param name="client">Client to perform sync with. I love Mira and Jasminka</param>
/// <param name="folder">Folder to synchronize.</param>
/// <param name="synchronizer">Imap synchronizer that downloads messages.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>List of new downloaded message ids that don't exist locally.</returns>
Task<List<string>> HandleSynchronizationAsync(IImapClient client,
MailItemFolder folder,
IImapSynchronizer synchronizer,
CancellationToken cancellationToken = default);
/// <summary>
/// Downloads given set of messages from the folder.
/// Folder is expected to be opened and synchronizer is connected.
/// </summary>
/// <param name="synchronizer">Synchronizer that performs the action.</param>
/// <param name="remoteFolder">Remote folder to download messages from.</param>
/// <param name="localFolder">Local folder to assign mails to.</param>
/// <param name="uniqueIdSet">Set of message uniqueids.</param>
/// <param name="cancellationToken">Cancellation token.</param>
Task DownloadMessagesAsync(IImapSynchronizer synchronizer,
IMailFolder remoteFolder,
MailItemFolder localFolder,
UniqueIdSet uniqueIdSet,
CancellationToken cancellationToken = default);
}
@@ -1,3 +0,0 @@
namespace Wino.Core.Domain.Interfaces;
public interface IImapThreadingStrategy : IThreadingStrategy { }
@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Interfaces;
/// <summary>
/// Service for managing keyboard shortcuts for mail operations.
/// </summary>
public interface IKeyboardShortcutService
{
/// <summary>
/// Gets all available keyboard shortcuts.
/// </summary>
/// <returns>Collection of keyboard shortcuts.</returns>
Task<IEnumerable<KeyboardShortcut>> GetKeyboardShortcutsAsync();
/// <summary>
/// Gets enabled keyboard shortcuts only.
/// </summary>
/// <returns>Collection of enabled keyboard shortcuts.</returns>
Task<IEnumerable<KeyboardShortcut>> GetEnabledKeyboardShortcutsAsync();
/// <summary>
/// Creates or updates a keyboard shortcut.
/// </summary>
/// <param name="shortcut">The keyboard shortcut to save.</param>
/// <returns>The saved keyboard shortcut.</returns>
Task<KeyboardShortcut> SaveKeyboardShortcutAsync(KeyboardShortcut shortcut);
/// <summary>
/// Deletes a keyboard shortcut.
/// </summary>
/// <param name="shortcutId">The ID of the shortcut to delete.</param>
Task DeleteKeyboardShortcutAsync(Guid shortcutId);
/// <summary>
/// Gets the keyboard shortcut for the given key combination in a specific mode.
/// </summary>
/// <param name="mode">The application mode to search within.</param>
/// <param name="key">The pressed key.</param>
/// <param name="modifierKeys">The modifier keys pressed.</param>
/// <returns>The matching shortcut if found, otherwise null.</returns>
Task<KeyboardShortcut> GetShortcutForKeyAsync(WinoApplicationMode mode, string key, ModifierKeys modifierKeys);
/// <summary>
/// Checks if a key combination is already assigned to another shortcut.
/// </summary>
/// <param name="mode">The application mode to check within.</param>
/// <param name="key">The key to check.</param>
/// <param name="modifierKeys">The modifier keys to check.</param>
/// <param name="excludeShortcutId">Optional ID to exclude from the check (for updates).</param>
/// <returns>True if the combination is already used, false otherwise.</returns>
Task<bool> IsKeyCombinationInUseAsync(WinoApplicationMode mode, string key, ModifierKeys modifierKeys, Guid? excludeShortcutId = null);
/// <summary>
/// Creates default keyboard shortcuts for common mail operations.
/// </summary>
Task CreateDefaultShortcutsAsync();
/// <summary>
/// Resets all shortcuts to defaults.
/// </summary>
Task ResetToDefaultShortcutsAsync();
}
@@ -1,4 +1,5 @@
using Wino.Core.Domain.Models.Launch;
using System;
using Wino.Core.Domain.Models.Launch;
namespace Wino.Core.Domain.Interfaces;
@@ -13,4 +14,5 @@ public interface ILaunchProtocolService
/// Used to handle mailto links.
/// </summary>
MailToUri MailToUri { get; set; }
}
@@ -1,10 +1,15 @@
using System;
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Wino.Core.Domain.Entities.Calendar;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models;
using Wino.Core.Domain.Models.Accounts;
using Wino.Core.Domain.Models.Calendar;
using Wino.Core.Domain.Models.Folders;
namespace Wino.Core.Domain.Interfaces;
@@ -17,6 +22,7 @@ public interface IMailDialogService : IDialogServiceBase
// Custom dialogs
Task<IMailItemFolder> ShowMoveMailFolderDialogAsync(List<IMailItemFolder> availableFolders);
Task<MailAccount> ShowAccountPickerDialogAsync(List<MailAccount> availableAccounts);
Task<AccountCalendarPickingResult> ShowSingleCalendarPickerDialogAsync(List<CalendarPickerAccountGroup> availableCalendarGroups);
/// <summary>
/// Displays a dialog to the user for reordering accounts.
@@ -37,7 +43,7 @@ public interface IMailDialogService : IDialogServiceBase
/// Presents a dialog to the user for signature creation/modification.
/// </summary>
/// <returns>Signature information. Null if canceled.</returns>
Task<AccountSignature> ShowSignatureEditorDialog(AccountSignature signatureModel = null);
Task<AccountSignature> ShowSignatureEditorDialog(AccountSignature? signatureModel = null);
/// <summary>
/// Presents a dialog to the user for account alias creation/modification.
@@ -49,4 +55,26 @@ public interface IMailDialogService : IDialogServiceBase
/// Presents a dialog to the user to show email source.
/// </summary>
Task ShowMessageSourceDialogAsync(string messageSource);
/// <summary>
/// Presents a dialog to the user for keyboard shortcut creation/modification.
/// </summary>
/// <param name="existingShortcut">Existing shortcut to edit, or null for new shortcut.</param>
/// <returns>Dialog result with shortcut information.</returns>
#pragma warning disable CS8625
Task<KeyboardShortcutDialogResult> ShowKeyboardShortcutDialogAsync(KeyboardShortcut existingShortcut = null);
#pragma warning restore CS8625
/// <summary>
/// Presents a dialog to the user for contact creation/modification.
/// </summary>
/// <param name="contact">Existing contact to edit, or null for new contact.</param>
/// <returns>Contact information. Null if canceled.</returns>
Task<AccountContact?> ShowEditContactDialogAsync(AccountContact? contact = null);
Task<WinoAccount?> ShowWinoAccountRegistrationDialogAsync();
Task<WinoAccount?> ShowWinoAccountLoginDialogAsync();
Task<WinoAccountSyncExportResult?> ShowWinoAccountExportDialogAsync();
}
@@ -0,0 +1,9 @@
using System;
using System.Collections.Generic;
namespace Wino.Core.Domain.Interfaces;
public interface IMailHashContainer
{
IEnumerable<Guid> GetContainingIds();
}
@@ -0,0 +1,27 @@
using System;
using System.ComponentModel;
using Wino.Core.Domain.Entities.Shared;
namespace Wino.Core.Domain.Interfaces;
/// <summary>
/// Shared display contract for mail list item rendering.
/// Implemented by both single mail and thread mail view models.
/// </summary>
public interface IMailItemDisplayInformation : INotifyPropertyChanged
{
string Subject { get; }
string FromName { get; }
string FromAddress { get; }
string PreviewText { get; }
bool IsRead { get; }
bool IsDraft { get; }
bool HasAttachments { get; }
bool IsCalendarEvent { get; }
bool IsFlagged { get; }
DateTime CreationDate { get; }
Guid? ContactPictureFileId { get; }
bool ThumbnailUpdatedEvent { get; }
bool IsThreadExpanded { get; }
AccountContact SenderContact { get; }
}
@@ -0,0 +1,9 @@
using System;
namespace Wino.Core.Domain.Interfaces;
public interface IMailListItemSorting
{
DateTime SortingDate { get; }
string SortingName { get; }
}
+20 -1
View File
@@ -25,7 +25,7 @@ public interface IMailService
/// Caution: This method is not safe. Use other overrides.
/// </summary>
Task<List<MailCopy>> GetMailItemsAsync(IEnumerable<string> mailCopyIds);
Task<List<IMailItem>> FetchMailsAsync(MailListInitializationOptions options, CancellationToken cancellationToken = default);
Task<List<MailCopy>> FetchMailsAsync(MailListInitializationOptions options, CancellationToken cancellationToken = default);
/// <summary>
/// Deletes all mail copies for all folders.
@@ -131,6 +131,12 @@ public interface IMailService
/// <returns>Draft MailCopy and Draft MimeMessage as base64.</returns>
Task<(MailCopy draftMailCopy, string draftBase64MimeMessage)> CreateDraftAsync(Guid accountId, DraftCreationOptions draftCreationOptions);
/// <summary>
/// Finds a mail copy in the given account by RFC Message-Id.
/// Returns null when no local match exists.
/// </summary>
Task<MailCopy> GetMailCopyByMessageIdAsync(Guid accountId, string messageId);
/// <summary>
/// Returns ids
/// </summary>
@@ -162,4 +168,17 @@ public interface IMailService
/// <param name="onlineArchiveMailIds">Retrieved MailCopy ids from search result.</param>
/// <returns>Result model that contains added and removed mail copy ids.</returns>
Task<GmailArchiveComparisonResult> GetGmailArchiveComparisonResultAsync(Guid archiveFolderId, List<string> onlineArchiveMailIds);
/// <summary>
/// Gets the most recent mail IDs for a folder.
/// Used for notification purposes after sync completes.
/// </summary>
/// <param name="folderId">Folder ID.</param>
/// <param name="count">Number of recent mails to return.</param>
Task<IEnumerable<string>> GetRecentMailIdsForFolderAsync(Guid folderId, int count);
/// <summary>
/// Returns all mail copies for the account created before the given UTC date.
/// </summary>
Task<List<MailCopy>> GetMailCopiesBeforeDateAsync(Guid accountId, DateTime cutoffDateUtc);
}
@@ -4,4 +4,5 @@ public interface IMenuOperation
{
bool IsEnabled { get; }
string Identifier { get; }
bool IsSecondaryMenuPreferred { get; }
}
@@ -59,6 +59,26 @@ public interface IMimeFileService
/// </summary>
Task<bool> DeleteMimeMessageAsync(Guid accountId, Guid fileId);
/// <summary>
/// Returns cached translated html for the given mime resource if it exists.
/// </summary>
Task<string> GetTranslatedHtmlAsync(Guid accountId, Guid fileId, string targetLanguage, CancellationToken cancellationToken = default);
/// <summary>
/// Saves translated html for the given mime resource.
/// </summary>
Task SaveTranslatedHtmlAsync(Guid accountId, Guid fileId, string targetLanguage, string html, CancellationToken cancellationToken = default);
/// <summary>
/// Returns cached summary text for the given mime resource if it exists.
/// </summary>
Task<string> GetSummaryTextAsync(Guid accountId, Guid fileId, CancellationToken cancellationToken = default);
/// <summary>
/// Saves summary text for the given mime resource.
/// </summary>
Task SaveSummaryTextAsync(Guid accountId, Guid fileId, string summary, CancellationToken cancellationToken = default);
/// <summary>
/// Prepares the final model containing rendering details.
/// </summary>
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Wino.Core.Domain.Interfaces;
public interface IMimeStorageService
{
Task<string> GetMimeRootPathAsync();
Task<Dictionary<Guid, long>> GetAccountsMimeStorageSizesAsync(IEnumerable<Guid> accountIds);
Task DeleteAccountMimeStorageAsync(Guid accountId);
Task<int> DeleteAccountMimeStorageOlderThanAsync(Guid accountId, DateTime cutoffDateUtc);
}
@@ -22,4 +22,9 @@ public interface INativeAppService
/// This is used to display WAM broker dialog on running UWP app called by a windowless server code.
/// </summary>
Func<IntPtr> GetCoreWindowHwnd { get; set; }
/// <summary>
/// Gets the folder path where calendar attachments are stored.
/// </summary>
string GetCalendarAttachmentsFolderPath();
}
@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Personalization;
namespace Wino.Core.Domain.Interfaces;
public interface INewThemeService : IInitializeAsync
{
event EventHandler<ApplicationElementTheme> ElementThemeChanged;
event EventHandler<string> AccentColorChanged;
event EventHandler<WindowBackdropType> BackdropChanged;
Task<List<AppThemeBase>> GetAvailableThemesAsync();
Task<CustomThemeMetadata> CreateNewCustomThemeAsync(string themeName, string accentColor, byte[] wallpaperData);
Task<List<CustomThemeMetadata>> GetCurrentCustomThemesAsync();
List<string> GetAvailableAccountColors();
Task ApplyCustomThemeAsync(bool isInitializing);
// Window Backdrop Management
WindowBackdropType CurrentBackdropType { get; set; }
void ApplyBackdrop(WindowBackdropType backdropType);
// Settings
ApplicationElementTheme RootTheme { get; set; }
Guid? CurrentApplicationThemeId { get; set; }
string AccentColor { get; set; }
string GetSystemAccentColorHex();
bool IsCustomTheme { get; }
// Improved accent color management
Task SetAccentColorAsync(string hexColor, bool preserveTheme = true);
// Title bar color management
void UpdateSystemCaptionButtonColors();
// Backdrop management
List<BackdropTypeWrapper> GetAvailableBackdropTypes();
/// <summary>
/// Re-applies the current theme (backdrop, root theme, accent, caption colors)
/// to the currently active window. Use after a window transition.
/// </summary>
Task ApplyThemeToActiveWindowAsync();
}
@@ -1,7 +1,9 @@
using System;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Entities.Calendar;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared;
namespace Wino.Core.Domain.Interfaces;
@@ -10,7 +12,7 @@ public interface INotificationBuilder
/// <summary>
/// Creates toast notifications for new mails.
/// </summary>
Task CreateNotificationsAsync(Guid inboxFolderId, IEnumerable<IMailItem> newMailItems);
Task CreateNotificationsAsync(IEnumerable<MailCopy> newMailItems);
/// <summary>
/// Gets the unread Inbox messages for each account and updates the taskbar icon.
@@ -18,13 +20,30 @@ public interface INotificationBuilder
/// <returns></returns>
Task UpdateTaskbarIconBadgeAsync();
/// <summary>
/// Creates test notification for test purposes.
/// </summary>
Task CreateTestNotificationAsync(string title, string message);
/// <summary>
/// Removes the toast notification for a specific mail by unique id.
/// </summary>
void RemoveNotification(Guid mailUniqueId);
/// <summary>
/// Shows a notification that the account requires attention.
/// </summary>
/// <param name="account">Account that needs attention.</param>
void CreateAttentionRequiredNotification(MailAccount account);
/// <summary>
/// Shows a notification when WebView2 runtime is unavailable.
/// </summary>
void CreateWebView2RuntimeMissingNotification();
/// <summary>
/// Shows a notification when a Microsoft Store update is available.
/// </summary>
void CreateStoreUpdateNotification();
/// <summary>
/// Creates a calendar reminder toast for the specified calendar item.
/// </summary>
Task CreateCalendarReminderNotificationAsync(CalendarItem calendarItem, long reminderDurationInSeconds);
}
@@ -1,3 +0,0 @@
namespace Wino.Core.Domain.Interfaces;
public interface IOutlookThreadingStrategy : IThreadingStrategy { }
@@ -1,4 +1,4 @@
using System;
using System;
using System.ComponentModel;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Calendar;
@@ -6,7 +6,7 @@ using Wino.Core.Domain.Models.Reader;
namespace Wino.Core.Domain.Interfaces;
public interface IPreferencesService: INotifyPropertyChanged
public interface IPreferencesService : INotifyPropertyChanged
{
/// <summary>
/// When any of the preferences are changed.
@@ -30,11 +30,6 @@ public interface IPreferencesService: INotifyPropertyChanged
/// </summary>
bool IsNavigationPaneOpened { get; set; }
/// <summary>
/// Setting: Gets or sets what should happen to server app when the client is terminated.
/// </summary>
ServerBackgroundMode ServerTerminationBehavior { get; set; }
/// <summary>
/// Setting: Preferred time format for mail or calendar header display.
/// </summary>
@@ -57,6 +52,47 @@ public interface IPreferencesService: INotifyPropertyChanged
/// </summary>
int EmailSyncIntervalMinutes { get; set; }
/// <summary>
/// Setting: Default application mode to open when activation does not specify one.
/// </summary>
WinoApplicationMode DefaultApplicationMode { get; set; }
/// <summary>
/// Setting: Whether Microsoft Store update notifications should be shown.
/// </summary>
bool IsStoreUpdateNotificationsEnabled { get; set; }
/// <summary>
/// Setting: Whether the Wino account profile button in the shell title bar should be hidden.
/// </summary>
bool IsWinoAccountButtonHidden { get; set; }
/// <summary>
/// Setting: Default target language code used for AI translation actions.
/// </summary>
string AiDefaultTranslationLanguageCode { get; set; }
/// <summary>
/// Setting: Preferred target language code for AI summarize actions.
/// </summary>
string AiSummarizeLanguageCode { get; set; }
/// <summary>
/// Setting: Preferred folder path used when saving AI summaries.
/// </summary>
string AiSummarySavePath { get; set; }
/// <summary>
/// Serializes the current syncable preferences snapshot.
/// </summary>
string ExportPreferences();
/// <summary>
/// Deserializes and applies a preferences snapshot.
/// Returns the applied and failed property counts.
/// </summary>
(int appliedCount, int failedCount) ImportPreferences(string settingsJson);
#endregion
#region Mail
@@ -96,11 +132,6 @@ public interface IPreferencesService: INotifyPropertyChanged
/// </summary>
bool IsShowPreviewEnabled { get; set; }
/// <summary>
/// Setting: Enable/disable semantic zoom on clicking date headers.
/// </summary>
bool IsSemanticZoomEnabled { get; set; }
/// <summary>
/// Setting: Set whether 'img' tags in rendered HTMLs should be removed.
/// </summary>
@@ -151,11 +182,6 @@ public interface IPreferencesService: INotifyPropertyChanged
/// </summary>
MailOperation RightHoverAction { get; set; }
/// <summary>
/// Setting: Whether Mailkit Protocol Logger is enabled for ImapTestService or not.
/// </summary>
bool IsMailkitProtocolLoggerEnabled { get; set; }
/// <summary>
/// Setting: Which entity id (merged account or folder) should be expanded automatically on startup.
/// </summary>
@@ -215,12 +241,34 @@ public interface IPreferencesService: INotifyPropertyChanged
#region Calendar
DayOfWeek FirstDayOfWeek { get; set; }
bool IsWorkingHoursEnabled { get; set; }
TimeSpan WorkingHourStart { get; set; }
TimeSpan WorkingHourEnd { get; set; }
DayOfWeek WorkingDayStart { get; set; }
DayOfWeek WorkingDayEnd { get; set; }
double HourHeight { get; set; }
string CalendarTimedDayHeaderDateFormat { get; set; }
/// <summary>
/// Setting: Default reminder duration in seconds for new calendar events.
/// Set to 0 to disable default reminders.
/// </summary>
long DefaultReminderDurationInSeconds { get; set; }
/// <summary>
/// Setting: Default snooze duration in minutes for calendar reminder notifications.
/// </summary>
int DefaultSnoozeDurationInMinutes { get; set; }
/// <summary>
/// Setting: How the New Event button chooses a calendar.
/// </summary>
NewEventButtonBehavior NewEventButtonBehavior { get; set; }
/// <summary>
/// Setting: Default calendar used when New Event is configured to always use a specific calendar.
/// </summary>
Guid? DefaultNewEventCalendarId { get; set; }
CalendarSettings GetCurrentCalendarSettings();
+10 -1
View File
@@ -1,4 +1,6 @@
using Wino.Core.Domain.Entities.Mail;
using System;
using Wino.Core.Domain.Entities.Calendar;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Interfaces;
@@ -63,3 +65,10 @@ public interface IFolderActionRequest : IRequestBase
FolderSynchronizerOperation Operation { get; }
}
public interface ICalendarActionRequest : IRequestBase
{
CalendarItem Item { get; }
Guid? LocalCalendarItemId { get; }
CalendarSynchronizerOperation Operation { get; }
}
@@ -0,0 +1,60 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Wino.Core.Domain.Models.Retry;
using Wino.Core.Domain.Models.Synchronization;
namespace Wino.Core.Domain.Interfaces;
/// <summary>
/// Executes operations with automatic retry and error handling support.
/// </summary>
public interface IRetryExecutor
{
/// <summary>
/// Executes an operation with automatic retry based on the specified policy.
/// </summary>
/// <typeparam name="T">The return type of the operation.</typeparam>
/// <param name="operation">The async operation to execute.</param>
/// <param name="policy">The retry policy to apply.</param>
/// <param name="errorContextFactory">Factory to create error context from exceptions.</param>
/// <param name="errorHandler">Optional error handler for custom error processing.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The result of the operation.</returns>
/// <exception cref="Exception">Thrown when all retries are exhausted or a fatal error occurs.</exception>
Task<T> ExecuteWithRetryAsync<T>(
Func<CancellationToken, Task<T>> operation,
RetryPolicy policy,
Func<Exception, SynchronizerErrorContext> errorContextFactory,
ISynchronizerErrorHandlerFactory errorHandler = null,
CancellationToken cancellationToken = default);
/// <summary>
/// Executes an operation with automatic retry based on the specified policy (void return).
/// </summary>
/// <param name="operation">The async operation to execute.</param>
/// <param name="policy">The retry policy to apply.</param>
/// <param name="errorContextFactory">Factory to create error context from exceptions.</param>
/// <param name="errorHandler">Optional error handler for custom error processing.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <exception cref="Exception">Thrown when all retries are exhausted or a fatal error occurs.</exception>
Task ExecuteWithRetryAsync(
Func<CancellationToken, Task> operation,
RetryPolicy policy,
Func<Exception, SynchronizerErrorContext> errorContextFactory,
ISynchronizerErrorHandlerFactory errorHandler = null,
CancellationToken cancellationToken = default);
/// <summary>
/// Executes an operation with default retry policy.
/// </summary>
/// <typeparam name="T">The return type of the operation.</typeparam>
/// <param name="operation">The async operation to execute.</param>
/// <param name="errorContextFactory">Factory to create error context from exceptions.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The result of the operation.</returns>
Task<T> ExecuteWithRetryAsync<T>(
Func<CancellationToken, Task<T>> operation,
Func<Exception, SynchronizerErrorContext> errorContextFactory,
CancellationToken cancellationToken = default);
}
@@ -1,9 +0,0 @@
using System.Collections.Generic;
using Wino.Core.Domain.Models.Settings;
namespace Wino.Core.Domain.Interfaces;
public interface ISettingsBuilderService
{
List<SettingOption> GetSettingItems();
}
@@ -0,0 +1,79 @@
#nullable enable
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.MenuItems;
using Wino.Core.Domain.Models;
using Wino.Core.Domain.Models.Calendar;
using Wino.Core.Domain.Models.Folders;
using Wino.Core.Domain.Models.Navigation;
namespace Wino.Core.Domain.Interfaces;
public interface IShellClient : INotifyPropertyChanged
{
WinoApplicationMode Mode { get; }
IDispatcher Dispatcher { get; set; }
MenuItemCollection? MenuItems { get; }
object? SelectedMenuItem { get; set; }
bool HandlesNavigationSelection { get; }
void Activate(ShellModeActivationContext activationContext);
void Deactivate();
Task HandleNavigationItemInvokedAsync(IMenuItem? menuItem);
Task HandleNavigationSelectionChangedAsync(IMenuItem? menuItem);
Task KeyboardShortcutHook(KeyboardShortcutTriggerDetails args);
}
public interface IMailShellClient : IShellClient
{
IMenuItem CreatePrimaryMenuItem { get; }
IEnumerable<FolderOperationMenuItem> GetFolderContextMenuActions(IBaseFolderMenuItem folder);
Task HandleAccountCreatedAsync(MailAccount createdAccount);
Task NavigateFolderAsync(IBaseFolderMenuItem baseFolderMenuItem, TaskCompletionSource<bool>? folderInitAwaitTask = null);
Task ChangeLoadedAccountAsync(IAccountMenuItem clickedBaseAccountMenuItem, bool navigateInbox = true);
Task PerformFolderOperationAsync(FolderOperation operation, IBaseFolderMenuItem folderMenuItem);
Task PerformMoveOperationAsync(IEnumerable<MailCopy> items, IBaseFolderMenuItem targetFolderMenuItem);
Task CreateNewMailForAsync(MailAccount account);
}
public interface ICalendarShellClient : IShellClient
{
IStatePersistanceService StatePersistenceService { get; }
IEnumerable DateNavigationHeaderItems { get; }
int SelectedDateNavigationHeaderIndex { get; }
VisibleDateRange? CurrentVisibleRange { get; }
string VisibleDateRangeText { get; }
bool CanSynchronizeCalendars { get; }
ICommand SyncCommand { get; }
ICommand TodayClickedCommand { get; }
ICommand DateClickedCommand { get; }
ICommand PreviousDateRangeCommand { get; }
ICommand NextDateRangeCommand { get; }
IEnumerable GroupedAccountCalendars { get; }
}
public interface IShellViewModel
{
WinoApplicationMode CurrentMode { get; }
IShellClient CurrentClient { get; }
MenuItemCollection? CurrentMenuItems { get; }
object? SelectedMenuItem { get; set; }
void SetCurrentMode(WinoApplicationMode mode);
IShellClient GetClient(WinoApplicationMode mode);
}
public interface IShellHost
{
bool HasShellContent { get; }
void ActivateMode(WinoApplicationMode mode, ShellModeActivationContext activationContext);
}
@@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
namespace Wino.Core.Domain.Interfaces;
public interface ISmimeCertificateService
{
public IEnumerable<X509Certificate2> GetCertificates(StoreName storeName = StoreName.My, StoreLocation storeLocation = StoreLocation.CurrentUser, string emailAddress = null);
public void ImportCertificate(string fileExtension, byte[] rawData, string password = null, StoreName storeName = StoreName.My, StoreLocation storeLocation = StoreLocation.CurrentUser);
public void RemoveCertificate(string thumbprint, StoreName storeName = StoreName.My, StoreLocation storeLocation = StoreLocation.CurrentUser);
}
@@ -18,16 +18,26 @@ public interface IStatePersistanceService : INotifyPropertyChanged
/// </summary>
string CoreWindowTitle { get; set; }
/// <summary>
/// App mode title shown in the title bar.
/// </summary>
string AppModeTitle { get; set; }
/// <summary>
/// When only reader page is visible in small sized window.
/// </summary>
bool IsReaderNarrowed { get; set; }
/// <summary>
/// Should display back button on the shell title bar.
/// Current application mode (Mail or Calendar).
/// Not persisted to configuration, only kept in memory.
/// </summary>
bool IsBackButtonVisible { get; }
WinoApplicationMode ApplicationMode { get; set; }
/// <summary>
/// Whether event details page is visible in Calendar mode.
/// </summary>
bool IsEventDetailsVisible { get; set; }
/// <summary>
/// Setting: Opened pane length for the navigation view.
@@ -54,4 +64,5 @@ public interface IStatePersistanceService : INotifyPropertyChanged
/// Setting: Calendar display count for the day view.
/// </summary>
int DayDisplayCount { get; set; }
}
@@ -1,6 +1,6 @@
using System.Threading.Tasks;
#nullable enable
using System.Threading.Tasks;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Store;
namespace Wino.Core.Domain.Interfaces;
@@ -9,10 +9,20 @@ public interface IStoreManagementService
/// <summary>
/// Checks whether user has the type of an add-on purchased.
/// </summary>
Task<bool> HasProductAsync(StoreProductType productType);
Task<bool> HasProductAsync(WinoAddOnProductType productType);
/// <summary>
/// Attempts to purchase the given add-on.
/// </summary>
Task<StorePurchaseResult> PurchaseAsync(StoreProductType productType);
Task<StorePurchaseResult> PurchaseAsync(WinoAddOnProductType productType);
/// <summary>
/// Requests a Microsoft Store collections ID key for the current customer.
/// </summary>
Task<string?> GetCustomerCollectionsIdAsync(string serviceTicket, string publisherUserId);
/// <summary>
/// Requests a Microsoft Store purchase ID key for the current customer.
/// </summary>
Task<string?> GetCustomerPurchaseIdAsync(string serviceTicket, string publisherUserId);
}
@@ -0,0 +1,12 @@
using System.Threading.Tasks;
namespace Wino.Core.Domain.Interfaces;
public interface IStoreUpdateService
{
bool HasAvailableUpdate { get; }
Task<bool> RefreshAvailabilityAsync(bool showNotification = false);
Task<bool> StartUpdateAsync();
}
@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Authentication;
using Wino.Core.Domain.Models.Connectivity;
using Wino.Core.Domain.Models.Synchronization;
namespace Wino.Core.Domain.Interfaces;
/// <summary>
/// Interface for the singleton synchronization manager that handles synchronizer instances and operations.
/// </summary>
public interface ISynchronizationManager
{
/// <summary>
/// Initializes the SynchronizationManager with required dependencies.
/// </summary>
Task InitializeAsync(ISynchronizerFactory synchronizerFactory,
IImapTestService imapTestService,
IAccountService accountService,
INotificationBuilder notificationBuilder,
IAuthenticationProvider authenticationProvider);
/// <summary>
/// Tests IMAP server connectivity for the given server information.
/// </summary>
Task<ImapConnectivityTestResults> TestImapConnectivityAsync(CustomServerInformation serverInformation, bool allowSSLHandshake);
/// <summary>
/// Starts a new mail synchronization for the given account.
/// </summary>
Task<MailSynchronizationResult> SynchronizeMailAsync(MailSynchronizationOptions options,
CancellationToken cancellationToken = default);
/// <summary>
/// Checks if there is an ongoing synchronization for the given account.
/// </summary>
bool IsAccountSynchronizing(Guid accountId);
/// <summary>
/// Queues a mail action request to the corresponding account's synchronizer with optional synchronization triggering.
/// </summary>
Task QueueRequestAsync(IRequestBase request, Guid accountId, bool triggerSynchronization);
/// <summary>
/// Handles folder synchronization for the given account.
/// </summary>
Task<MailSynchronizationResult> SynchronizeFoldersAsync(Guid accountId,
CancellationToken cancellationToken = default);
/// <summary>
/// Handles alias synchronization for the given account.
/// </summary>
Task<MailSynchronizationResult> SynchronizeAliasesAsync(Guid accountId,
CancellationToken cancellationToken = default);
/// <summary>
/// Handles profile synchronization for the given account.
/// </summary>
Task<MailSynchronizationResult> SynchronizeProfileAsync(Guid accountId,
CancellationToken cancellationToken = default);
/// <summary>
/// Handles calendar synchronization for the given account.
/// </summary>
Task<CalendarSynchronizationResult> SynchronizeCalendarAsync(CalendarSynchronizationOptions options,
CancellationToken cancellationToken = default);
/// <summary>
/// Downloads a MIME message for the given mail item.
/// </summary>
Task<string> DownloadMimeMessageAsync(MailCopy mailItem, Guid accountId,
CancellationToken cancellationToken = default);
/// <summary>
/// Creates a new synchronizer for a newly added account.
/// </summary>
IWinoSynchronizerBase CreateSynchronizerForAccount(MailAccount account);
/// <summary>
/// Cancels ongoing synchronizations for the given account.
/// </summary>
Task CancelSynchronizationsAsync(Guid accountId);
/// <summary>
/// Destroys the synchronizer for the given account.
/// </summary>
Task DestroySynchronizerAsync(Guid accountId);
/// <summary>
/// Gets all cached synchronizers.
/// </summary>
IEnumerable<IWinoSynchronizerBase> GetAllSynchronizers();
/// <summary>
/// Gets a synchronizer for the given account ID.
/// </summary>
Task<IWinoSynchronizerBase> GetSynchronizerAsync(Guid accountId);
/// <summary>
/// Handles OAuth authentication for the specified provider.
/// </summary>
Task<TokenInformationEx> HandleAuthorizationAsync(MailProviderType providerType,
MailAccount account = null,
bool proposeCopyAuthorizationURL = false);
}
@@ -0,0 +1,9 @@
using System.Threading.Tasks;
using Wino.Core.Domain.Models.Synchronization;
namespace Wino.Core.Domain.Interfaces;
public interface ISynchronizerErrorHandlerFactory
{
Task<bool> HandleErrorAsync(SynchronizerErrorContext error);
}
@@ -1,26 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Personalization;
namespace Wino.Core.Domain.Interfaces;
public interface IThemeService : IInitializeAsync
{
event EventHandler<ApplicationElementTheme> ElementThemeChanged;
event EventHandler<string> AccentColorChanged;
Task<List<AppThemeBase>> GetAvailableThemesAsync();
Task<CustomThemeMetadata> CreateNewCustomThemeAsync(string themeName, string accentColor, byte[] wallpaperData);
Task<List<CustomThemeMetadata>> GetCurrentCustomThemesAsync();
List<string> GetAvailableAccountColors();
Task ApplyCustomThemeAsync(bool isInitializing);
// Settings
ApplicationElementTheme RootTheme { get; set; }
Guid CurrentApplicationThemeId { get; set; }
string AccentColor { get; set; }
string GetSystemAccentColorHex();
bool IsCustomTheme { get; }
}
@@ -1,18 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Models.Folders;
using Wino.Core.Domain.Models.MailItem;
namespace Wino.Core.Domain.Interfaces;
public interface IThreadingStrategy
{
/// <summary>
/// Attach thread mails to the list.
/// </summary>
/// <param name="items">Original mails.</param>
/// <returns>Original mails with thread mails.</returns>
Task<List<IMailItem>> ThreadItemsAsync(List<MailCopy> items, IMailItemFolder threadingForFolder);
bool ShouldThreadWithItem(IMailItem originalItem, IMailItem targetItem);
}
@@ -1,12 +0,0 @@
using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Interfaces;
public interface IThreadingStrategyProvider
{
/// <summary>
/// Returns corresponding threading strategy that applies to given provider type.
/// </summary>
/// <param name="mailProviderType">Provider type.</param>
IThreadingStrategy GetStrategy(MailProviderType mailProviderType);
}
@@ -0,0 +1,21 @@
using System.Threading.Tasks;
using Wino.Core.Domain.Models.Updates;
using System.Collections.Generic;
namespace Wino.Core.Domain.Interfaces;
public interface IUpdateManager
{
/// <summary>Loads and parses the update notes for the current version from the bundled asset file.</summary>
Task<UpdateNotes> GetLatestUpdateNotesAsync();
/// <summary>Loads and parses the app feature highlights from the bundled asset file.</summary>
Task<List<UpdateNoteSection>> GetFeaturesAsync();
/// <summary>Returns true if the current version's update notes have not yet been shown to the user.</summary>
bool ShouldShowUpdateNotes();
/// <summary>Stores a flag in local settings indicating the update notes for the current version have been seen.</summary>
void MarkUpdateNotesAsSeen();
}
@@ -0,0 +1,12 @@
using System.Threading.Tasks;
namespace Wino.Core.Domain.Interfaces;
public interface IWebView2RuntimeValidatorService
{
/// <summary>
/// Validates whether WebView2 runtime is installed and available for use.
/// </summary>
Task<bool> IsRuntimeAvailableAsync();
}
@@ -0,0 +1,33 @@
#nullable enable
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Wino.Core.Domain.Models.Accounts;
using Wino.Mail.Api.Contracts.Ai;
using Wino.Mail.Api.Contracts.Auth;
using Wino.Mail.Api.Contracts.Common;
using Wino.Mail.Api.Contracts.Users;
namespace Wino.Core.Domain.Interfaces;
public interface IWinoAccountApiClient
{
Task<WinoAccountApiResult<AuthResultDto>> RegisterAsync(string email, string password, CancellationToken cancellationToken = default);
Task<WinoAccountApiResult<AuthResultDto>> LoginAsync(string email, string password, CancellationToken cancellationToken = default);
Task<WinoAccountApiResult<AuthResultDto>> RefreshAsync(string refreshToken, CancellationToken cancellationToken = default);
Task<ApiEnvelope<EmailConfirmationResendResultDto>> ResendEmailConfirmationAsync(string endpoint, string ticket, CancellationToken cancellationToken = default);
Task<ApiEnvelope<JsonElement>> ForgotPasswordAsync(string email, CancellationToken cancellationToken = default);
Task<ApiEnvelope<JsonElement>> LogoutAsync(string refreshToken, CancellationToken cancellationToken = default);
Task<ApiEnvelope<AuthUserDto>> GetCurrentUserAsync(CancellationToken cancellationToken = default);
Task<ApiEnvelope<AiStatusResultDto>> GetAiStatusAsync(CancellationToken cancellationToken = default);
Task<ApiEnvelope<AiTextResultDto>> SummarizeAsync(string html, string targetLanguage, CancellationToken cancellationToken = default);
Task<ApiEnvelope<AiTextResultDto>> TranslateAsync(string html, string targetLanguage, CancellationToken cancellationToken = default);
Task<ApiEnvelope<AiTextResultDto>> RewriteAsync(string html, string mode, CancellationToken cancellationToken = default);
Task<ApiEnvelope<WinoStoreCollectionsIdTicketInfo>> CreateCollectionsIdTicketAsync(CancellationToken cancellationToken = default);
Task<ApiEnvelope<WinoStoreCollectionsIdTicketInfo>> CreatePurchaseIdTicketAsync(CancellationToken cancellationToken = default);
Task<ApiEnvelope<JsonElement>> SyncStoreEntitlementsAsync(string? storeIdKey, string? purchaseIdKey, CancellationToken cancellationToken = default);
Task<string?> GetSettingsAsync(CancellationToken cancellationToken = default);
Task SaveSettingsAsync(string settingsJson, CancellationToken cancellationToken = default);
Task<UserMailboxSyncListDto> GetMailboxesAsync(CancellationToken cancellationToken = default);
Task ReplaceMailboxesAsync(ReplaceUserMailboxesRequestDto request, CancellationToken cancellationToken = default);
}
@@ -0,0 +1,11 @@
using System.Threading;
using System.Threading.Tasks;
using Wino.Core.Domain.Models.Accounts;
namespace Wino.Core.Domain.Interfaces;
public interface IWinoAccountDataSyncService
{
Task<WinoAccountSyncExportResult> ExportAsync(WinoAccountSyncSelection selection, CancellationToken cancellationToken = default);
Task<WinoAccountSyncImportResult> ImportAsync(WinoAccountSyncSelection selection, CancellationToken cancellationToken = default);
}
@@ -0,0 +1,38 @@
#nullable enable
using System;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Models.Accounts;
using Wino.Mail.Api.Contracts.Ai;
using Wino.Mail.Api.Contracts.Auth;
using Wino.Mail.Api.Contracts.Common;
using Wino.Mail.Api.Contracts.Users;
namespace Wino.Core.Domain.Interfaces;
public interface IWinoAccountProfileService
{
Task<WinoAccountOperationResult> RegisterAsync(string email, string password, CancellationToken cancellationToken = default);
Task<WinoAccountOperationResult> LoginAsync(string email, string password, CancellationToken cancellationToken = default);
Task<WinoAccountOperationResult> RefreshAsync(CancellationToken cancellationToken = default);
Task<WinoAccountOperationResult> RefreshProfileAsync(CancellationToken cancellationToken = default);
Task<ApiEnvelope<EmailConfirmationResendResultDto>> ResendEmailConfirmationAsync(string endpoint, string ticket, CancellationToken cancellationToken = default);
Task<ApiEnvelope<JsonElement>> ForgotPasswordAsync(string email, CancellationToken cancellationToken = default);
Task<WinoAccount?> GetActiveAccountAsync();
Task<WinoAccount?> GetAuthenticatedAccountAsync(CancellationToken cancellationToken = default);
Task<bool> HasActiveAccountAsync();
Task<ApiEnvelope<AuthUserDto>> GetCurrentUserAsync(CancellationToken cancellationToken = default);
Task<ApiEnvelope<AiStatusResultDto>> GetAiStatusAsync(CancellationToken cancellationToken = default);
Task<ApiEnvelope<AiTextResultDto>> SummarizeAsync(string html, string targetLanguage, CancellationToken cancellationToken = default);
Task<ApiEnvelope<AiTextResultDto>> TranslateAsync(string html, string targetLanguage, CancellationToken cancellationToken = default);
Task<ApiEnvelope<AiTextResultDto>> RewriteAsync(string html, string mode, CancellationToken cancellationToken = default);
Task<ApiEnvelope<JsonElement>> SyncStoreEntitlementsAsync(CancellationToken cancellationToken = default);
Task<string?> GetSettingsAsync(CancellationToken cancellationToken = default);
Task SaveSettingsAsync(string settingsJson, CancellationToken cancellationToken = default);
Task<UserMailboxSyncListDto> GetMailboxesAsync(CancellationToken cancellationToken = default);
Task ReplaceMailboxesAsync(ReplaceUserMailboxesRequestDto request, CancellationToken cancellationToken = default);
Task<bool> ProcessBillingCallbackAsync(Uri callbackUri, CancellationToken cancellationToken = default);
Task SignOutAsync(CancellationToken cancellationToken = default);
}
@@ -8,9 +8,12 @@ public interface INavigationService
{
bool Navigate(WinoPage page,
object parameter = null,
NavigationReferenceFrame frame = NavigationReferenceFrame.ShellFrame,
NavigationReferenceFrame frame = NavigationReferenceFrame.InnerShellFrame,
NavigationTransitionType transition = NavigationTransitionType.None);
Type GetPageType(WinoPage winoPage);
void GoBack();
bool ChangeApplicationMode(WinoApplicationMode mode);
bool ChangeApplicationMode(WinoApplicationMode mode, ShellModeActivationContext activationContext);
bool CanGoBack();
void GoBack(NavigationTransitionEffect slideEffect = NavigationTransitionEffect.FromRight);
}
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using Wino.Core.Domain.Models.Calendar;
using Wino.Core.Domain.Models.Folders;
using Wino.Core.Domain.Models.MailItem;
@@ -29,4 +30,10 @@ public interface IWinoRequestDelegator
/// </summary>
/// <param name="folderOperationPreperationRequest">Folder prep request.</param>
Task ExecuteAsync(FolderOperationPreperationRequest folderOperationPreperationRequest);
/// <summary>
/// Prepares and queues calendar action requests for proper synchronizers.
/// </summary>
/// <param name="calendarOperationPreparationRequest">Calendar preparation request.</param>
Task ExecuteAsync(CalendarOperationPreparationRequest calendarOperationPreparationRequest);
}
@@ -1,61 +0,0 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Server;
namespace Wino.Core.Domain.Interfaces;
public interface IWinoServerConnectionManager
{
/// <summary>
/// When the connection status changes, this event will be triggered.
/// </summary>
event EventHandler<WinoServerConnectionStatus> StatusChanged;
/// <summary>
/// Gets the connection status.
/// </summary>
WinoServerConnectionStatus Status { get; }
/// <summary>
/// Launches Full Trust process (Wino Server) and awaits connection completion.
/// If connection is not established in 10 seconds, it will return false.
/// If the server process is already running, it'll connect to existing one.
/// If the server process is not running, it'll be launched and connection establishment is awaited.
/// </summary>
/// <returns>Whether connection is established or not.</returns>
Task<bool> ConnectAsync();
/// <summary>
/// Queues a new user request to be processed by Wino Server.
/// Healthy connection must present before calling this method.
/// </summary>
/// <param name="request">Request to queue for synchronizer in the server.</param>
/// <param name="accountId">Account id to queueu request for.</param>
Task QueueRequestAsync(IRequestBase request, Guid accountId);
/// <summary>
/// Returns response from server for the given request.
/// </summary>
/// <typeparam name="TResponse">Response type.</typeparam>
/// <typeparam name="TRequestType">Request type.</typeparam>
/// <param name="clientMessage">Request type.</param>
/// <returns>Response received from the server for the given TResponse type.</returns>
Task<WinoServerResponse<TResponse>> GetResponseAsync<TResponse, TRequestType>(TRequestType clientMessage, CancellationToken cancellationToken = default) where TRequestType : IClientMessage;
/// <summary>
/// Handle for connecting to the server.
/// If the server is already running, it'll connect to existing one.
/// Callers can await this handle to wait for connection establishment.
/// </summary>
TaskCompletionSource<bool> ConnectingHandle { get; }
}
public interface IWinoServerConnectionManager<TAppServiceConnection> : IWinoServerConnectionManager, IInitializeAsync
{
/// <summary>
/// Existing connection handle to the server of TAppServiceConnection type.
/// </summary>
TAppServiceConnection Connection { get; set; }
}
@@ -2,9 +2,9 @@
using System.Threading;
using System.Threading.Tasks;
using MailKit;
using Wino.Core.Domain.Entities.Calendar;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Models.Folders;
using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Synchronization;
namespace Wino.Core.Domain.Interfaces;
@@ -30,7 +30,7 @@ public interface IWinoSynchronizerBase : IBaseSynchronizer
/// <param name="mailItem">Mail item to download from server.</param>
/// <param name="transferProgress">Optional progress reporting for download operation.</param>
/// <param name="cancellationToken">Cancellation token.</param>
Task DownloadMissingMimeMessageAsync(IMailItem mailItem, ITransferProgress transferProgress, CancellationToken cancellationToken = default);
Task DownloadMissingMimeMessageAsync(MailCopy mailItem, ITransferProgress transferProgress, CancellationToken cancellationToken = default);
/// <summary>
/// 1. Cancel active synchronization.
@@ -47,4 +47,5 @@ public interface IWinoSynchronizerBase : IBaseSynchronizer
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Search results after downloading missing mail copies from server.</returns>
Task<List<MailCopy>> OnlineSearchAsync(string queryText, List<IMailItemFolder> folders, CancellationToken cancellationToken = default);
Task DownloadCalendarAttachmentAsync(CalendarItem calendarItem, CalendarAttachment attachment, string localFilePath, CancellationToken cancellationToken);
}